diff options
188 files changed, 8070 insertions, 4348 deletions
diff --git a/.gitattributes b/.gitattributes index aaf6a39e..d753b1db 100644 --- a/.gitattributes +++ b/.gitattributes | |||
@@ -21,7 +21,6 @@ Dockerfile text | |||
21 | .gitattributes export-ignore | 21 | .gitattributes export-ignore |
22 | .gitignore export-ignore | 22 | .gitignore export-ignore |
23 | .travis.yml export-ignore | 23 | .travis.yml export-ignore |
24 | composer.json export-ignore | ||
25 | doc/**/*.json export-ignore | 24 | doc/**/*.json export-ignore |
26 | doc/**/*.md export-ignore | 25 | doc/**/*.md export-ignore |
27 | docker/ export-ignore | 26 | docker/ export-ignore |
@@ -1,4 +1,4 @@ | |||
1 | # Ignore data/, tmp/, cache/ and pagecache/ | 1 | # Shaarli runtime resources |
2 | data | 2 | data |
3 | tmp | 3 | tmp |
4 | cache | 4 | cache |
@@ -9,18 +9,22 @@ pagecache | |||
9 | .buildpath | 9 | .buildpath |
10 | .project | 10 | .project |
11 | 11 | ||
12 | # Ignore raintpl generated pages | 12 | # Raintpl generated pages |
13 | *.rtpl.php | 13 | *.rtpl.php |
14 | 14 | ||
15 | # Ignore test dependencies | 15 | # 3rd-party dependencies |
16 | composer.lock | 16 | composer.lock |
17 | /vendor/ | 17 | vendor/ |
18 | 18 | ||
19 | # Ignore development and test resources | 19 | # Release archives |
20 | *.tar | ||
21 | *.zip | ||
22 | |||
23 | # Development and test resources | ||
20 | coverage | 24 | coverage |
21 | doxygen | 25 | doxygen |
22 | sandbox | 26 | sandbox |
23 | phpmd.html | 27 | phpmd.html |
24 | 28 | ||
25 | # Ignore user plugin configuration | 29 | # User plugin configuration |
26 | plugins/*/config.php | 30 | plugins/*/config.php |
diff --git a/.travis.yml b/.travis.yml index 7408b2e2..6ff1b20f 100644 --- a/.travis.yml +++ b/.travis.yml | |||
@@ -1,6 +1,10 @@ | |||
1 | sudo: false | 1 | sudo: false |
2 | language: php | 2 | language: php |
3 | cache: | ||
4 | directories: | ||
5 | - $HOME/.composer/cache | ||
3 | php: | 6 | php: |
7 | - 7.1 | ||
4 | - 7.0 | 8 | - 7.0 |
5 | - 5.6 | 9 | - 5.6 |
6 | - 5.5 | 10 | - 5.5 |
@@ -8,7 +12,7 @@ php: | |||
8 | - 5.3 | 12 | - 5.3 |
9 | install: | 13 | install: |
10 | - composer self-update | 14 | - composer self-update |
11 | - composer install | 15 | - composer install --prefer-dist |
12 | script: | 16 | script: |
13 | - make clean | 17 | - make clean |
14 | - make check_permissions | 18 | - make check_permissions |
diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..1340db56 --- /dev/null +++ b/CHANGELOG.md | |||
@@ -0,0 +1,920 @@ | |||
1 | # Change Log | ||
2 | All notable changes to this project will be documented in this file. | ||
3 | |||
4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) | ||
5 | and this project adheres to [Semantic Versioning](http://semver.org/). | ||
6 | |||
7 | |||
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 | ## [v0.8.4](https://github.com/shaarli/Shaarli/releases/tag/v0.8.4) - 2017-03-04 | ||
17 | ### Security | ||
18 | - Markdown plugin: escape HTML entities by default | ||
19 | |||
20 | |||
21 | ## [v0.8.3](https://github.com/shaarli/Shaarli/releases/tag/v0.8.3) - 2017-01-20 | ||
22 | |||
23 | ### Fixed | ||
24 | |||
25 | - PHP 7.1 compatibility: add ConfigManager parameter to anti-bruteforce function call in login template. | ||
26 | |||
27 | ## [v0.8.2](https://github.com/shaarli/Shaarli/releases/tag/v0.8.2) - 2016-12-15 | ||
28 | |||
29 | ### Fixed | ||
30 | |||
31 | - Editing a link created before the new ID system would change its permalink. | ||
32 | |||
33 | ## [v0.8.1](https://github.com/shaarli/Shaarli/releases/tag/v0.8.1) - 2016-12-12 | ||
34 | |||
35 | > Note: this version will create an automatic backup of your database if anything goes wrong. | ||
36 | |||
37 | ### Added | ||
38 | - Add CHANGELOG.md to track the whole project's history | ||
39 | - Enable Composer cache for Travis builds | ||
40 | - Save the last edition date for shaares and use it in Atom/RSS feeds | ||
41 | - Plugins: | ||
42 | - Add an [Isso](https://posativ.org/isso/) plugin to enable user comments on permalinks | ||
43 | - Allow defining init functions, e.g. for performing checks and error processing | ||
44 | - Add a Piwik plugin for analytics. | ||
45 | - Markdown: add warning notice regarding HTML rendering | ||
46 | - Meta tag to *not* send the referrer to external resources. | ||
47 | |||
48 | ### Changed | ||
49 | - Link ID complete refactoring: | ||
50 | - Links now have a numeric ID instead of dates | ||
51 | - Short URLs are now created once and can't change over time (previous URL are kept) | ||
52 | - Templates: | ||
53 | - Changed placeholder behaviour for: `buttons_toolbar`, `fields_toolbar` and `action_plugin` | ||
54 | - Cleanup `{loop}` declarations in templates | ||
55 | - Tools: hide Firefox Social button when not in HTTPS | ||
56 | - Firefox Social: show Shaarli's title when shaaring using Firefox Social | ||
57 | - Release archives now have the same structure as GitHub-generated archives: | ||
58 | - archives contain a `Shaarli` directory, itself containing sources + dependencies | ||
59 | - the tarball is now gzipped | ||
60 | - Plugins: | ||
61 | - Markdown: Parsedown library is now imported through Composer | ||
62 | - Minor code cleanup: PHPDoc, spelling, unused variables, etc. | ||
63 | - Docker: explicitly set the maximum file upload size to 10 MiB | ||
64 | |||
65 | ### Fixed | ||
66 | - Fix the server `<self>` value in Atom/RSS feeds | ||
67 | - Plugins: | ||
68 | - Tools: only display parameter description when it exists | ||
69 | - archive.org: do not propose archival of private notes | ||
70 | - Markdown: | ||
71 | - render links properly in code blocks | ||
72 | - bug regarding the `nomarkdown` tag | ||
73 | - W3C compliance | ||
74 | - Use absolute URL for hashtags in RSS and ATOM feeds | ||
75 | - Docker: specify the location of the favicon | ||
76 | - ATOM feed: remove new line between content tag and data | ||
77 | |||
78 | ### Security | ||
79 | - Allow whitelisting trusted IPs, else continue banning clients upon login failure | ||
80 | |||
81 | |||
82 | ## [v0.8.0](https://github.com/shaarli/Shaarli/releases/tag/v0.8.0) - 2016-10-12 | ||
83 | Shaarli now uses [Composer](https://getcomposer.org/) to handle its dependencies. | ||
84 | Please use our release archives, or follow the | ||
85 | [installation documentation](https://github.com/shaarli/Shaarli/wiki/Download-and-Installation). | ||
86 | |||
87 | ### Added | ||
88 | - Composer is required to resolve Shaarli's PHP dependencies | ||
89 | - Shaarli now supports `#hashtags` | ||
90 | - Firefox social share now uses selected text as a description | ||
91 | - Plugin parameters can have a description in each plugin's `.meta` file | ||
92 | |||
93 | ### Changed | ||
94 | - Configuration is now stored as a JSON file | ||
95 | - Previous configuration format will be automatically updated (PHP -> JSON) | ||
96 | - Shaarli now defaults to cURL to fetch shaare titles | ||
97 | - URL cleanup: remove `PHPSESSID` parameter | ||
98 | - `nomarkdown` tag is no longer private, and now affects visitors | ||
99 | - Cleanup template indentation | ||
100 | - Rewrite bookmark import using a generic Netscape parser | ||
101 | |||
102 | ### Removed | ||
103 | - Shaarli no longer references Delicious in its description | ||
104 | |||
105 | ### Deprecated | ||
106 | - Shaarli configuration is not held as PHP globals anymore | ||
107 | |||
108 | ### Fixed | ||
109 | - Ignore case for tags in autocompletion and cloud tag | ||
110 | - Avoid generating empty tags | ||
111 | - Fix a Dockerfile syntax error | ||
112 | |||
113 | ### Security | ||
114 | - Fixed a bug preventing to change password | ||
115 | - XSRF token now generated each time a page is rendered | ||
116 | |||
117 | |||
118 | ## [v0.7.0](https://github.com/shaarli/Shaarli/releases/tag/v0.7.0) - 2016-05-14 | ||
119 | ### Added | ||
120 | - Adds an option to encode redirector URL parameter | ||
121 | - Atom/RSS feeds now support Markdown formatting, and plugins in general | ||
122 | - Markdown: use the tag `.nomarkdown` to avoid markdown processing | ||
123 | - Prefill the login field when the authentication has failed | ||
124 | - Show a private links counter | ||
125 | |||
126 | ### Changed | ||
127 | - Allow to use the bookmarklet in Firefox reader view (URL clean up) | ||
128 | - Improve tagcloud font size | ||
129 | - Improve title retrieving | ||
130 | - Markdown: inline code background color | ||
131 | - Refactor Netscape bookmark export | ||
132 | - Refactor Atom/RSS feed generation | ||
133 | |||
134 | ### Removed | ||
135 | - Remove delicious from Shaarli description | ||
136 | |||
137 | ### Fixed | ||
138 | - Fix bad login redirections causing a 404 in a few cases | ||
139 | - Fix tagcloud font-size with French locale | ||
140 | - Don't display empty tags in tag search | ||
141 | - Fix Awesomeplete conflicts with jQuery | ||
142 | - Fix UTC timezone selection | ||
143 | - Fix a bug preventing to import notes in browsers from bookmarks export | ||
144 | - Don't redirect to ?post if ?addlink is reached while logged out | ||
145 | |||
146 | |||
147 | ## [v0.6.5](https://github.com/shaarli/Shaarli/releases/tag/v0.6.5) - 2016-03-02 | ||
148 | ### Fixed | ||
149 | - Fixes a regression generating an unnecessary warning (language in HTTP request) | ||
150 | - Fixes a bug where going through multiple reverse proxy could generate malformed URL | ||
151 | - Markdown: Fixes a bug where empty description blocks were displayed | ||
152 | |||
153 | |||
154 | ## [v0.6.4](https://github.com/shaarli/Shaarli/releases/tag/v0.6.4) - 2016-02-28 | ||
155 | ### Added | ||
156 | - Add an updater class to automate user data upgrades | ||
157 | - Plugin admin page: adds a label for checkboxes and improve name display | ||
158 | - Plugin Wallabag: API version can be specified in plugin admin page | ||
159 | |||
160 | ### Changed | ||
161 | - Better tag cloud sorting, including special chars (`a > E > é > z`) | ||
162 | - Autolocale now sets all locale categories, not just time | ||
163 | - Use PHP's DateTime object instead of custom functions | ||
164 | - Plugin hooks: process includes before header/footer | ||
165 | - Markdown plugin: better styles for `<code>` and `<pre>` tags | ||
166 | - Improve searching: | ||
167 | - search terms are now considered separated and won't only return exact results anymore | ||
168 | - exact search can be done with quotes `"this exact sentence"` | ||
169 | - search supports excluded terms starting a dash `-exclude` | ||
170 | - implement crossed search: terms + tags | ||
171 | - all of them combined across all shaare fields | ||
172 | - New tag behaviour: | ||
173 | - tags starting with a dash will be renamed without it | ||
174 | - tags starting with a dot `.` will be hidden unless the user is logged in | ||
175 | |||
176 | ### Fixed | ||
177 | - Fix Markdown plugin escape issues (code/quote blocks, etc.) | ||
178 | - Link description aren't trimmed anymore to allow markdown format at the beginning of a shaare | ||
179 | - Fixes plugin admin redirection page on error | ||
180 | |||
181 | ### Security | ||
182 | - Fix a bug where non initialized variables were causing a warning | ||
183 | - Fix a bug where saving a link after edit could cause a 404 error | ||
184 | |||
185 | |||
186 | ## [v0.6.3](https://github.com/shaarli/Shaarli/releases/tag/v0.6.3) - 2016-01-31 | ||
187 | ### Added | ||
188 | - Plugins administration page | ||
189 | - Markdown plugin added for shaares description | ||
190 | - Docker: Dockerfile is now in the main git repository and improved | ||
191 | - Add a `.gitattributes` to ease repository management | ||
192 | - Travis: include file permission checks | ||
193 | |||
194 | ### Changed | ||
195 | - Auto retrieve of title know works with websites (HTTPS, follow redirections, etc.) | ||
196 | - 404 page is now handled in a template | ||
197 | - Date in log files updated to work with fail2ban | ||
198 | - Wallabag: support of Wallabag v2 and minor fixes | ||
199 | - Link search refactoring | ||
200 | - Logging function refactoring | ||
201 | |||
202 | ### Fixed | ||
203 | - Fix a bug where renaming a tag was causing a 404 | ||
204 | - Fix a bug allowing to search blank terms | ||
205 | - Fix a bug preventing to remove a tag with special chars when searching | ||
206 | |||
207 | |||
208 | ## [v0.6.2](https://github.com/shaarli/Shaarli/releases/tag/v0.6.2) - 2015-12-23 | ||
209 | ### Changed | ||
210 | - Plugins: new footer hook | ||
211 | - Plugins: improve QR code | ||
212 | - Cleanup templates | ||
213 | |||
214 | ### Fixed | ||
215 | - Plugins: use the actual link URL to generate QR codes | ||
216 | - Templates: missing/erroneous page titles | ||
217 | - Templates: missing variables resulting in PHP errors | ||
218 | |||
219 | ### Security | ||
220 | - Fix invalid file permissions (remove executable bit) | ||
221 | |||
222 | |||
223 | ## [v0.6.1](https://github.com/shaarli/Shaarli/releases/tag/v0.6.1) - 2015-12-01 | ||
224 | ### Added | ||
225 | - Add OpenSearch support | ||
226 | - Add a Doxygen makefile target | ||
227 | - Tools: add fine-grained file/directory permission checks (installation) | ||
228 | |||
229 | ### Changed | ||
230 | - Tools: check the 'stable' branch for new versions (updates) | ||
231 | - Cleanup: introduce an `ApplicationUtils` class | ||
232 | |||
233 | ### Removed | ||
234 | - Cleanup: remove `json_encode()` function (built-in since PHP 5.2) | ||
235 | |||
236 | ### Fixed | ||
237 | - Auto-complete more than one tag | ||
238 | - Bookmarklet: support titles containing quotes | ||
239 | - URL encode links when setting a redirector | ||
240 | |||
241 | |||
242 | ## [v0.6.0](https://github.com/shaarli/Shaarli/releases/tag/v0.6.0) - 2015-11-18 | ||
243 | ### Added | ||
244 | - Introduce a plugin system | ||
245 | - Add a demo_plugin | ||
246 | - Add plugins: | ||
247 | - addlink_toolbar | ||
248 | - archiveorg | ||
249 | - playvideos | ||
250 | - qrcode | ||
251 | - readityourself | ||
252 | - wallabag | ||
253 | |||
254 | ### Changed | ||
255 | - Coding style | ||
256 | |||
257 | ### Fixed | ||
258 | - Adding a new link now returns the correct anchor in the URL | ||
259 | - Set default file permissions | ||
260 | |||
261 | |||
262 | ## [v0.5.4](https://github.com/shaarli/Shaarli/releases/tag/v0.5.4) - 2015-09-15 | ||
263 | ### Added | ||
264 | - HTTPS: support being served behing an SSL-enabled proxy | ||
265 | |||
266 | ### Changed | ||
267 | - HTTP/Server utilities: refactor & add test coverage | ||
268 | - Project & documentation: | ||
269 | - improve/rewrite `README.md` | ||
270 | - update contributor list | ||
271 | - update `index.php` header | ||
272 | |||
273 | ### Fixed | ||
274 | - PHP session IDs: handle hash algorithms and bits per char representations | ||
275 | |||
276 | |||
277 | ## [v0.5.3](https://github.com/shaarli/Shaarli/releases/tag/v0.5.3) - 2015-09-02 | ||
278 | ### Fixed | ||
279 | - Fix a bug that could prevent user to login | ||
280 | |||
281 | |||
282 | ## [0.5.3](https://github.com/shaarli/Shaarli/releases/tag/0.5.3) - 2015-09-02 | ||
283 | This release has been YANKED as it points to a tag that does not follow our naming convention. Please use `v0.5.3` instead | ||
284 | |||
285 | ### Fixed | ||
286 | - Allow uppercase letters in PHP sessionid format | ||
287 | |||
288 | |||
289 | ## [v0.5.2](https://github.com/shaarli/Shaarli/releases/tag/v0.5.2) - 2015-08-31 | ||
290 | ### Added | ||
291 | - Add PHP 7 to Travis platforms | ||
292 | |||
293 | ### Changed | ||
294 | - Also extract HTTPS page metadata (title) | ||
295 | |||
296 | ### Fixed | ||
297 | - Fix regression preventing to load LinkDB info when adding an existing link | ||
298 | |||
299 | ### Security | ||
300 | - Fix Full Path Disclosure upon cookie forgery | ||
301 | |||
302 | |||
303 | ## [v0.5.1](https://github.com/shaarli/Shaarli/releases/tag/v0.5.1) - 2015-08-17 | ||
304 | ### Added | ||
305 | - Add a link to the shaarli/shaarli DockerHub repository | ||
306 | |||
307 | ### Changed | ||
308 | - Update local documentation | ||
309 | - Improve timezone detection at installation | ||
310 | - Improve feed cache handling | ||
311 | - Improve URL cleanup for new links | ||
312 | |||
313 | ### Fixed | ||
314 | - Fix 404 after editing a link while being logged out | ||
315 | |||
316 | |||
317 | ## [v0.5.0](https://github.com/shaarli/Shaarli/releases/tag/v0.5.0) - 2015-07-31 | ||
318 | ### Added | ||
319 | - Add Firefox Social API | ||
320 | - Start code refactoring: | ||
321 | - add unit test coverage | ||
322 | - add Travis integration | ||
323 | |||
324 | ### Changed | ||
325 | - Search/Filter by tag fieds can now be accessed quickly with the `Tab` key | ||
326 | - Update documentation | ||
327 | - Remove duplicate tags in links | ||
328 | - Remove annoying URL patterns | ||
329 | - Start code refactoring: | ||
330 | - move all settings to `data/config.php` | ||
331 | - refactor Config, LinkDB, TimeZone, Utils | ||
332 | |||
333 | ### Fixed | ||
334 | - Fix locale handling | ||
335 | - Fix note URLs | ||
336 | - Fix page redirections | ||
337 | - Fix daily RSS browsing | ||
338 | - Fix title display | ||
339 | - Restore compatibility with PHP 5.3 | ||
340 | |||
341 | ### Security | ||
342 | - Fix links not being hidden when `HIDE_PUBLIC_LINKS` is set | ||
343 | |||
344 | |||
345 | ## [v0.0.45beta](https://github.com/shaarli/Shaarli/releases/tag/v0.0.45beta) - 2015-03-16 | ||
346 | ### Fixed | ||
347 | - Fix improperly displayed Unicode character | ||
348 | - Fix incorrect font size for "Add link" input field | ||
349 | |||
350 | |||
351 | ## [v0.0.44beta](https://github.com/shaarli/Shaarli/releases/tag/v0.0.44beta) - 2015-03-15 | ||
352 | ### Added | ||
353 | - Add a Makefile to run static code checkers | ||
354 | - Add local documentation (help link in page footer) | ||
355 | - Use awesomplete library for autocompletion | ||
356 | - Use bLazy.js library for images lazy loading | ||
357 | - New 'Add Note' bookmarklet to immediatly open a note (text post) compose window | ||
358 | |||
359 | ### Changed | ||
360 | - Theme improvements and cleanup (menu, search fields, icons, linklist...) | ||
361 | - Allow 'javascript:' links sharing (bookmarklets) | ||
362 | - Make update check optional | ||
363 | - Redirect to homepage after adding a link via "Add Link" dialog | ||
364 | - Remove more annoying URL parameters for shared links | ||
365 | - Code cleanup | ||
366 | |||
367 | ### Removed | ||
368 | - Remove jQuery | ||
369 | |||
370 | ### Security | ||
371 | - Don't disclose version to visitors (shaarli-version.txt) | ||
372 | |||
373 | |||
374 | ## [v0.0.43beta](https://github.com/shaarli/Shaarli/releases/tag/v0.0.43beta) - 2015-02-20 | ||
375 | ### Added | ||
376 | - Title button link URL is now configurable | ||
377 | - RainTPL's TMP and TPL directories path are now configurable | ||
378 | - Displayed URLs for each link are now clickable links | ||
379 | - Show links timestamps in Daily view | ||
380 | |||
381 | ### Changed | ||
382 | - Automatically prepend "Note:" to title of self-posts (posts not pointing to an URL) | ||
383 | - Make ATOM toolbar button optional (`SHOW_ATOM` configuration variable) | ||
384 | - Optional archive.org links for each Shaarli link (`ARCHIVE_ORG` option) | ||
385 | - Thumbnails: force HTTPS when possible | ||
386 | - Improve tag cloud font scaling | ||
387 | - Allow pointing RSS items to the permalink instead of the direct URL (`ENABLE_RSS_PERMALINKS` option) | ||
388 | - Update JS libraries and add version numbers in filenames | ||
389 | - Updates to README and footer | ||
390 | |||
391 | ### Fixed | ||
392 | - Fix problems when running Shaarli behind a reverse proxy (invalid RSS feed URL) | ||
393 | - Update check now checks against the community fork version | ||
394 | - Include `cache/`, `data/`, `pagecache/` and `tmp/` directories in the repository | ||
395 | - Fix duplicate tag search returning no results | ||
396 | - Fix unnecessary 404 error on "Add link" when the user is logged out | ||
397 | - Fixes to copyright/licensing information and unlicensed media | ||
398 | - Fixes for tag cloud invalid links | ||
399 | - Coding style fixes/cleanup | ||
400 | - Fix redirection after deleteing a link leading to a 404 error | ||
401 | - Shaarli's HTML is now W3C-compliant | ||
402 | - Search now works with Unicode characters | ||
403 | |||
404 | ### Security | ||
405 | - Do not leak server's PHP version and Shaarli's full path on errors | ||
406 | - Prevent Shaarli from sending a lot of duplicate cookies | ||
407 | |||
408 | |||
409 | ## [v0.0.42beta](https://github.com/shaarli/Shaarli/releases/tag/v0.0.42beta) - 2014-07-27 | ||
410 | ### Added | ||
411 | - Add QRCode Javascript library | ||
412 | - Allow importing bookmarks with the same timestamp (hack) | ||
413 | - Allow putting a description in the bookmarklet URL | ||
414 | - Add `json_encode()` implementation for PHP<5.2 | ||
415 | - Highlight search results | ||
416 | |||
417 | ### Changed | ||
418 | - Improve 'Stay signed in' behaviour | ||
419 | - Improve `smallHash()` | ||
420 | - Refactor QRCode generation | ||
421 | - Update Javascript lazyloading | ||
422 | - Update CSS | ||
423 | |||
424 | ### Removed | ||
425 | - Remove jQuery from almost all pages | ||
426 | |||
427 | ### Fixed | ||
428 | - Fix overlapping tags | ||
429 | - Fix field foxus in the bookmarklet | ||
430 | - Fix error message when `data/` is not writable | ||
431 | - Fix HTML generation | ||
432 | |||
433 | ### Security | ||
434 | - Fix XSS flaw | ||
435 | |||
436 | |||
437 | ## [v0.0.41beta](https://github.com/shaarli/Shaarli/releases/tag/v0.0.41beta) - 2013-03-08 | ||
438 | ### Added | ||
439 | - Add HTTPS to the allowed protocols | ||
440 | - Add support for magnet links in link descriptions | ||
441 | - Allow creating new links as private by default | ||
442 | - Allow disabling jQuery | ||
443 | - Check write permissions | ||
444 | - Check session support before installation | ||
445 | |||
446 | ### Changed | ||
447 | - Improve token security | ||
448 | - RSS feed: allow inverting links/permalinks | ||
449 | |||
450 | ### Fixed | ||
451 | - Fix display issues during installation | ||
452 | - Fix popup redirection after login failure | ||
453 | - Fix RSS formatting for Thunderbird | ||
454 | - Fix thumbnail creation | ||
455 | - Fix cache purge | ||
456 | |||
457 | ### Security | ||
458 | - Fix login issue with WebKit browsers | ||
459 | |||
460 | |||
461 | ## [v0.0.40beta](https://github.com/shaarli/Shaarli/releases/tag/v0.0.40beta) - 2013-02-26 | ||
462 | Initial release on GitHub. | ||
463 | |||
464 | |||
465 | ## [v0.0.40beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2012-08-24 | ||
466 | ### Added | ||
467 | - Flickr thumbnail now also support albums, galleries and users | ||
468 | - Add a configuration option to disable session cookie protection | ||
469 | Check this if your get disconnected often or your IP address changes often | ||
470 | |||
471 | ### Removed | ||
472 | - Removed the xml comment in cached RSS/ATOM feed | ||
473 | (although W3C-compliant, this may cause problems in some feed readers) | ||
474 | |||
475 | ### Fixed | ||
476 | - A bug in the RSS cache would present old items as new in some cases | ||
477 | - A small bug (non-initialized variable) in page cache cleaning | ||
478 | - Proper "Nothing found" message when search returns no results | ||
479 | - No more 404 error when searching with empty input | ||
480 | - Flickr thumbnails are back (Flickr has made some changes to their domains) | ||
481 | |||
482 | ## [v0.0.39beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2012-08-10 | ||
483 | ### Added | ||
484 | - A cache for RSS feed, ATOM feed and Daily RSS feed, because these URLs | ||
485 | are massively hammered. Cache is automatically purged whenever the database | ||
486 | is changed. This will reduce server load. I may add cache to other pages later. | ||
487 | |||
488 | ### Changed | ||
489 | - No more global `$LINKSDB` (Yuk) | ||
490 | - Background color was removed when hovering a link | ||
491 | |||
492 | ### Fixed | ||
493 | - Small bug corrected in config screen on timezones | ||
494 | - Calling a non-existing permalink now returns a crude 404 error instead of 200 (OK) | ||
495 | This is done on purpose | ||
496 | - The `shaarli` session cookie now has a proper path | ||
497 | Thus you can now install several Shaarlis on the same server in different paths, | ||
498 | and each will have its session | ||
499 | - Now when you delete a link, you go back the same page/search parameters you were on | ||
500 | - Restore previously removed `error_get_last()`, to ensure PHP 5.1 compatibility | ||
501 | (Yes, now it works on free.fr hosting) | ||
502 | - Added `dialog=1` in bookmarklet code for some browsers | ||
503 | |||
504 | |||
505 | ## [v0.0.38beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2012-02-06 | ||
506 | ### Added | ||
507 | - Automatic creation of the `tmp` directory with proper rights (for RainTPL) | ||
508 | - When you click the key to see only private links, it turns yellow | ||
509 | |||
510 | ### Changed | ||
511 | - The "Daily" page now automatically skips empty days. | ||
512 | |||
513 | ### Fixed | ||
514 | - Corrected the tag encoding (there was a bug when selecting a second tag which contains accented characters) | ||
515 | |||
516 | |||
517 | ## [v0.0.37beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2012-02-01 | ||
518 | ### Added | ||
519 | - Basic CSS for mobiles, which makes Shaarli //much// more usable on mobile devices | ||
520 | - Picture wall no more instantly kills your browser. Now it uses | ||
521 | [lazy image loading](http://www.appelsiini.net/projects/lazyload); | ||
522 | the pictures are loaded only as you scroll the page. | ||
523 | This will reduce browser memory usage (especially on mobile devices), | ||
524 | as well as server load. | ||
525 | If you have javascript disabled, the page will still work as before | ||
526 | (all images loaded at once) | ||
527 | - RSS feed for the "Daily" page. 1 RSS entry per day, with all links of that day. | ||
528 | RSS feed provides the last 7 days (only non-empty days are returned). | ||
529 | - In link list, added an icon to see only private links. Click to toggle (only private / all) | ||
530 | |||
531 | |||
532 | ## [v0.0.36beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2012-01-25 | ||
533 | ### Added | ||
534 | - Shaarli licence in COPYING | ||
535 | |||
536 | ### Changed | ||
537 | - Display adjustments in "Daily" page | ||
538 | |||
539 | ### Fixed | ||
540 | - Improper text color in install form | ||
541 | - Error in QRCode url (missing '?') | ||
542 | |||
543 | |||
544 | ## [v0.0.35beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2012-01-25 | ||
545 | ### Fixed | ||
546 | - Corrected a bug introduced in 0.0.34 which would improperly preprend data to URLs | ||
547 | |||
548 | |||
549 | ## [v0.0.34beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2012-01-25 | ||
550 | ### Added | ||
551 | - There is now a QR-Code of each permalink to easily open a link on your smartphone | ||
552 | - Protocols `file:` and `apt:` are now also converted to clickable links (patch by Francis Chavanon) | ||
553 | - Thumbnail support for http://xkcd.com/ (patch by Emilien Klein) | ||
554 | - Thumbnail support for http://pix.toile-libre.org/ | ||
555 | - Well I had _some_ mercy for users with antique browsers (IE) which do not have | ||
556 | support for gradients: I added a few `background-color` | ||
557 | - First version of the "Shaarli Daily", a page showing all links of a specific day. | ||
558 | By default, you see the links of the previous day. | ||
559 | There is still work to do on this page (error checking, better navigation (calendar?), | ||
560 | RSS feed, CSS for mobile and printing...) | ||
561 | |||
562 | ### Changed | ||
563 | - Upgraded bundled versions of jQuery (1.7.1) and jQuery UI (1.8.17) | ||
564 | - Upgraded bundled version of RainTPL (2.7) | ||
565 | - Changed HTTPS detection code | ||
566 | |||
567 | ### Fixed | ||
568 | - In link edition, you can now click the word "Private" to check the box | ||
569 | - Clicking a tag would not work properly if the tag contained special characters (like +) | ||
570 | - Added proper jQuery licence (shame on me) | ||
571 | |||
572 | |||
573 | ## [v0.0.33beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2012-01-17 | ||
574 | ### Added | ||
575 | - Shaarli packaged to ease Linux distributions integration | ||
576 | As a simple user, you do not need to cope with these versions | ||
577 | Future releases of Shaarli will also be customized and published in these directories | ||
578 | Differences with the standard Shaarli version: | ||
579 | - deb: | ||
580 | - .tar.gz instead of .zip | ||
581 | - COPYING licence file added | ||
582 | - jQuery/jQuery-UI libraries removed to cope with Debian rules | ||
583 | This version links to the libs hosted at http://code.jquery.com | ||
584 | - rpm: | ||
585 | - sources located in a subdirectory with the same name as the zip file | ||
586 | - COPYING licence file added | ||
587 | - WARNING: When downloading the .tar.gz, always use wget (and not your browser), | ||
588 | otherwise the .tar.gz will be corrupted | ||
589 | |||
590 | ### Fixed | ||
591 | - ATOM feed validates again | ||
592 | |||
593 | ### Security | ||
594 | - XSS vulnerability patched (thanks to Stanislas D.!) | ||
595 | |||
596 | |||
597 | ## [v0.0.32beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2011-12-16 | ||
598 | ### Added | ||
599 | - Better check on URL parameters (patch by gege2061) | ||
600 | - Add `max-height` and `overflow:auto` attributes so that content can be scrolled if too large | ||
601 | |||
602 | ### Changed | ||
603 | - HTML generation moved to RainTPL templates (in the `tpl/` directory) | ||
604 | - Better detection of HTTPS (patch by gege2061) | ||
605 | - In RSS/ATOM feeds, the GUID is now the permalink instead of the final URL (patch by gege2061) | ||
606 | - Jerrywham CSS patch included | ||
607 | - Multiple spaces are now respected in description. | ||
608 | Thus you can use Shaarli as a personal pastebin (for posting source code, for example). | ||
609 | |||
610 | ### Removed | ||
611 | - Page time generation was removed | ||
612 | |||
613 | ### Fixed | ||
614 | - Tab order changed in login screen | ||
615 | - Permalinks now work even if additional parameters have been added | ||
616 | (e.g. `/?E8Yj2Q&utm_source=blablabla...`) | ||
617 | - user.css is included only if the file is present | ||
618 | (This prevents a useless CSS include which makes a harmless but useless 404 error.) | ||
619 | |||
620 | |||
621 | ## [v0.0.31beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2011-11-29 | ||
622 | ### Added | ||
623 | - Support for TED Talks (ted.com/talks) thumbnails (patch by Emilien K.) | ||
624 | - partial [patch](http://www.idleman.fr/blog/?p=508) by Idleman: Better design consistency, icon on private links. In-page popup was not included because it causes problem on some websites | ||
625 | - Support for bookmark files without ADD_DATE attributes | ||
626 | - Logo is clickable | ||
627 | - `user.css` can be added to overload Shaarli base CSS.(patch by Jerrywham). | ||
628 | Just put `user.css` in the same directory as shaarli.css. | ||
629 | Example: `<code css>#pageheader { background: blue; }</code>` | ||
630 | Please note that Shaarli CSS are not stable and may completely change on each version | ||
631 | |||
632 | ### Changed | ||
633 | - Edit and Delete buttons in link list were replaced with icons. (patch by Jerrywham) | ||
634 | |||
635 | ### Fixed | ||
636 | - Better error handling in thumbnail generation (patch by Emilien K.) | ||
637 | - The top menu is no longer displayed in bookmarklet popup | ||
638 | - Bookmark which have the exact same date/time are now correctly imported. | ||
639 | Most remaining import problems should be solved now | ||
640 | - Comment in Shaarli export moved to beginning of file to prevent clash with last link description | ||
641 | |||
642 | |||
643 | ## [v0.0.30beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2011-11-18 | ||
644 | ### Added | ||
645 | - Add a small `delete` button in link list (after the `edit` button) | ||
646 | |||
647 | ### Fixed | ||
648 | - Moved the call to PubSubHub | ||
649 | |||
650 | |||
651 | ## [v0.0.29beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2011-11-18 | ||
652 | ### Fixed | ||
653 | - Corrected a bug introduced in v0.0.28beta | ||
654 | (there was an error if you use the bookmarklet and you're not logged in) | ||
655 | |||
656 | |||
657 | ## [v0.0.28beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2011-11-17 | ||
658 | ### Added | ||
659 | - Thumbnail support for youtu.be URLs (YouTube short url service) | ||
660 | - PubSubHub protocol support (from http://aldarone.fr/les-flux-rss-shaarli-et-pubsubhubbub/). | ||
661 | Warning: This was not tested. You need to set your hub url in | ||
662 | `$GLOBALS['config']['PUBSUBHUB_URL']` and put the official client (`publisher.php`) | ||
663 | in the same directory as Shaarli's `index.php` | ||
664 | - RSS and ATOM feeds now also contain tags (in `category` tags, as per their | ||
665 | respective specifications) | ||
666 | |||
667 | ### Changed | ||
668 | - New Shaarli theme and logo by Idle (http://www.idleman.fr/blog/?p=469) | ||
669 | - In picture wall, pictures point to Shaarli permalink instead of final URL. | ||
670 | This way, users can read the description. | ||
671 | - In RSS/ATOM feeds, guid and link URL of permalinks are now proper absolute URLs | ||
672 | - In RSS/ATOM feeds, URLs are now clickable | ||
673 | - Rename `http_parse_headers()` to `http_parse_headers_shaarli()` to prevent | ||
674 | name collision with some PHP extensions | ||
675 | |||
676 | ### Fixed | ||
677 | - Thumbnails removed for imgur.com/a/ URLs (Thumbnails are not available for albums on imgur) | ||
678 | - Shaarli now correctly only tries to get thumbnails for vimeo video URLs | ||
679 | - Fix a bug in imgur.com URLs handling that would cause some thumbnails not to appear | ||
680 | - The search engine would not return a result if the word to search was the first in description | ||
681 | - Extracted title is now correct if the page has two `title` html tags | ||
682 | |||
683 | |||
684 | ## [v0.0.27beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2011-10-18 | ||
685 | ### Added | ||
686 | - Add a picture wall, which can be filtered too: it will use the same filters | ||
687 | (tags,text search) as current page when clicked. | ||
688 | |||
689 | |||
690 | ## [v0.0.26beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2011-10-17 | ||
691 | ### Changed | ||
692 | - Made permalink more visible (smallHash) | ||
693 | |||
694 | ### Fixed | ||
695 | - Removed extras space in description when URLs are converted to clickable links | ||
696 | - Thumbnail for subreddit imgur urls (/r/...) were corrected (thanks to Accent Grave) | ||
697 | |||
698 | |||
699 | ## [v0.0.25beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2011-10-13 | ||
700 | ### Added | ||
701 | - Better CSS for printing (thanks to jerrywham suggestion) | ||
702 | - Allow using a redirector or anonymizing proxy for links | ||
703 | (such as `http://anonym.to/?` to mask you `HTTP_REFERER`). | ||
704 | Just go to `Tools > Configure > Redirector` | ||
705 | (thanks to Accent Grave for the suggestion). | ||
706 | - The `ENABLE_LOCALCACHE` option can be set to `false` for those who have | ||
707 | a limited quota on their host. | ||
708 | This will disable the local thumbnail cache. | ||
709 | Services which require the use of the cache will have no thumbnails | ||
710 | (vimeo, flickr, direct link to image). | ||
711 | Other services will still have a thumbnail (youtube,imgur.com,dailymotion,imageshack.us) | ||
712 | |||
713 | ### Changed | ||
714 | - Now thumbnails generated by Shaarli are croped to a height of 120 pixels | ||
715 | - YouTube thumbnails now use `default.jpg` instead of `2.jpg` (This is usually more pertinent) | ||
716 | - Configuration options (such as `HIDE_TIMESTAMPS`, `ENABLE_THUMBNAILS`, etc.) | ||
717 | can now be put in a an external file so that you do not have to tweak them again | ||
718 | when you upgrade Shaarli. | ||
719 | Just add the file `data/options.php`. | ||
720 | - If a single link is displayed, the page title contains the title of the link | ||
721 | - Shaarli page title is clickable (and has the same link as "Home") | ||
722 | - A few CSS tweaks (thanks to maethor for suggestion) | ||
723 | |||
724 | ### Fixed | ||
725 | - Shaarli now supports newlines in titles (thanks to dixy) | ||
726 | - The link to the RSS feed in page header was not correct | ||
727 | |||
728 | |||
729 | ## [v0.0.24beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
730 | ### Added | ||
731 | - Allow posting an entry without a link. (You can use Shaarli as a kind of "personal twitter") | ||
732 | - Each Shaarli entry now has a short link (just click on the date of a link). | ||
733 | Now you can send a link that points to a single entry in your Shaarli | ||
734 | - In descriptions, URLs are now clickable | ||
735 | - Thumbnails will be generated for all link pointing to .jpg/png/gif | ||
736 | (as long as the images are less than 4 Mb and take less than 30 seconds to download) | ||
737 | |||
738 | ### Fixed | ||
739 | - Now thumbnails also work for imgur gallery links (/gallery/...) | ||
740 | (Thanks to Accent Grave for the correction) | ||
741 | - Removed useless debugging information in log | ||
742 | - The filter in RSS/ATOM feed now works again properly (it was broken in 0.0.17beta) | ||
743 | |||
744 | |||
745 | ## [v0.0.23beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
746 | ### Added | ||
747 | - Added thumbnail support for imageshack.us | ||
748 | |||
749 | ### Changed | ||
750 | - Now you can clic the sentence "Stay signed in" to tick the checkbox (patch by Emilien) | ||
751 | - In tag editing, comma (,) are now automatically converted to spaces | ||
752 | - In tag editing, autocomplete no longuer suggests a tag you have already entered in the same line | ||
753 | |||
754 | |||
755 | ## [v0.0.22beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
756 | ### Added | ||
757 | - Support for thumbnails for flickr.com | ||
758 | - Allow staying signed in: | ||
759 | Your session will be kept open even if you close the browser. | ||
760 | This is available through a checkbox in the login screen. | ||
761 | |||
762 | ### Changed | ||
763 | - Some hosts (flickr, vimeo) are slow as hell for the thumbnails, | ||
764 | or require an extra HTTP request. | ||
765 | For these hosts the thumbnail generation has been deported outside the generation | ||
766 | of the page to keep Shaarli snappy. | ||
767 | For these slow services, the thumbnails are also cached. | ||
768 | |||
769 | ### Fixed | ||
770 | - Title was not properly passed if you had to login when using the bookmarklet (patch by shenshei) | ||
771 | |||
772 | |||
773 | ## [v0.0.21beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
774 | ### Added | ||
775 | - Thumbnails for some services | ||
776 | Currently supports: YouTube.com, dailymotion.com, vimeo.com (slow!) and imgur.com. | ||
777 | Thumbnails are enabled by default, but you can turn them off | ||
778 | (set `define('ENABLE_THUMBNAILS',true);` to `false`). | ||
779 | |||
780 | ### Changed | ||
781 | - Removed the focus on the searchbox (this is cumbersome when you want to browse pages | ||
782 | and scroll with the keyboard) | ||
783 | |||
784 | |||
785 | ## [v0.0.20beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
786 | ### Fixed | ||
787 | - RSS feed is now served as `application/rss+xml` instead of `application/xhtml+xml` | ||
788 | (which was causing problem in //RSS Lounge//) | ||
789 | - ATOM feed is now served as `application/atom+xml` instead of `application/xhtml+xml` | ||
790 | |||
791 | |||
792 | ## [v0.0.19beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
793 | ### Added | ||
794 | - ATOM feed | ||
795 | |||
796 | ### Fixed | ||
797 | - Patch by Emilien to remove the update notification after the update | ||
798 | |||
799 | |||
800 | ## [v0.0.18beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
801 | ### Added | ||
802 | - You can now configure the title of your page | ||
803 | - New screen to configure title and timezone | ||
804 | |||
805 | ### Changed | ||
806 | - Nicer timezone selection patch by killruana | ||
807 | |||
808 | ### Fixed | ||
809 | - New lines now appear correctly in the RSS feed descriptions. | ||
810 | |||
811 | |||
812 | ## [v0.0.17beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
813 | ### Added | ||
814 | - Change password screen added (based on a patch by killruana) | ||
815 | - Autocomplete in the tag search form | ||
816 | - You can rename or delete a tag in all links | ||
817 | (very handy if you misspelled a tag or want to merge tags) | ||
818 | - When you click the RSS feed, the feed will be filtered with the same filters | ||
819 | as the page you were viewing | ||
820 | |||
821 | ### Changed | ||
822 | - CSS adjustments by jerrywham | ||
823 | - Minor corrections | ||
824 | |||
825 | |||
826 | ## [v0.0.16beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
827 | ### Added | ||
828 | - Upgrade notification: | ||
829 | If a new version of Shaarli is available, you will be notified by a discreet | ||
830 | message in top-right corner. | ||
831 | This message will only be visible if you are logged in, and the check will be | ||
832 | performed at most once a day. | ||
833 | - Preliminary tag cloud (ugly for the moment, I need to find something better) | ||
834 | |||
835 | ### Changed | ||
836 | - Replaced `preg_match()` with `version_compare()` to check PHP version | ||
837 | - Includes a patch by Emilien K. to mask dates if user is not logged in. | ||
838 | The option can be activated by changing `define('HIDE_TIMESTAMPS',false);` to `true` | ||
839 | |||
840 | |||
841 | ## [v0.0.15beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
842 | ### Added | ||
843 | - New in import: Option to overwrite existing links when importing | ||
844 | - On free.fr, automatic creation of the `/sessions` directory | ||
845 | |||
846 | ### Changed | ||
847 | - CSS Stylesheet is now an external file (shaarli.css). | ||
848 | This reduces page size and eases customization. | ||
849 | |||
850 | ### Removed | ||
851 | - Removed some parameters in URL added by some feed proxies (`#xtor=RSS-...`) | ||
852 | |||
853 | ### Fixed | ||
854 | - Bug corrected: Prevented loop on login screen upon successful login after a failed login | ||
855 | - Bug corrected in import: HTML entities were not properly decoded. | ||
856 | If you imported your Delicious/Diigo bookmarks, your should import them again | ||
857 | and use the 'overwrite' option of the import feature. | ||
858 | |||
859 | |||
860 | ## [v0.0.14beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
861 | ### Added | ||
862 | - You no longer need to disable `magic_quotes` on your host. | ||
863 | Shaarli will cope with this option beeing activated. | ||
864 | |||
865 | |||
866 | ## [v0.0.13beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
867 | ### Added | ||
868 | - Import: New option to import html bookmark file as private links | ||
869 | - Import: Importing a bookmark file will not overwrite existing links anymore | ||
870 | - Export: New options to export only public or private links | ||
871 | |||
872 | ### Changed | ||
873 | - In tag autocomplete, tags are presented in use order | ||
874 | (most used tags first, instead of alphabetical order) | ||
875 | - RSS Feed can now be filtered by tags or fulltext search. Just add to the feed url: | ||
876 | - `&searchtags=minecraft+video` for tag filtering | ||
877 | - `&searchterm=portal` for fulltext search to the feed url | ||
878 | |||
879 | |||
880 | ## [v0.0.12beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
881 | ### Added | ||
882 | - Add a check that the config file was properly created | ||
883 | (in case Shaarli does not have the write rights in its folder) | ||
884 | - Open Shaarli: there is an option to open your Shaarli to anyone. | ||
885 | Anybody will be able to add/edit/delete links without having to login. | ||
886 | In code, change `define('OPEN_SHAARLI',false);` to `true`. | ||
887 | Note: No anti-spam for the moment. You are warned! | ||
888 | - Autocomplete for tags | ||
889 | |||
890 | |||
891 | ## [v0.0.11beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
892 | ### Added | ||
893 | - Add a check and a warning for some hosts which still have `magic_quotes` activated | ||
894 | |||
895 | |||
896 | ## [v0.0.10beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
897 | ### Added | ||
898 | - Get rid of `"` in titles | ||
899 | |||
900 | |||
901 | ## [v0.0.9beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
902 | ### Added | ||
903 | - Now works on hosts `free.fr` and `1and1` | ||
904 | - Now works with PHP 5.1 | ||
905 | - PHP version is now checked and an error message is displayed if version is not correct | ||
906 | |||
907 | ### Fixed | ||
908 | - No more error messages if the browser does not send `HTTP_REFERER` | ||
909 | - No more error messages if the host has disabled http protocol in PHP config (eg. 1and1) | ||
910 | |||
911 | |||
912 | ## [v0.0.8beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) | ||
913 | ### Changed | ||
914 | - In RSS feed, GUID content replaced with the URL of the link, because some | ||
915 | stupid RSS reader (like Google Reader) use `<guid>` as a link instead of using `<link>` | ||
916 | |||
917 | |||
918 | ## [v0.0.7beta](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - 2011-09-16 | ||
919 | First public release by Sebsauvage, see original article: | ||
920 | [Adieu Delicious, Diigo et StumbleUpon. Salut Shaarli !](http://sebsauvage.net/rhaa/index.php?2011/09/16/09/29/58-adieu-delicious-diigo-et-stumbleupon-salut-shaarli-) (FR) | ||
@@ -72,10 +72,6 @@ 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 | |||
79 | ---------------------------------------------------- | 75 | ---------------------------------------------------- |
80 | ZLIB/LIBPNG LICENSE | 76 | ZLIB/LIBPNG LICENSE |
81 | 77 | ||
@@ -1,5 +1,5 @@ | |||
1 | # Shaarli, the personal, minimalist, super-fast, no-database delicious clone. | 1 | # The personal, minimalist, super-fast, database free, bookmarking service. |
2 | # Makefile for PHP code analysis & testing | 2 | # Makefile for PHP code analysis & testing, documentation and release generation |
3 | 3 | ||
4 | # Prerequisites: | 4 | # Prerequisites: |
5 | # - install Composer, either: | 5 | # - install Composer, either: |
@@ -128,6 +128,39 @@ test: | |||
128 | @$(BIN)/phpunit tests | 128 | @$(BIN)/phpunit tests |
129 | 129 | ||
130 | ## | 130 | ## |
131 | # Custom release archive generation | ||
132 | # | ||
133 | # For each tagged revision, GitHub provides tar and zip archives that correspond | ||
134 | # to the output of git-archive | ||
135 | # | ||
136 | # These targets produce similar archives, featuring 3rd-party dependencies | ||
137 | # to ease deployment on shared hosting. | ||
138 | ## | ||
139 | ARCHIVE_VERSION := shaarli-$$(git describe)-full | ||
140 | ARCHIVE_PREFIX=Shaarli/ | ||
141 | |||
142 | release_archive: release_tar release_zip | ||
143 | |||
144 | ### download 3rd-party PHP libraries | ||
145 | composer_dependencies: clean | ||
146 | composer update --no-dev | ||
147 | find vendor/ -name ".git" -type d -exec rm -rf {} + | ||
148 | |||
149 | ### generate a release tarball and include 3rd-party dependencies | ||
150 | release_tar: composer_dependencies | ||
151 | git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).tar HEAD | ||
152 | tar rvf $(ARCHIVE_VERSION).tar --transform "s|^vendor|$(ARCHIVE_PREFIX)vendor|" vendor/ | ||
153 | gzip $(ARCHIVE_VERSION).tar | ||
154 | |||
155 | ### generate a release zip and include 3rd-party dependencies | ||
156 | release_zip: composer_dependencies | ||
157 | git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).zip -9 HEAD | ||
158 | mkdir $(ARCHIVE_PREFIX) | ||
159 | rsync -a vendor/ $(ARCHIVE_PREFIX)vendor/ | ||
160 | zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)vendor/ | ||
161 | rm -rf $(ARCHIVE_PREFIX) | ||
162 | |||
163 | ## | ||
131 | # Targets for repository and documentation maintenance | 164 | # Targets for repository and documentation maintenance |
132 | ## | 165 | ## |
133 | 166 | ||
@@ -16,6 +16,7 @@ _It is designed to be personal (single-user), fast and handy._ | |||
16 | 16 | ||
17 | ## Quickstart | 17 | ## Quickstart |
18 | - [Wiki/documentation](https://github.com/shaarli/Shaarli/wiki) | 18 | - [Wiki/documentation](https://github.com/shaarli/Shaarli/wiki) |
19 | - [Change log](CHANGELOG.md) | ||
19 | - [Bugs/Feature requests/Discussion](https://github.com/shaarli/Shaarli/issues/) | 20 | - [Bugs/Feature requests/Discussion](https://github.com/shaarli/Shaarli/issues/) |
20 | 21 | ||
21 | ### Demo | 22 | ### Demo |
@@ -25,7 +26,8 @@ It runs the latest development version of Shaarli and is updated/reset daily. | |||
25 | Login: `demo`; Password: `demo` | 26 | Login: `demo`; Password: `demo` |
26 | 27 | ||
27 | ### Installation & upgrade | 28 | ### Installation & upgrade |
28 | - [Download](https://github.com/shaarli/Shaarli/wiki/Download) | 29 | - [Download and installation](https://github.com/shaarli/Shaarli/wiki/Download-and-Installation) |
30 | - [Upgrade and migration](https://github.com/shaarli/Shaarli/wiki/Upgrade-and-migration) | ||
29 | - [Server requirements](https://github.com/shaarli/Shaarli/wiki/Server-requirements) | 31 | - [Server requirements](https://github.com/shaarli/Shaarli/wiki/Server-requirements) |
30 | - [Server configuration](https://github.com/shaarli/Shaarli/wiki/Server-configuration) | 32 | - [Server configuration](https://github.com/shaarli/Shaarli/wiki/Server-configuration) |
31 | - [Shaarli configuration](https://github.com/shaarli/Shaarli/wiki/Shaarli-configuration) | 33 | - [Shaarli configuration](https://github.com/shaarli/Shaarli/wiki/Shaarli-configuration) |
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/ApplicationUtils.php b/application/ApplicationUtils.php index 978fc9da..7f963e97 100644 --- a/application/ApplicationUtils.php +++ b/application/ApplicationUtils.php | |||
@@ -15,6 +15,9 @@ class ApplicationUtils | |||
15 | * | 15 | * |
16 | * The code is read from the raw content of the version file on the Git server. | 16 | * The code is read from the raw content of the version file on the Git server. |
17 | * | 17 | * |
18 | * @param string $url URL to reach to get the latest version. | ||
19 | * @param int $timeout Timeout to check the URL (in seconds). | ||
20 | * | ||
18 | * @return mixed the version code from the repository if available, else 'false' | 21 | * @return mixed the version code from the repository if available, else 'false' |
19 | */ | 22 | */ |
20 | public static function getLatestGitVersionCode($url, $timeout=2) | 23 | public static function getLatestGitVersionCode($url, $timeout=2) |
@@ -49,6 +52,7 @@ class ApplicationUtils | |||
49 | * @param int $checkInterval the minimum interval between update checks (in seconds | 52 | * @param int $checkInterval the minimum interval between update checks (in seconds |
50 | * @param bool $enableCheck whether to check for new versions | 53 | * @param bool $enableCheck whether to check for new versions |
51 | * @param bool $isLoggedIn whether the user is logged in | 54 | * @param bool $isLoggedIn whether the user is logged in |
55 | * @param string $branch check update for the given branch | ||
52 | * | 56 | * |
53 | * @throws Exception an invalid branch has been set for update checks | 57 | * @throws Exception an invalid branch has been set for update checks |
54 | * | 58 | * |
@@ -132,11 +136,11 @@ class ApplicationUtils | |||
132 | /** | 136 | /** |
133 | * Checks Shaarli has the proper access permissions to its resources | 137 | * Checks Shaarli has the proper access permissions to its resources |
134 | * | 138 | * |
135 | * @param array $globalConfig The $GLOBALS['config'] array | 139 | * @param ConfigManager $conf Configuration Manager instance. |
136 | * | 140 | * |
137 | * @return array A list of the detected configuration issues | 141 | * @return array A list of the detected configuration issues |
138 | */ | 142 | */ |
139 | public static function checkResourcePermissions($globalConfig) | 143 | public static function checkResourcePermissions($conf) |
140 | { | 144 | { |
141 | $errors = array(); | 145 | $errors = array(); |
142 | 146 | ||
@@ -145,19 +149,19 @@ class ApplicationUtils | |||
145 | 'application', | 149 | 'application', |
146 | 'inc', | 150 | 'inc', |
147 | 'plugins', | 151 | 'plugins', |
148 | $globalConfig['RAINTPL_TPL'] | 152 | $conf->get('resource.raintpl_tpl'), |
149 | ) as $path) { | 153 | ) as $path) { |
150 | if (! is_readable(realpath($path))) { | 154 | if (! is_readable(realpath($path))) { |
151 | $errors[] = '"'.$path.'" directory is not readable'; | 155 | $errors[] = '"'.$path.'" directory is not readable'; |
152 | } | 156 | } |
153 | } | 157 | } |
154 | 158 | ||
155 | // Check cache and data directories are readable and writeable | 159 | // Check cache and data directories are readable and writable |
156 | foreach (array( | 160 | foreach (array( |
157 | $globalConfig['CACHEDIR'], | 161 | $conf->get('resource.thumbnails_cache'), |
158 | $globalConfig['DATADIR'], | 162 | $conf->get('resource.data_dir'), |
159 | $globalConfig['PAGECACHE'], | 163 | $conf->get('resource.page_cache'), |
160 | $globalConfig['RAINTPL_TMP'] | 164 | $conf->get('resource.raintpl_tmp'), |
161 | ) as $path) { | 165 | ) as $path) { |
162 | if (! is_readable(realpath($path))) { | 166 | if (! is_readable(realpath($path))) { |
163 | $errors[] = '"'.$path.'" directory is not readable'; | 167 | $errors[] = '"'.$path.'" directory is not readable'; |
@@ -167,13 +171,13 @@ class ApplicationUtils | |||
167 | } | 171 | } |
168 | } | 172 | } |
169 | 173 | ||
170 | // Check configuration files are readable and writeable | 174 | // Check configuration files are readable and writable |
171 | foreach (array( | 175 | foreach (array( |
172 | $globalConfig['CONFIG_FILE'], | 176 | $conf->getConfigFileExt(), |
173 | $globalConfig['DATASTORE'], | 177 | $conf->get('resource.datastore'), |
174 | $globalConfig['IPBANS_FILENAME'], | 178 | $conf->get('resource.ban_file'), |
175 | $globalConfig['LOG_FILE'], | 179 | $conf->get('resource.log'), |
176 | $globalConfig['UPDATECHECK_FILENAME'] | 180 | $conf->get('resource.update_check'), |
177 | ) as $path) { | 181 | ) as $path) { |
178 | if (! is_file(realpath($path))) { | 182 | if (! is_file(realpath($path))) { |
179 | # the file may not exist yet | 183 | # the file may not exist yet |
diff --git a/application/CachedPage.php b/application/CachedPage.php index 50cfa9ac..5087d0c4 100644 --- a/application/CachedPage.php +++ b/application/CachedPage.php | |||
@@ -35,7 +35,7 @@ class CachedPage | |||
35 | /** | 35 | /** |
36 | * Returns the cached version of a page, if it exists and should be cached | 36 | * Returns the cached version of a page, if it exists and should be cached |
37 | * | 37 | * |
38 | * @return a cached version of the page if it exists, null otherwise | 38 | * @return string a cached version of the page if it exists, null otherwise |
39 | */ | 39 | */ |
40 | public function cachedVersion() | 40 | public function cachedVersion() |
41 | { | 41 | { |
diff --git a/application/Config.php b/application/Config.php deleted file mode 100644 index 05a59452..00000000 --- a/application/Config.php +++ /dev/null | |||
@@ -1,221 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * Functions related to configuration management. | ||
4 | */ | ||
5 | |||
6 | /** | ||
7 | * Re-write configuration file according to given array. | ||
8 | * Requires mandatory fields listed in $MANDATORY_FIELDS. | ||
9 | * | ||
10 | * @param array $config contains all configuration fields. | ||
11 | * @param bool $isLoggedIn true if user is logged in. | ||
12 | * | ||
13 | * @return void | ||
14 | * | ||
15 | * @throws MissingFieldConfigException: a mandatory field has not been provided in $config. | ||
16 | * @throws UnauthorizedConfigException: user is not authorize to change configuration. | ||
17 | * @throws Exception: an error occured while writing the new config file. | ||
18 | */ | ||
19 | function writeConfig($config, $isLoggedIn) | ||
20 | { | ||
21 | // These fields are required in configuration. | ||
22 | $MANDATORY_FIELDS = array( | ||
23 | 'login', 'hash', 'salt', 'timezone', 'title', 'titleLink', | ||
24 | 'redirector', 'disablesessionprotection', 'privateLinkByDefault' | ||
25 | ); | ||
26 | |||
27 | if (!isset($config['config']['CONFIG_FILE'])) { | ||
28 | throw new MissingFieldConfigException('CONFIG_FILE'); | ||
29 | } | ||
30 | |||
31 | // Only logged in user can alter config. | ||
32 | if (is_file($config['config']['CONFIG_FILE']) && !$isLoggedIn) { | ||
33 | throw new UnauthorizedConfigException(); | ||
34 | } | ||
35 | |||
36 | // Check that all mandatory fields are provided in $config. | ||
37 | foreach ($MANDATORY_FIELDS as $field) { | ||
38 | if (!isset($config[$field])) { | ||
39 | throw new MissingFieldConfigException($field); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | $configStr = '<?php '. PHP_EOL; | ||
44 | $configStr .= '$GLOBALS[\'login\'] = '.var_export($config['login'], true).';'. PHP_EOL; | ||
45 | $configStr .= '$GLOBALS[\'hash\'] = '.var_export($config['hash'], true).';'. PHP_EOL; | ||
46 | $configStr .= '$GLOBALS[\'salt\'] = '.var_export($config['salt'], true).'; '. PHP_EOL; | ||
47 | $configStr .= '$GLOBALS[\'timezone\'] = '.var_export($config['timezone'], true).';'. PHP_EOL; | ||
48 | $configStr .= 'date_default_timezone_set('.var_export($config['timezone'], true).');'. PHP_EOL; | ||
49 | $configStr .= '$GLOBALS[\'title\'] = '.var_export($config['title'], true).';'. PHP_EOL; | ||
50 | $configStr .= '$GLOBALS[\'titleLink\'] = '.var_export($config['titleLink'], true).'; '. PHP_EOL; | ||
51 | $configStr .= '$GLOBALS[\'redirector\'] = '.var_export($config['redirector'], true).'; '. PHP_EOL; | ||
52 | $configStr .= '$GLOBALS[\'disablesessionprotection\'] = '.var_export($config['disablesessionprotection'], true).'; '. PHP_EOL; | ||
53 | $configStr .= '$GLOBALS[\'privateLinkByDefault\'] = '.var_export($config['privateLinkByDefault'], true).'; '. PHP_EOL; | ||
54 | |||
55 | // Store all $config['config'] | ||
56 | foreach ($config['config'] as $key => $value) { | ||
57 | $configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($config['config'][$key], true).';'. PHP_EOL; | ||
58 | } | ||
59 | |||
60 | if (isset($config['plugins'])) { | ||
61 | foreach ($config['plugins'] as $key => $value) { | ||
62 | $configStr .= '$GLOBALS[\'plugins\'][\''. $key .'\'] = '.var_export($config['plugins'][$key], true).';'. PHP_EOL; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | if (!file_put_contents($config['config']['CONFIG_FILE'], $configStr) | ||
67 | || strcmp(file_get_contents($config['config']['CONFIG_FILE']), $configStr) != 0 | ||
68 | ) { | ||
69 | throw new Exception( | ||
70 | 'Shaarli could not create the config file. | ||
71 | Please make sure Shaarli has the right to write in the folder is it installed in.' | ||
72 | ); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * Process plugin administration form data and save it in an array. | ||
78 | * | ||
79 | * @param array $formData Data sent by the plugin admin form. | ||
80 | * | ||
81 | * @return array New list of enabled plugin, ordered. | ||
82 | * | ||
83 | * @throws PluginConfigOrderException Plugins can't be sorted because their order is invalid. | ||
84 | */ | ||
85 | function save_plugin_config($formData) | ||
86 | { | ||
87 | // Make sure there are no duplicates in orders. | ||
88 | if (!validate_plugin_order($formData)) { | ||
89 | throw new PluginConfigOrderException(); | ||
90 | } | ||
91 | |||
92 | $plugins = array(); | ||
93 | $newEnabledPlugins = array(); | ||
94 | foreach ($formData as $key => $data) { | ||
95 | if (startsWith($key, 'order')) { | ||
96 | continue; | ||
97 | } | ||
98 | |||
99 | // If there is no order, it means a disabled plugin has been enabled. | ||
100 | if (isset($formData['order_' . $key])) { | ||
101 | $plugins[(int) $formData['order_' . $key]] = $key; | ||
102 | } | ||
103 | else { | ||
104 | $newEnabledPlugins[] = $key; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | // New enabled plugins will be added at the end of order. | ||
109 | $plugins = array_merge($plugins, $newEnabledPlugins); | ||
110 | |||
111 | // Sort plugins by order. | ||
112 | if (!ksort($plugins)) { | ||
113 | throw new PluginConfigOrderException(); | ||
114 | } | ||
115 | |||
116 | $finalPlugins = array(); | ||
117 | // Make plugins order continuous. | ||
118 | foreach ($plugins as $plugin) { | ||
119 | $finalPlugins[] = $plugin; | ||
120 | } | ||
121 | |||
122 | return $finalPlugins; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * Validate plugin array submitted. | ||
127 | * Will fail if there is duplicate orders value. | ||
128 | * | ||
129 | * @param array $formData Data from submitted form. | ||
130 | * | ||
131 | * @return bool true if ok, false otherwise. | ||
132 | */ | ||
133 | function validate_plugin_order($formData) | ||
134 | { | ||
135 | $orders = array(); | ||
136 | foreach ($formData as $key => $value) { | ||
137 | // No duplicate order allowed. | ||
138 | if (in_array($value, $orders)) { | ||
139 | return false; | ||
140 | } | ||
141 | |||
142 | if (startsWith($key, 'order')) { | ||
143 | $orders[] = $value; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | return true; | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * Affect plugin parameters values into plugins array. | ||
152 | * | ||
153 | * @param mixed $plugins Plugins array ($plugins[<plugin_name>]['parameters']['param_name'] = <value>. | ||
154 | * @param mixed $config Plugins configuration. | ||
155 | * | ||
156 | * @return mixed Updated $plugins array. | ||
157 | */ | ||
158 | function load_plugin_parameter_values($plugins, $config) | ||
159 | { | ||
160 | $out = $plugins; | ||
161 | foreach ($plugins as $name => $plugin) { | ||
162 | if (empty($plugin['parameters'])) { | ||
163 | continue; | ||
164 | } | ||
165 | |||
166 | foreach ($plugin['parameters'] as $key => $param) { | ||
167 | if (!empty($config[$key])) { | ||
168 | $out[$name]['parameters'][$key] = $config[$key]; | ||
169 | } | ||
170 | } | ||
171 | } | ||
172 | |||
173 | return $out; | ||
174 | } | ||
175 | |||
176 | /** | ||
177 | * Exception used if a mandatory field is missing in given configuration. | ||
178 | */ | ||
179 | class MissingFieldConfigException extends Exception | ||
180 | { | ||
181 | public $field; | ||
182 | |||
183 | /** | ||
184 | * Construct exception. | ||
185 | * | ||
186 | * @param string $field field name missing. | ||
187 | */ | ||
188 | public function __construct($field) | ||
189 | { | ||
190 | $this->field = $field; | ||
191 | $this->message = 'Configuration value is required for '. $this->field; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * Exception used if an unauthorized attempt to edit configuration has been made. | ||
197 | */ | ||
198 | class UnauthorizedConfigException extends Exception | ||
199 | { | ||
200 | /** | ||
201 | * Construct exception. | ||
202 | */ | ||
203 | public function __construct() | ||
204 | { | ||
205 | $this->message = 'You are not authorized to alter config.'; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * Exception used if an error occur while saving plugin configuration. | ||
211 | */ | ||
212 | class PluginConfigOrderException extends Exception | ||
213 | { | ||
214 | /** | ||
215 | * Construct exception. | ||
216 | */ | ||
217 | public function __construct() | ||
218 | { | ||
219 | $this->message = 'An error occurred while trying to save plugins loading order.'; | ||
220 | } | ||
221 | } | ||
diff --git a/application/FeedBuilder.php b/application/FeedBuilder.php index ddefe6ce..fedd90e6 100644 --- a/application/FeedBuilder.php +++ b/application/FeedBuilder.php | |||
@@ -124,7 +124,8 @@ class FeedBuilder | |||
124 | $data['last_update'] = $this->getLatestDateFormatted(); | 124 | $data['last_update'] = $this->getLatestDateFormatted(); |
125 | $data['show_dates'] = !$this->hideDates || $this->isLoggedIn; | 125 | $data['show_dates'] = !$this->hideDates || $this->isLoggedIn; |
126 | // Remove leading slash from REQUEST_URI. | 126 | // Remove leading slash from REQUEST_URI. |
127 | $data['self_link'] = $pageaddr . escape(ltrim($this->serverInfo['REQUEST_URI'], '/')); | 127 | $data['self_link'] = escape(server_url($this->serverInfo)) |
128 | . escape($this->serverInfo['REQUEST_URI']); | ||
128 | $data['index_url'] = $pageaddr; | 129 | $data['index_url'] = $pageaddr; |
129 | $data['usepermalinks'] = $this->usePermalinks === true; | 130 | $data['usepermalinks'] = $this->usePermalinks === true; |
130 | $data['links'] = $linkDisplayed; | 131 | $data['links'] = $linkDisplayed; |
@@ -142,7 +143,7 @@ class FeedBuilder | |||
142 | */ | 143 | */ |
143 | protected function buildItem($link, $pageaddr) | 144 | protected function buildItem($link, $pageaddr) |
144 | { | 145 | { |
145 | $link['guid'] = $pageaddr .'?'. smallHash($link['linkdate']); | 146 | $link['guid'] = $pageaddr .'?'. $link['shorturl']; |
146 | // Check for both signs of a note: starting with ? and 7 chars long. | 147 | // Check for both signs of a note: starting with ? and 7 chars long. |
147 | if ($link['url'][0] === '?' && strlen($link['url']) === 7) { | 148 | if ($link['url'][0] === '?' && strlen($link['url']) === 7) { |
148 | $link['url'] = $pageaddr . $link['url']; | 149 | $link['url'] = $pageaddr . $link['url']; |
@@ -152,19 +153,26 @@ class FeedBuilder | |||
152 | } else { | 153 | } else { |
153 | $permalink = '<a href="'. $link['guid'] .'" title="Permalink">Permalink</a>'; | 154 | $permalink = '<a href="'. $link['guid'] .'" title="Permalink">Permalink</a>'; |
154 | } | 155 | } |
155 | $link['description'] = format_description($link['description']) . PHP_EOL .'<br>— '. $permalink; | 156 | $link['description'] = format_description($link['description'], '', $pageaddr); |
157 | $link['description'] .= PHP_EOL .'<br>— '. $permalink; | ||
156 | 158 | ||
157 | $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); | 159 | $pubDate = $link['created']; |
160 | $link['pub_iso_date'] = $this->getIsoDate($pubDate); | ||
158 | 161 | ||
159 | if ($this->feedType == self::$FEED_RSS) { | 162 | // atom:entry elements MUST contain exactly one atom:updated element. |
160 | $link['iso_date'] = $date->format(DateTime::RSS); | 163 | if (!empty($link['updated'])) { |
164 | $upDate = $link['updated']; | ||
165 | $link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM); | ||
161 | } else { | 166 | } else { |
162 | $link['iso_date'] = $date->format(DateTime::ATOM); | 167 | $link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM);; |
163 | } | 168 | } |
164 | 169 | ||
165 | // Save the more recent item. | 170 | // Save the more recent item. |
166 | if (empty($this->latestDate) || $this->latestDate < $date) { | 171 | if (empty($this->latestDate) || $this->latestDate < $pubDate) { |
167 | $this->latestDate = $date; | 172 | $this->latestDate = $pubDate; |
173 | } | ||
174 | if (!empty($upDate) && $this->latestDate < $upDate) { | ||
175 | $this->latestDate = $upDate; | ||
168 | } | 176 | } |
169 | 177 | ||
170 | $taglist = array_filter(explode(' ', $link['tags']), 'strlen'); | 178 | $taglist = array_filter(explode(' ', $link['tags']), 'strlen'); |
@@ -250,6 +258,26 @@ class FeedBuilder | |||
250 | } | 258 | } |
251 | 259 | ||
252 | /** | 260 | /** |
261 | * Get ISO date from DateTime according to feed type. | ||
262 | * | ||
263 | * @param DateTime $date Date to format. | ||
264 | * @param string|bool $format Force format. | ||
265 | * | ||
266 | * @return string Formatted date. | ||
267 | */ | ||
268 | protected function getIsoDate(DateTime $date, $format = false) | ||
269 | { | ||
270 | if ($format !== false) { | ||
271 | return $date->format($format); | ||
272 | } | ||
273 | if ($this->feedType == self::$FEED_RSS) { | ||
274 | return $date->format(DateTime::RSS); | ||
275 | |||
276 | } | ||
277 | return $date->format(DateTime::ATOM); | ||
278 | } | ||
279 | |||
280 | /** | ||
253 | * Returns the number of link to display according to 'nb' user input parameter. | 281 | * Returns the number of link to display according to 'nb' user input parameter. |
254 | * | 282 | * |
255 | * If 'nb' not set or invalid, default value: $DEFAULT_NB_LINKS. | 283 | * If 'nb' not set or invalid, default value: $DEFAULT_NB_LINKS. |
diff --git a/application/FileUtils.php b/application/FileUtils.php index 6a12ef0e..6cac9825 100644 --- a/application/FileUtils.php +++ b/application/FileUtils.php | |||
@@ -9,11 +9,13 @@ class IOException extends Exception | |||
9 | /** | 9 | /** |
10 | * Construct a new IOException | 10 | * Construct a new IOException |
11 | * | 11 | * |
12 | * @param string $path path to the ressource that cannot be accessed | 12 | * @param string $path path to the resource that cannot be accessed |
13 | * @param string $message Custom exception message. | ||
13 | */ | 14 | */ |
14 | public function __construct($path) | 15 | public function __construct($path, $message = '') |
15 | { | 16 | { |
16 | $this->path = $path; | 17 | $this->path = $path; |
17 | $this->message = 'Error accessing '.$this->path; | 18 | $this->message = empty($message) ? 'Error accessing' : $message; |
19 | $this->message .= PHP_EOL . $this->path; | ||
18 | } | 20 | } |
19 | } | 21 | } |
diff --git a/application/HttpUtils.php b/application/HttpUtils.php index 2e0792f9..e705cfd6 100644 --- a/application/HttpUtils.php +++ b/application/HttpUtils.php | |||
@@ -1,6 +1,7 @@ | |||
1 | <?php | 1 | <?php |
2 | /** | 2 | /** |
3 | * GET an HTTP URL to retrieve its content | 3 | * GET an HTTP URL to retrieve its content |
4 | * Uses the cURL library or a fallback method | ||
4 | * | 5 | * |
5 | * @param string $url URL to get (http://...) | 6 | * @param string $url URL to get (http://...) |
6 | * @param int $timeout network timeout (in seconds) | 7 | * @param int $timeout network timeout (in seconds) |
@@ -20,38 +21,177 @@ | |||
20 | * echo 'There was an error: '.htmlspecialchars($headers[0]); | 21 | * echo 'There was an error: '.htmlspecialchars($headers[0]); |
21 | * } | 22 | * } |
22 | * | 23 | * |
23 | * @see http://php.net/manual/en/function.file-get-contents.php | 24 | * @see https://secure.php.net/manual/en/ref.curl.php |
24 | * @see http://php.net/manual/en/function.stream-context-create.php | 25 | * @see https://secure.php.net/manual/en/functions.anonymous.php |
25 | * @see http://php.net/manual/en/function.get-headers.php | 26 | * @see https://secure.php.net/manual/en/function.preg-split.php |
27 | * @see https://secure.php.net/manual/en/function.explode.php | ||
28 | * @see http://stackoverflow.com/q/17641073 | ||
29 | * @see http://stackoverflow.com/q/9183178 | ||
30 | * @see http://stackoverflow.com/q/1462720 | ||
26 | */ | 31 | */ |
27 | function get_http_response($url, $timeout = 30, $maxBytes = 4194304) | 32 | function get_http_response($url, $timeout = 30, $maxBytes = 4194304) |
28 | { | 33 | { |
29 | $urlObj = new Url($url); | 34 | $urlObj = new Url($url); |
30 | $cleanUrl = $urlObj->idnToAscii(); | 35 | $cleanUrl = $urlObj->idnToAscii(); |
31 | 36 | ||
32 | if (! filter_var($cleanUrl, FILTER_VALIDATE_URL) || ! $urlObj->isHttp()) { | 37 | if (!filter_var($cleanUrl, FILTER_VALIDATE_URL) || !$urlObj->isHttp()) { |
33 | return array(array(0 => 'Invalid HTTP Url'), false); | 38 | return array(array(0 => 'Invalid HTTP Url'), false); |
34 | } | 39 | } |
35 | 40 | ||
41 | $userAgent = | ||
42 | 'Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:45.0)' | ||
43 | . ' Gecko/20100101 Firefox/45.0'; | ||
44 | $acceptLanguage = | ||
45 | substr(setlocale(LC_COLLATE, 0), 0, 2) . ',en-US;q=0.7,en;q=0.3'; | ||
46 | $maxRedirs = 3; | ||
47 | |||
48 | if (!function_exists('curl_init')) { | ||
49 | return get_http_response_fallback( | ||
50 | $cleanUrl, | ||
51 | $timeout, | ||
52 | $maxBytes, | ||
53 | $userAgent, | ||
54 | $acceptLanguage, | ||
55 | $maxRedirs | ||
56 | ); | ||
57 | } | ||
58 | |||
59 | $ch = curl_init($cleanUrl); | ||
60 | if ($ch === false) { | ||
61 | return array(array(0 => 'curl_init() error'), false); | ||
62 | } | ||
63 | |||
64 | // General cURL settings | ||
65 | curl_setopt($ch, CURLOPT_AUTOREFERER, true); | ||
66 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); | ||
67 | curl_setopt($ch, CURLOPT_HEADER, true); | ||
68 | curl_setopt( | ||
69 | $ch, | ||
70 | CURLOPT_HTTPHEADER, | ||
71 | array('Accept-Language: ' . $acceptLanguage) | ||
72 | ); | ||
73 | curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRedirs); | ||
74 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | ||
75 | curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); | ||
76 | curl_setopt($ch, CURLOPT_USERAGENT, $userAgent); | ||
77 | |||
78 | // Max download size management | ||
79 | curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024); | ||
80 | curl_setopt($ch, CURLOPT_NOPROGRESS, false); | ||
81 | curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, | ||
82 | function($arg0, $arg1, $arg2, $arg3, $arg4 = 0) use ($maxBytes) | ||
83 | { | ||
84 | if (version_compare(phpversion(), '5.5', '<')) { | ||
85 | // PHP version lower than 5.5 | ||
86 | // Callback has 4 arguments | ||
87 | $downloaded = $arg1; | ||
88 | } else { | ||
89 | // Callback has 5 arguments | ||
90 | $downloaded = $arg2; | ||
91 | } | ||
92 | // Non-zero return stops downloading | ||
93 | return ($downloaded > $maxBytes) ? 1 : 0; | ||
94 | } | ||
95 | ); | ||
96 | |||
97 | $response = curl_exec($ch); | ||
98 | $errorNo = curl_errno($ch); | ||
99 | $errorStr = curl_error($ch); | ||
100 | $headSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); | ||
101 | curl_close($ch); | ||
102 | |||
103 | if ($response === false) { | ||
104 | if ($errorNo == CURLE_COULDNT_RESOLVE_HOST) { | ||
105 | /* | ||
106 | * Workaround to match fallback method behaviour | ||
107 | * Removing this would require updating | ||
108 | * GetHttpUrlTest::testGetInvalidRemoteUrl() | ||
109 | */ | ||
110 | return array(false, false); | ||
111 | } | ||
112 | return array(array(0 => 'curl_exec() error: ' . $errorStr), false); | ||
113 | } | ||
114 | |||
115 | // Formatting output like the fallback method | ||
116 | $rawHeaders = substr($response, 0, $headSize); | ||
117 | |||
118 | // Keep only headers from latest redirection | ||
119 | $rawHeadersArrayRedirs = explode("\r\n\r\n", trim($rawHeaders)); | ||
120 | $rawHeadersLastRedir = end($rawHeadersArrayRedirs); | ||
121 | |||
122 | $content = substr($response, $headSize); | ||
123 | $headers = array(); | ||
124 | foreach (preg_split('~[\r\n]+~', $rawHeadersLastRedir) as $line) { | ||
125 | if (empty($line) or ctype_space($line)) { | ||
126 | continue; | ||
127 | } | ||
128 | $splitLine = explode(': ', $line, 2); | ||
129 | if (count($splitLine) > 1) { | ||
130 | $key = $splitLine[0]; | ||
131 | $value = $splitLine[1]; | ||
132 | if (array_key_exists($key, $headers)) { | ||
133 | if (!is_array($headers[$key])) { | ||
134 | $headers[$key] = array(0 => $headers[$key]); | ||
135 | } | ||
136 | $headers[$key][] = $value; | ||
137 | } else { | ||
138 | $headers[$key] = $value; | ||
139 | } | ||
140 | } else { | ||
141 | $headers[] = $splitLine[0]; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | return array($headers, $content); | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * GET an HTTP URL to retrieve its content (fallback method) | ||
150 | * | ||
151 | * @param string $cleanUrl URL to get (http://... valid and in ASCII form) | ||
152 | * @param int $timeout network timeout (in seconds) | ||
153 | * @param int $maxBytes maximum downloaded bytes | ||
154 | * @param string $userAgent "User-Agent" header | ||
155 | * @param string $acceptLanguage "Accept-Language" header | ||
156 | * @param int $maxRedr maximum amount of redirections followed | ||
157 | * | ||
158 | * @return array HTTP response headers, downloaded content | ||
159 | * | ||
160 | * Output format: | ||
161 | * [0] = associative array containing HTTP response headers | ||
162 | * [1] = URL content (downloaded data) | ||
163 | * | ||
164 | * @see http://php.net/manual/en/function.file-get-contents.php | ||
165 | * @see http://php.net/manual/en/function.stream-context-create.php | ||
166 | * @see http://php.net/manual/en/function.get-headers.php | ||
167 | */ | ||
168 | function get_http_response_fallback( | ||
169 | $cleanUrl, | ||
170 | $timeout, | ||
171 | $maxBytes, | ||
172 | $userAgent, | ||
173 | $acceptLanguage, | ||
174 | $maxRedr | ||
175 | ) { | ||
36 | $options = array( | 176 | $options = array( |
37 | 'http' => array( | 177 | 'http' => array( |
38 | 'method' => 'GET', | 178 | 'method' => 'GET', |
39 | 'timeout' => $timeout, | 179 | 'timeout' => $timeout, |
40 | 'user_agent' => 'Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:45.0)' | 180 | 'user_agent' => $userAgent, |
41 | .' Gecko/20100101 Firefox/45.0', | 181 | 'header' => "Accept: */*\r\n" |
42 | 'accept_language' => substr(setlocale(LC_COLLATE, 0), 0, 2) . ',en-US;q=0.7,en;q=0.3', | 182 | . 'Accept-Language: ' . $acceptLanguage |
43 | ) | 183 | ) |
44 | ); | 184 | ); |
45 | 185 | ||
46 | stream_context_set_default($options); | 186 | stream_context_set_default($options); |
47 | list($headers, $finalUrl) = get_redirected_headers($cleanUrl); | 187 | list($headers, $finalUrl) = get_redirected_headers($cleanUrl, $maxRedr); |
48 | if (! $headers || strpos($headers[0], '200 OK') === false) { | 188 | if (! $headers || strpos($headers[0], '200 OK') === false) { |
49 | $options['http']['request_fulluri'] = true; | 189 | $options['http']['request_fulluri'] = true; |
50 | stream_context_set_default($options); | 190 | stream_context_set_default($options); |
51 | list($headers, $finalUrl) = get_redirected_headers($cleanUrl); | 191 | list($headers, $finalUrl) = get_redirected_headers($cleanUrl, $maxRedr); |
52 | } | 192 | } |
53 | 193 | ||
54 | if (! $headers || strpos($headers[0], '200 OK') === false) { | 194 | if (! $headers) { |
55 | return array($headers, false); | 195 | return array($headers, false); |
56 | } | 196 | } |
57 | 197 | ||
@@ -215,3 +355,29 @@ function page_url($server) | |||
215 | } | 355 | } |
216 | return index_url($server); | 356 | return index_url($server); |
217 | } | 357 | } |
358 | |||
359 | /** | ||
360 | * Retrieve the initial IP forwarded by the reverse proxy. | ||
361 | * | ||
362 | * Inspired from: https://github.com/zendframework/zend-http/blob/master/src/PhpEnvironment/RemoteAddress.php | ||
363 | * | ||
364 | * @param array $server $_SERVER array which contains HTTP headers. | ||
365 | * @param array $trustedIps List of trusted IP from the configuration. | ||
366 | * | ||
367 | * @return string|bool The forwarded IP, or false if none could be extracted. | ||
368 | */ | ||
369 | function getIpAddressFromProxy($server, $trustedIps) | ||
370 | { | ||
371 | $forwardedIpHeader = 'HTTP_X_FORWARDED_FOR'; | ||
372 | if (empty($server[$forwardedIpHeader])) { | ||
373 | return false; | ||
374 | } | ||
375 | |||
376 | $ips = preg_split('/\s*,\s*/', $server[$forwardedIpHeader]); | ||
377 | $ips = array_diff($ips, $trustedIps); | ||
378 | if (empty($ips)) { | ||
379 | return false; | ||
380 | } | ||
381 | |||
382 | return array_pop($ips); | ||
383 | } | ||
diff --git a/application/Languages.php b/application/Languages.php new file mode 100644 index 00000000..c8b0a25a --- /dev/null +++ b/application/Languages.php | |||
@@ -0,0 +1,21 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Wrapper function for translation which match the API | ||
5 | * of gettext()/_() and ngettext(). | ||
6 | * | ||
7 | * Not doing translation for now. | ||
8 | * | ||
9 | * @param string $text Text to translate. | ||
10 | * @param string $nText The plural message ID. | ||
11 | * @param int $nb The number of items for plural forms. | ||
12 | * | ||
13 | * @return String Text translated. | ||
14 | */ | ||
15 | function t($text, $nText = '', $nb = 0) { | ||
16 | if (empty($nText)) { | ||
17 | return $text; | ||
18 | } | ||
19 | $actualForm = $nb > 1 ? $nText : $text; | ||
20 | return sprintf($actualForm, $nb); | ||
21 | } | ||
diff --git a/application/LinkDB.php b/application/LinkDB.php index 1cb70de0..1e13286a 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php | |||
@@ -6,14 +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: date of the creation of this entry, in the form 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 | * - private: Is this link private? 0=no, other value=yes | 18 | * - private: Is this link private? 0=no, other value=yes |
18 | * - tags: tags attached to this entry (separated by spaces) | 19 | * - tags: tags attached to this entry (separated by spaces) |
19 | * - title Title of the link | 20 | * - title Title of the link |
@@ -21,16 +22,30 @@ | |||
21 | * Can be absolute or relative. | 22 | * Can be absolute or relative. |
22 | * Relative URLs are permalinks (e.g.'?m-ukcw') | 23 | * Relative URLs are permalinks (e.g.'?m-ukcw') |
23 | * - real_url Absolute processed URL. | 24 | * - real_url Absolute processed URL. |
25 | * - shorturl Permalink smallhash | ||
24 | * | 26 | * |
25 | * Implements 3 interfaces: | 27 | * Implements 3 interfaces: |
26 | * - ArrayAccess: behaves like an associative array; | 28 | * - ArrayAccess: behaves like an associative array; |
27 | * - Countable: there is a count() method; | 29 | * - Countable: there is a count() method; |
28 | * - 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 | ||
29 | */ | 44 | */ |
30 | class LinkDB implements Iterator, Countable, ArrayAccess | 45 | class LinkDB implements Iterator, Countable, ArrayAccess |
31 | { | 46 | { |
32 | // Links are stored as a PHP serialized string | 47 | // Links are stored as a PHP serialized string |
33 | private $_datastore; | 48 | private $datastore; |
34 | 49 | ||
35 | // Link date storage format | 50 | // Link date storage format |
36 | const LINK_DATE_FORMAT = 'Ymd_His'; | 51 | const LINK_DATE_FORMAT = 'Ymd_His'; |
@@ -44,26 +59,32 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
44 | // List of links (associative array) | 59 | // List of links (associative array) |
45 | // - key: link date (e.g. "20110823_124546"), | 60 | // - key: link date (e.g. "20110823_124546"), |
46 | // - value: associative array (keys: title, description...) | 61 | // - value: associative array (keys: title, description...) |
47 | private $_links; | 62 | private $links; |
63 | |||
64 | // List of all recorded URLs (key=url, value=link offset) | ||
65 | // for fast reserve search (url-->link offset) | ||
66 | private $urls; | ||
48 | 67 | ||
49 | // List of all recorded URLs (key=url, value=linkdate) | 68 | /** |
50 | // for fast reserve search (url-->linkdate) | 69 | * @var array List of all links IDS mapped with their array offset. |
51 | private $_urls; | 70 | * Map: id->offset. |
71 | */ | ||
72 | protected $ids; | ||
52 | 73 | ||
53 | // List of linkdate keys (for the Iterator interface implementation) | 74 | // List of offset keys (for the Iterator interface implementation) |
54 | private $_keys; | 75 | private $keys; |
55 | 76 | ||
56 | // Position in the $this->_keys array (for the Iterator interface) | 77 | // Position in the $this->keys array (for the Iterator interface) |
57 | private $_position; | 78 | private $position; |
58 | 79 | ||
59 | // Is the user logged in? (used to filter private links) | 80 | // Is the user logged in? (used to filter private links) |
60 | private $_loggedIn; | 81 | private $loggedIn; |
61 | 82 | ||
62 | // Hide public links | 83 | // Hide public links |
63 | private $_hidePublicLinks; | 84 | private $hidePublicLinks; |
64 | 85 | ||
65 | // link redirector set in user settings. | 86 | // link redirector set in user settings. |
66 | private $_redirector; | 87 | private $redirector; |
67 | 88 | ||
68 | /** | 89 | /** |
69 | * Set this to `true` to urlencode link behind redirector link, `false` to leave it untouched. | 90 | * Set this to `true` to urlencode link behind redirector link, `false` to leave it untouched. |
@@ -86,7 +107,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
86 | * @param string $redirector link redirector set in user settings. | 107 | * @param string $redirector link redirector set in user settings. |
87 | * @param boolean $redirectorEncode Enable urlencode on redirected urls (default: true). | 108 | * @param boolean $redirectorEncode Enable urlencode on redirected urls (default: true). |
88 | */ | 109 | */ |
89 | function __construct( | 110 | public function __construct( |
90 | $datastore, | 111 | $datastore, |
91 | $isLoggedIn, | 112 | $isLoggedIn, |
92 | $hidePublicLinks, | 113 | $hidePublicLinks, |
@@ -94,13 +115,13 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
94 | $redirectorEncode = true | 115 | $redirectorEncode = true |
95 | ) | 116 | ) |
96 | { | 117 | { |
97 | $this->_datastore = $datastore; | 118 | $this->datastore = $datastore; |
98 | $this->_loggedIn = $isLoggedIn; | 119 | $this->loggedIn = $isLoggedIn; |
99 | $this->_hidePublicLinks = $hidePublicLinks; | 120 | $this->hidePublicLinks = $hidePublicLinks; |
100 | $this->_redirector = $redirector; | 121 | $this->redirector = $redirector; |
101 | $this->redirectorEncode = $redirectorEncode === true; | 122 | $this->redirectorEncode = $redirectorEncode === true; |
102 | $this->_checkDB(); | 123 | $this->check(); |
103 | $this->_readDB(); | 124 | $this->read(); |
104 | } | 125 | } |
105 | 126 | ||
106 | /** | 127 | /** |
@@ -108,7 +129,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
108 | */ | 129 | */ |
109 | public function count() | 130 | public function count() |
110 | { | 131 | { |
111 | return count($this->_links); | 132 | return count($this->links); |
112 | } | 133 | } |
113 | 134 | ||
114 | /** | 135 | /** |
@@ -117,17 +138,29 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
117 | public function offsetSet($offset, $value) | 138 | public function offsetSet($offset, $value) |
118 | { | 139 | { |
119 | // TODO: use exceptions instead of "die" | 140 | // TODO: use exceptions instead of "die" |
120 | if (!$this->_loggedIn) { | 141 | if (!$this->loggedIn) { |
121 | die('You are not authorized to add a link.'); | 142 | die('You are not authorized to add a link.'); |
122 | } | 143 | } |
123 | if (empty($value['linkdate']) || empty($value['url'])) { | 144 | if (!isset($value['id']) || empty($value['url'])) { |
124 | 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.'); |
146 | } | ||
147 | if ((! empty($offset) && ! is_int($offset)) || ! is_int($value['id'])) { | ||
148 | die('You must specify an integer as a key.'); | ||
125 | } | 149 | } |
126 | if (empty($offset)) { | 150 | if (! empty($offset) && $offset !== $value['id']) { |
127 | die('You must specify a key.'); | 151 | die('Array offset and link ID must be equal.'); |
128 | } | 152 | } |
129 | $this->_links[$offset] = $value; | 153 | |
130 | $this->_urls[$value['url']]=$offset; | 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); | ||
160 | } | ||
161 | $this->links[$offset] = $value; | ||
162 | $this->urls[$value['url']] = $offset; | ||
163 | $this->ids[$value['id']] = $offset; | ||
131 | } | 164 | } |
132 | 165 | ||
133 | /** | 166 | /** |
@@ -135,7 +168,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
135 | */ | 168 | */ |
136 | public function offsetExists($offset) | 169 | public function offsetExists($offset) |
137 | { | 170 | { |
138 | return array_key_exists($offset, $this->_links); | 171 | return array_key_exists($this->getLinkOffset($offset), $this->links); |
139 | } | 172 | } |
140 | 173 | ||
141 | /** | 174 | /** |
@@ -143,13 +176,15 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
143 | */ | 176 | */ |
144 | public function offsetUnset($offset) | 177 | public function offsetUnset($offset) |
145 | { | 178 | { |
146 | if (!$this->_loggedIn) { | 179 | if (!$this->loggedIn) { |
147 | // TODO: raise an exception | 180 | // TODO: raise an exception |
148 | die('You are not authorized to delete a link.'); | 181 | die('You are not authorized to delete a link.'); |
149 | } | 182 | } |
150 | $url = $this->_links[$offset]['url']; | 183 | $realOffset = $this->getLinkOffset($offset); |
151 | unset($this->_urls[$url]); | 184 | $url = $this->links[$realOffset]['url']; |
152 | unset($this->_links[$offset]); | 185 | unset($this->urls[$url]); |
186 | unset($this->ids[$realOffset]); | ||
187 | unset($this->links[$realOffset]); | ||
153 | } | 188 | } |
154 | 189 | ||
155 | /** | 190 | /** |
@@ -157,31 +192,32 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
157 | */ | 192 | */ |
158 | public function offsetGet($offset) | 193 | public function offsetGet($offset) |
159 | { | 194 | { |
160 | return isset($this->_links[$offset]) ? $this->_links[$offset] : null; | 195 | $realOffset = $this->getLinkOffset($offset); |
196 | return isset($this->links[$realOffset]) ? $this->links[$realOffset] : null; | ||
161 | } | 197 | } |
162 | 198 | ||
163 | /** | 199 | /** |
164 | * Iterator - Returns the current element | 200 | * Iterator - Returns the current element |
165 | */ | 201 | */ |
166 | function current() | 202 | public function current() |
167 | { | 203 | { |
168 | return $this->_links[$this->_keys[$this->_position]]; | 204 | return $this[$this->keys[$this->position]]; |
169 | } | 205 | } |
170 | 206 | ||
171 | /** | 207 | /** |
172 | * Iterator - Returns the key of the current element | 208 | * Iterator - Returns the key of the current element |
173 | */ | 209 | */ |
174 | function key() | 210 | public function key() |
175 | { | 211 | { |
176 | return $this->_keys[$this->_position]; | 212 | return $this->keys[$this->position]; |
177 | } | 213 | } |
178 | 214 | ||
179 | /** | 215 | /** |
180 | * Iterator - Moves forward to next element | 216 | * Iterator - Moves forward to next element |
181 | */ | 217 | */ |
182 | function next() | 218 | public function next() |
183 | { | 219 | { |
184 | ++$this->_position; | 220 | ++$this->position; |
185 | } | 221 | } |
186 | 222 | ||
187 | /** | 223 | /** |
@@ -189,19 +225,18 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
189 | * | 225 | * |
190 | * Entries are sorted by date (latest first) | 226 | * Entries are sorted by date (latest first) |
191 | */ | 227 | */ |
192 | function rewind() | 228 | public function rewind() |
193 | { | 229 | { |
194 | $this->_keys = array_keys($this->_links); | 230 | $this->keys = array_keys($this->ids); |
195 | rsort($this->_keys); | 231 | $this->position = 0; |
196 | $this->_position = 0; | ||
197 | } | 232 | } |
198 | 233 | ||
199 | /** | 234 | /** |
200 | * Iterator - Checks if current position is valid | 235 | * Iterator - Checks if current position is valid |
201 | */ | 236 | */ |
202 | function valid() | 237 | public function valid() |
203 | { | 238 | { |
204 | return isset($this->_keys[$this->_position]); | 239 | return isset($this->keys[$this->position]); |
205 | } | 240 | } |
206 | 241 | ||
207 | /** | 242 | /** |
@@ -209,15 +244,16 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
209 | * | 244 | * |
210 | * If no DB file is found, creates a dummy DB. | 245 | * If no DB file is found, creates a dummy DB. |
211 | */ | 246 | */ |
212 | private function _checkDB() | 247 | private function check() |
213 | { | 248 | { |
214 | if (file_exists($this->_datastore)) { | 249 | if (file_exists($this->datastore)) { |
215 | return; | 250 | return; |
216 | } | 251 | } |
217 | 252 | ||
218 | // Create a dummy database for example | 253 | // Create a dummy database for example |
219 | $this->_links = array(); | 254 | $this->links = array(); |
220 | $link = array( | 255 | $link = array( |
256 | 'id' => 1, | ||
221 | 'title'=>' Shaarli: the personal, minimalist, super-fast, no-database delicious clone', | 257 | 'title'=>' Shaarli: the personal, minimalist, super-fast, no-database delicious clone', |
222 | 'url'=>'https://github.com/shaarli/Shaarli/wiki', | 258 | 'url'=>'https://github.com/shaarli/Shaarli/wiki', |
223 | '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. |
@@ -226,77 +262,69 @@ To learn how to use Shaarli, consult the link "Help/documentation" at the bottom | |||
226 | 262 | ||
227 | 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.', |
228 | 'private'=>0, | 264 | 'private'=>0, |
229 | 'linkdate'=> date('Ymd_His'), | 265 | 'created'=> new DateTime(), |
230 | 'tags'=>'opensource software' | 266 | 'tags'=>'opensource software' |
231 | ); | 267 | ); |
232 | $this->_links[$link['linkdate']] = $link; | 268 | $link['shorturl'] = link_small_hash($link['created'], $link['id']); |
269 | $this->links[1] = $link; | ||
233 | 270 | ||
234 | $link = array( | 271 | $link = array( |
272 | 'id' => 0, | ||
235 | 'title'=>'My secret stuff... - Pastebin.com', | 273 | 'title'=>'My secret stuff... - Pastebin.com', |
236 | 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', | 274 | 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', |
237 | '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.', |
238 | 'private'=>1, | 276 | 'private'=>1, |
239 | 'linkdate'=> date('Ymd_His', strtotime('-1 minute')), | 277 | 'created'=> new DateTime('1 minute ago'), |
240 | 'tags'=>'secretstuff' | 278 | 'tags'=>'secretstuff', |
241 | ); | 279 | ); |
242 | $this->_links[$link['linkdate']] = $link; | 280 | $link['shorturl'] = link_small_hash($link['created'], $link['id']); |
281 | $this->links[0] = $link; | ||
243 | 282 | ||
244 | // Write database to disk | 283 | // Write database to disk |
245 | $this->writeDB(); | 284 | $this->write(); |
246 | } | 285 | } |
247 | 286 | ||
248 | /** | 287 | /** |
249 | * Reads database from disk to memory | 288 | * Reads database from disk to memory |
250 | */ | 289 | */ |
251 | private function _readDB() | 290 | private function read() |
252 | { | 291 | { |
253 | |||
254 | // 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 |
255 | if ($this->_hidePublicLinks && !$this->_loggedIn) { | 293 | if ($this->hidePublicLinks && !$this->loggedIn) { |
256 | $this->_links = array(); | 294 | $this->links = array(); |
257 | return; | 295 | return; |
258 | } | 296 | } |
259 | 297 | ||
260 | // Read data | 298 | // Read data |
261 | // Note that gzinflate is faster than gzuncompress. | 299 | // Note that gzinflate is faster than gzuncompress. |
262 | // See: http://www.php.net/manual/en/function.gzdeflate.php#96439 | 300 | // See: http://www.php.net/manual/en/function.gzdeflate.php#96439 |
263 | $this->_links = array(); | 301 | $this->links = array(); |
264 | 302 | ||
265 | if (file_exists($this->_datastore)) { | 303 | if (file_exists($this->datastore)) { |
266 | $this->_links = unserialize(gzinflate(base64_decode( | 304 | $this->links = unserialize(gzinflate(base64_decode( |
267 | substr(file_get_contents($this->_datastore), | 305 | substr(file_get_contents($this->datastore), |
268 | strlen(self::$phpPrefix), -strlen(self::$phpSuffix))))); | 306 | strlen(self::$phpPrefix), -strlen(self::$phpSuffix))))); |
269 | } | 307 | } |
270 | 308 | ||
271 | // If user is not logged in, filter private links. | 309 | $toremove = array(); |
272 | if (!$this->_loggedIn) { | 310 | foreach ($this->links as $key => &$link) { |
273 | $toremove = array(); | 311 | if (! $this->loggedIn && $link['private'] != 0) { |
274 | foreach ($this->_links as $link) { | 312 | // Transition for not upgraded databases. |
275 | if ($link['private'] != 0) { | 313 | $toremove[] = $key; |
276 | $toremove[] = $link['linkdate']; | 314 | continue; |
277 | } | ||
278 | } | ||
279 | foreach ($toremove as $linkdate) { | ||
280 | unset($this->_links[$linkdate]); | ||
281 | } | 315 | } |
282 | } | ||
283 | |||
284 | $this->_urls = array(); | ||
285 | foreach ($this->_links as &$link) { | ||
286 | // Keep the list of the mapping URLs-->linkdate up-to-date. | ||
287 | $this->_urls[$link['url']] = $link['linkdate']; | ||
288 | 316 | ||
289 | // Sanitize data fields. | 317 | // Sanitize data fields. |
290 | sanitizeLink($link); | 318 | sanitizeLink($link); |
291 | 319 | ||
292 | // Remove private tags if the user is not logged in. | 320 | // Remove private tags if the user is not logged in. |
293 | if (! $this->_loggedIn) { | 321 | if (! $this->loggedIn) { |
294 | $link['tags'] = preg_replace('/(^| )\.[^($| )]+/', '', $link['tags']); | 322 | $link['tags'] = preg_replace('/(^|\s+)\.[^($|\s)]+\s*/', ' ', $link['tags']); |
295 | } | 323 | } |
296 | 324 | ||
297 | // Do not use the redirector for internal links (Shaarli note URL starting with a '?'). | 325 | // Do not use the redirector for internal links (Shaarli note URL starting with a '?'). |
298 | if (!empty($this->_redirector) && !startsWith($link['url'], '?')) { | 326 | if (!empty($this->redirector) && !startsWith($link['url'], '?')) { |
299 | $link['real_url'] = $this->_redirector; | 327 | $link['real_url'] = $this->redirector; |
300 | if ($this->redirectorEncode) { | 328 | if ($this->redirectorEncode) { |
301 | $link['real_url'] .= urlencode(unescape($link['url'])); | 329 | $link['real_url'] .= urlencode(unescape($link['url'])); |
302 | } else { | 330 | } else { |
@@ -306,7 +334,24 @@ You use the community supported version of the original Shaarli project, by Seba | |||
306 | else { | 334 | else { |
307 | $link['real_url'] = $link['url']; | 335 | $link['real_url'] = $link['url']; |
308 | } | 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 | } | ||
309 | } | 347 | } |
348 | |||
349 | // If user is not logged in, filter private links. | ||
350 | foreach ($toremove as $offset) { | ||
351 | unset($this->links[$offset]); | ||
352 | } | ||
353 | |||
354 | $this->reorder(); | ||
310 | } | 355 | } |
311 | 356 | ||
312 | /** | 357 | /** |
@@ -314,19 +359,19 @@ You use the community supported version of the original Shaarli project, by Seba | |||
314 | * | 359 | * |
315 | * @throws IOException the datastore is not writable | 360 | * @throws IOException the datastore is not writable |
316 | */ | 361 | */ |
317 | private function writeDB() | 362 | private function write() |
318 | { | 363 | { |
319 | if (is_file($this->_datastore) && !is_writeable($this->_datastore)) { | 364 | if (is_file($this->datastore) && !is_writeable($this->datastore)) { |
320 | // The datastore exists but is not writeable | 365 | // The datastore exists but is not writeable |
321 | throw new IOException($this->_datastore); | 366 | throw new IOException($this->datastore); |
322 | } else if (!is_file($this->_datastore) && !is_writeable(dirname($this->_datastore))) { | 367 | } else if (!is_file($this->datastore) && !is_writeable(dirname($this->datastore))) { |
323 | // The datastore does not exist and its parent directory is not writeable | 368 | // The datastore does not exist and its parent directory is not writeable |
324 | throw new IOException(dirname($this->_datastore)); | 369 | throw new IOException(dirname($this->datastore)); |
325 | } | 370 | } |
326 | 371 | ||
327 | file_put_contents( | 372 | file_put_contents( |
328 | $this->_datastore, | 373 | $this->datastore, |
329 | self::$phpPrefix.base64_encode(gzdeflate(serialize($this->_links))).self::$phpSuffix | 374 | self::$phpPrefix.base64_encode(gzdeflate(serialize($this->links))).self::$phpSuffix |
330 | ); | 375 | ); |
331 | 376 | ||
332 | } | 377 | } |
@@ -336,14 +381,14 @@ You use the community supported version of the original Shaarli project, by Seba | |||
336 | * | 381 | * |
337 | * @param string $pageCacheDir page cache directory | 382 | * @param string $pageCacheDir page cache directory |
338 | */ | 383 | */ |
339 | public function savedb($pageCacheDir) | 384 | public function save($pageCacheDir) |
340 | { | 385 | { |
341 | if (!$this->_loggedIn) { | 386 | if (!$this->loggedIn) { |
342 | // TODO: raise an Exception instead | 387 | // TODO: raise an Exception instead |
343 | die('You are not authorized to change the database.'); | 388 | die('You are not authorized to change the database.'); |
344 | } | 389 | } |
345 | 390 | ||
346 | $this->writeDB(); | 391 | $this->write(); |
347 | 392 | ||
348 | invalidateCaches($pageCacheDir); | 393 | invalidateCaches($pageCacheDir); |
349 | } | 394 | } |
@@ -357,8 +402,8 @@ You use the community supported version of the original Shaarli project, by Seba | |||
357 | */ | 402 | */ |
358 | public function getLinkFromUrl($url) | 403 | public function getLinkFromUrl($url) |
359 | { | 404 | { |
360 | if (isset($this->_urls[$url])) { | 405 | if (isset($this->urls[$url])) { |
361 | return $this->_links[$this->_urls[$url]]; | 406 | return $this->links[$this->urls[$url]]; |
362 | } | 407 | } |
363 | return false; | 408 | return false; |
364 | } | 409 | } |
@@ -375,7 +420,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
375 | public function filterHash($request) | 420 | public function filterHash($request) |
376 | { | 421 | { |
377 | $request = substr($request, 0, 6); | 422 | $request = substr($request, 0, 6); |
378 | $linkFilter = new LinkFilter($this->_links); | 423 | $linkFilter = new LinkFilter($this->links); |
379 | return $linkFilter->filter(LinkFilter::$FILTER_HASH, $request); | 424 | return $linkFilter->filter(LinkFilter::$FILTER_HASH, $request); |
380 | } | 425 | } |
381 | 426 | ||
@@ -387,7 +432,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
387 | * @return array list of shaare found. | 432 | * @return array list of shaare found. |
388 | */ | 433 | */ |
389 | public function filterDay($request) { | 434 | public function filterDay($request) { |
390 | $linkFilter = new LinkFilter($this->_links); | 435 | $linkFilter = new LinkFilter($this->links); |
391 | return $linkFilter->filter(LinkFilter::$FILTER_DAY, $request); | 436 | return $linkFilter->filter(LinkFilter::$FILTER_DAY, $request); |
392 | } | 437 | } |
393 | 438 | ||
@@ -409,7 +454,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
409 | $searchterm = !empty($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : ''; | 454 | $searchterm = !empty($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : ''; |
410 | 455 | ||
411 | // Search tags + fullsearch. | 456 | // Search tags + fullsearch. |
412 | if (empty($type) && ! empty($searchtags) && ! empty($searchterm)) { | 457 | if (! empty($searchtags) && ! empty($searchterm)) { |
413 | $type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT; | 458 | $type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT; |
414 | $request = array($searchtags, $searchterm); | 459 | $request = array($searchtags, $searchterm); |
415 | } | 460 | } |
@@ -429,7 +474,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
429 | $request = ''; | 474 | $request = ''; |
430 | } | 475 | } |
431 | 476 | ||
432 | $linkFilter = new LinkFilter($this->_links); | 477 | $linkFilter = new LinkFilter($this); |
433 | return $linkFilter->filter($type, $request, $casesensitive, $privateonly); | 478 | return $linkFilter->filter($type, $request, $casesensitive, $privateonly); |
434 | } | 479 | } |
435 | 480 | ||
@@ -440,11 +485,18 @@ You use the community supported version of the original Shaarli project, by Seba | |||
440 | public function allTags() | 485 | public function allTags() |
441 | { | 486 | { |
442 | $tags = array(); | 487 | $tags = array(); |
443 | foreach ($this->_links as $link) { | 488 | $caseMapping = array(); |
444 | foreach (explode(' ', $link['tags']) as $tag) { | 489 | foreach ($this->links as $link) { |
445 | if (!empty($tag)) { | 490 | foreach (preg_split('/\s+/', $link['tags'], 0, PREG_SPLIT_NO_EMPTY) as $tag) { |
446 | $tags[$tag] = (empty($tags[$tag]) ? 1 : $tags[$tag] + 1); | 491 | if (empty($tag)) { |
492 | continue; | ||
447 | } | 493 | } |
494 | // The first case found will be displayed. | ||
495 | if (!isset($caseMapping[strtolower($tag)])) { | ||
496 | $caseMapping[strtolower($tag)] = $tag; | ||
497 | $tags[$caseMapping[strtolower($tag)]] = 0; | ||
498 | } | ||
499 | $tags[$caseMapping[strtolower($tag)]]++; | ||
448 | } | 500 | } |
449 | } | 501 | } |
450 | // Sort tags by usage (most used tag first) | 502 | // Sort tags by usage (most used tag first) |
@@ -459,12 +511,64 @@ You use the community supported version of the original Shaarli project, by Seba | |||
459 | public function days() | 511 | public function days() |
460 | { | 512 | { |
461 | $linkDays = array(); | 513 | $linkDays = array(); |
462 | foreach (array_keys($this->_links) as $day) { | 514 | foreach ($this->links as $link) { |
463 | $linkDays[substr($day, 0, 8)] = 0; | 515 | $linkDays[$link['created']->format('Ymd')] = 0; |
464 | } | 516 | } |
465 | $linkDays = array_keys($linkDays); | 517 | $linkDays = array_keys($linkDays); |
466 | sort($linkDays); | 518 | sort($linkDays); |
467 | 519 | ||
468 | return $linkDays; | 520 | return $linkDays; |
469 | } | 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 | } | ||
470 | } | 574 | } |
diff --git a/application/LinkFilter.php b/application/LinkFilter.php index e693b284..daa6d9cc 100644 --- a/application/LinkFilter.php +++ b/application/LinkFilter.php | |||
@@ -28,12 +28,17 @@ class LinkFilter | |||
28 | public static $FILTER_DAY = 'FILTER_DAY'; | 28 | public static $FILTER_DAY = 'FILTER_DAY'; |
29 | 29 | ||
30 | /** | 30 | /** |
31 | * @var array all available links. | 31 | * @var string Allowed characters for hashtags (regex syntax). |
32 | */ | ||
33 | public static $HASHTAG_CHARS = '\p{Pc}\p{N}\p{L}\p{Mn}'; | ||
34 | |||
35 | /** | ||
36 | * @var LinkDB all available links. | ||
32 | */ | 37 | */ |
33 | private $links; | 38 | private $links; |
34 | 39 | ||
35 | /** | 40 | /** |
36 | * @param array $links initialization. | 41 | * @param LinkDB $links initialization. |
37 | */ | 42 | */ |
38 | public function __construct($links) | 43 | public function __construct($links) |
39 | { | 44 | { |
@@ -89,18 +94,16 @@ class LinkFilter | |||
89 | private function noFilter($privateonly = false) | 94 | private function noFilter($privateonly = false) |
90 | { | 95 | { |
91 | if (! $privateonly) { | 96 | if (! $privateonly) { |
92 | krsort($this->links); | ||
93 | return $this->links; | 97 | return $this->links; |
94 | } | 98 | } |
95 | 99 | ||
96 | $out = array(); | 100 | $out = array(); |
97 | foreach ($this->links as $value) { | 101 | foreach ($this->links as $key => $value) { |
98 | if ($value['private']) { | 102 | if ($value['private']) { |
99 | $out[$value['linkdate']] = $value; | 103 | $out[$key] = $value; |
100 | } | 104 | } |
101 | } | 105 | } |
102 | 106 | ||
103 | krsort($out); | ||
104 | return $out; | 107 | return $out; |
105 | } | 108 | } |
106 | 109 | ||
@@ -116,10 +119,10 @@ class LinkFilter | |||
116 | private function filterSmallHash($smallHash) | 119 | private function filterSmallHash($smallHash) |
117 | { | 120 | { |
118 | $filtered = array(); | 121 | $filtered = array(); |
119 | foreach ($this->links as $l) { | 122 | foreach ($this->links as $key => $l) { |
120 | if ($smallHash == smallHash($l['linkdate'])) { | 123 | if ($smallHash == $l['shorturl']) { |
121 | // Yes, this is ugly and slow | 124 | // Yes, this is ugly and slow |
122 | $filtered[$l['linkdate']] = $l; | 125 | $filtered[$key] = $l; |
123 | return $filtered; | 126 | return $filtered; |
124 | } | 127 | } |
125 | } | 128 | } |
@@ -183,7 +186,7 @@ class LinkFilter | |||
183 | $keys = array('title', 'description', 'url', 'tags'); | 186 | $keys = array('title', 'description', 'url', 'tags'); |
184 | 187 | ||
185 | // Iterate over every stored link. | 188 | // Iterate over every stored link. |
186 | foreach ($this->links as $link) { | 189 | foreach ($this->links as $id => $link) { |
187 | 190 | ||
188 | // ignore non private links when 'privatonly' is on. | 191 | // ignore non private links when 'privatonly' is on. |
189 | if (! $link['private'] && $privateonly === true) { | 192 | if (! $link['private'] && $privateonly === true) { |
@@ -217,11 +220,10 @@ class LinkFilter | |||
217 | } | 220 | } |
218 | 221 | ||
219 | if ($found) { | 222 | if ($found) { |
220 | $filtered[$link['linkdate']] = $link; | 223 | $filtered[$id] = $link; |
221 | } | 224 | } |
222 | } | 225 | } |
223 | 226 | ||
224 | krsort($filtered); | ||
225 | return $filtered; | 227 | return $filtered; |
226 | } | 228 | } |
227 | 229 | ||
@@ -251,7 +253,7 @@ class LinkFilter | |||
251 | return $filtered; | 253 | return $filtered; |
252 | } | 254 | } |
253 | 255 | ||
254 | foreach ($this->links as $link) { | 256 | foreach ($this->links as $key => $link) { |
255 | // ignore non private links when 'privatonly' is on. | 257 | // ignore non private links when 'privatonly' is on. |
256 | if (! $link['private'] && $privateonly === true) { | 258 | if (! $link['private'] && $privateonly === true) { |
257 | continue; | 259 | continue; |
@@ -263,18 +265,19 @@ class LinkFilter | |||
263 | for ($i = 0 ; $i < count($searchtags) && $found; $i++) { | 265 | for ($i = 0 ; $i < count($searchtags) && $found; $i++) { |
264 | // Exclusive search, quit if tag found. | 266 | // Exclusive search, quit if tag found. |
265 | // Or, tag not found in the link, quit. | 267 | // Or, tag not found in the link, quit. |
266 | if (($searchtags[$i][0] == '-' && in_array(substr($searchtags[$i], 1), $linktags)) | 268 | if (($searchtags[$i][0] == '-' |
267 | || ($searchtags[$i][0] != '-') && ! in_array($searchtags[$i], $linktags) | 269 | && $this->searchTagAndHashTag(substr($searchtags[$i], 1), $linktags, $link['description'])) |
270 | || ($searchtags[$i][0] != '-') | ||
271 | && ! $this->searchTagAndHashTag($searchtags[$i], $linktags, $link['description']) | ||
268 | ) { | 272 | ) { |
269 | $found = false; | 273 | $found = false; |
270 | } | 274 | } |
271 | } | 275 | } |
272 | 276 | ||
273 | if ($found) { | 277 | if ($found) { |
274 | $filtered[$link['linkdate']] = $link; | 278 | $filtered[$key] = $link; |
275 | } | 279 | } |
276 | } | 280 | } |
277 | krsort($filtered); | ||
278 | return $filtered; | 281 | return $filtered; |
279 | } | 282 | } |
280 | 283 | ||
@@ -297,13 +300,36 @@ class LinkFilter | |||
297 | } | 300 | } |
298 | 301 | ||
299 | $filtered = array(); | 302 | $filtered = array(); |
300 | foreach ($this->links as $l) { | 303 | foreach ($this->links as $key => $l) { |
301 | if (startsWith($l['linkdate'], $day)) { | 304 | if ($l['created']->format('Ymd') == $day) { |
302 | $filtered[$l['linkdate']] = $l; | 305 | $filtered[$key] = $l; |
303 | } | 306 | } |
304 | } | 307 | } |
305 | ksort($filtered); | 308 | |
306 | return $filtered; | 309 | // sort by date ASC |
310 | return array_reverse($filtered, true); | ||
311 | } | ||
312 | |||
313 | /** | ||
314 | * Check if a tag is found in the taglist, or as an hashtag in the link description. | ||
315 | * | ||
316 | * @param string $tag Tag to search. | ||
317 | * @param array $taglist List of tags for the current link. | ||
318 | * @param string $description Link description. | ||
319 | * | ||
320 | * @return bool True if found, false otherwise. | ||
321 | */ | ||
322 | protected function searchTagAndHashTag($tag, $taglist, $description) | ||
323 | { | ||
324 | if (in_array($tag, $taglist)) { | ||
325 | return true; | ||
326 | } | ||
327 | |||
328 | if (preg_match('/(^| )#'. $tag .'([^'. self::$HASHTAG_CHARS .']|$)/mui', $description) > 0) { | ||
329 | return true; | ||
330 | } | ||
331 | |||
332 | return false; | ||
307 | } | 333 | } |
308 | 334 | ||
309 | /** | 335 | /** |
diff --git a/application/LinkUtils.php b/application/LinkUtils.php index da04ca97..cf58f808 100644 --- a/application/LinkUtils.php +++ b/application/LinkUtils.php | |||
@@ -81,7 +81,7 @@ function html_extract_charset($html) | |||
81 | /** | 81 | /** |
82 | * Count private links in given linklist. | 82 | * Count private links in given linklist. |
83 | * | 83 | * |
84 | * @param array $links Linklist. | 84 | * @param array|Countable $links Linklist. |
85 | * | 85 | * |
86 | * @return int Number of private links. | 86 | * @return int Number of private links. |
87 | */ | 87 | */ |
@@ -91,5 +91,94 @@ function count_private($links) | |||
91 | foreach ($links as $link) { | 91 | foreach ($links as $link) { |
92 | $cpt = $link['private'] == true ? $cpt + 1 : $cpt; | 92 | $cpt = $link['private'] == true ? $cpt + 1 : $cpt; |
93 | } | 93 | } |
94 | |||
94 | return $cpt; | 95 | return $cpt; |
95 | } | 96 | } |
97 | |||
98 | /** | ||
99 | * In a string, converts URLs to clickable links. | ||
100 | * | ||
101 | * @param string $text input string. | ||
102 | * @param string $redirector if a redirector is set, use it to gerenate links. | ||
103 | * | ||
104 | * @return string returns $text with all links converted to HTML links. | ||
105 | * | ||
106 | * @see Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 | ||
107 | */ | ||
108 | function text2clickable($text, $redirector = '') | ||
109 | { | ||
110 | $regex = '!(((?:https?|ftp|file)://|apt:|magnet:)\S+[[:alnum:]]/?)!si'; | ||
111 | |||
112 | if (empty($redirector)) { | ||
113 | return preg_replace($regex, '<a href="$1">$1</a>', $text); | ||
114 | } | ||
115 | // Redirector is set, urlencode the final URL. | ||
116 | return preg_replace_callback( | ||
117 | $regex, | ||
118 | function ($matches) use ($redirector) { | ||
119 | return '<a href="' . $redirector . urlencode($matches[1]) .'">'. $matches[1] .'</a>'; | ||
120 | }, | ||
121 | $text | ||
122 | ); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * Auto-link hashtags. | ||
127 | * | ||
128 | * @param string $description Given description. | ||
129 | * @param string $indexUrl Root URL. | ||
130 | * | ||
131 | * @return string Description with auto-linked hashtags. | ||
132 | */ | ||
133 | function hashtag_autolink($description, $indexUrl = '') | ||
134 | { | ||
135 | /* | ||
136 | * To support unicode: http://stackoverflow.com/a/35498078/1484919 | ||
137 | * \p{Pc} - to match underscore | ||
138 | * \p{N} - numeric character in any script | ||
139 | * \p{L} - letter from any language | ||
140 | * \p{Mn} - any non marking space (accents, umlauts, etc) | ||
141 | */ | ||
142 | $regex = '/(^|\s)#([\p{Pc}\p{N}\p{L}\p{Mn}]+)/mui'; | ||
143 | $replacement = '$1<a href="'. $indexUrl .'?addtag=$2" title="Hashtag $2">#$2</a>'; | ||
144 | return preg_replace($regex, $replacement, $description); | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * This function inserts where relevant so that multiple spaces are properly displayed in HTML | ||
149 | * even in the absence of <pre> (This is used in description to keep text formatting). | ||
150 | * | ||
151 | * @param string $text input text. | ||
152 | * | ||
153 | * @return string formatted text. | ||
154 | */ | ||
155 | function space2nbsp($text) | ||
156 | { | ||
157 | return preg_replace('/(^| ) /m', '$1 ', $text); | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * Format Shaarli's description | ||
162 | * | ||
163 | * @param string $description shaare's description. | ||
164 | * @param string $redirector if a redirector is set, use it to gerenate links. | ||
165 | * @param string $indexUrl URL to Shaarli's index. | ||
166 | * | ||
167 | * @return string formatted description. | ||
168 | */ | ||
169 | function format_description($description, $redirector = '', $indexUrl = '') { | ||
170 | return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector), $indexUrl))); | ||
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 fdbb0ad7..e7148d00 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 | ||
@@ -51,4 +51,141 @@ class NetscapeBookmarkUtils | |||
51 | 51 | ||
52 | return $bookmarkLinks; | 52 | return $bookmarkLinks; |
53 | } | 53 | } |
54 | |||
55 | /** | ||
56 | * Generates an import status summary | ||
57 | * | ||
58 | * @param string $filename name of the file to import | ||
59 | * @param int $filesize size of the file to import | ||
60 | * @param int $importCount how many links were imported | ||
61 | * @param int $overwriteCount how many links were overwritten | ||
62 | * @param int $skipCount how many links were skipped | ||
63 | * | ||
64 | * @return string Summary of the bookmark import status | ||
65 | */ | ||
66 | private static function importStatus( | ||
67 | $filename, | ||
68 | $filesize, | ||
69 | $importCount=0, | ||
70 | $overwriteCount=0, | ||
71 | $skipCount=0 | ||
72 | ) | ||
73 | { | ||
74 | $status = 'File '.$filename.' ('.$filesize.' bytes) '; | ||
75 | if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) { | ||
76 | $status .= 'has an unknown file format. Nothing was imported.'; | ||
77 | } else { | ||
78 | $status .= 'was successfully processed: '.$importCount.' links imported, '; | ||
79 | $status .= $overwriteCount.' links overwritten, '; | ||
80 | $status .= $skipCount.' links skipped.'; | ||
81 | } | ||
82 | return $status; | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * Imports Web bookmarks from an uploaded Netscape bookmark dump | ||
87 | * | ||
88 | * @param array $post Server $_POST parameters | ||
89 | * @param array $files Server $_FILES parameters | ||
90 | * @param LinkDB $linkDb Loaded LinkDB instance | ||
91 | * @param string $pagecache Page cache | ||
92 | * | ||
93 | * @return string Summary of the bookmark import status | ||
94 | */ | ||
95 | public static function import($post, $files, $linkDb, $pagecache) | ||
96 | { | ||
97 | $filename = $files['filetoupload']['name']; | ||
98 | $filesize = $files['filetoupload']['size']; | ||
99 | $data = file_get_contents($files['filetoupload']['tmp_name']); | ||
100 | |||
101 | if (strpos($data, '<!DOCTYPE NETSCAPE-Bookmark-file-1>') === false) { | ||
102 | return self::importStatus($filename, $filesize); | ||
103 | } | ||
104 | |||
105 | // Overwrite existing links? | ||
106 | $overwrite = ! empty($post['overwrite']); | ||
107 | |||
108 | // Add tags to all imported links? | ||
109 | if (empty($post['default_tags'])) { | ||
110 | $defaultTags = array(); | ||
111 | } else { | ||
112 | $defaultTags = preg_split( | ||
113 | '/[\s,]+/', | ||
114 | escape($post['default_tags']) | ||
115 | ); | ||
116 | } | ||
117 | |||
118 | // links are imported as public by default | ||
119 | $defaultPrivacy = 0; | ||
120 | |||
121 | $parser = new NetscapeBookmarkParser( | ||
122 | true, // nested tag support | ||
123 | $defaultTags, // additional user-specified tags | ||
124 | strval(1 - $defaultPrivacy) // defaultPub = 1 - defaultPrivacy | ||
125 | ); | ||
126 | $bookmarks = $parser->parseString($data); | ||
127 | |||
128 | $importCount = 0; | ||
129 | $overwriteCount = 0; | ||
130 | $skipCount = 0; | ||
131 | |||
132 | foreach ($bookmarks as $bkm) { | ||
133 | $private = $defaultPrivacy; | ||
134 | if (empty($post['privacy']) || $post['privacy'] == 'default') { | ||
135 | // use value from the imported file | ||
136 | $private = $bkm['pub'] == '1' ? 0 : 1; | ||
137 | } else if ($post['privacy'] == 'private') { | ||
138 | // all imported links are private | ||
139 | $private = 1; | ||
140 | } else if ($post['privacy'] == 'public') { | ||
141 | // all imported links are public | ||
142 | $private = 0; | ||
143 | } | ||
144 | |||
145 | $newLink = array( | ||
146 | 'title' => $bkm['title'], | ||
147 | 'url' => $bkm['uri'], | ||
148 | 'description' => $bkm['note'], | ||
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 | } | ||
54 | } | 191 | } |
diff --git a/application/PageBuilder.php b/application/PageBuilder.php index 82580787..32c7f9f1 100644 --- a/application/PageBuilder.php +++ b/application/PageBuilder.php | |||
@@ -15,12 +15,20 @@ class PageBuilder | |||
15 | private $tpl; | 15 | private $tpl; |
16 | 16 | ||
17 | /** | 17 | /** |
18 | * @var ConfigManager $conf Configuration Manager instance. | ||
19 | */ | ||
20 | protected $conf; | ||
21 | |||
22 | /** | ||
18 | * PageBuilder constructor. | 23 | * PageBuilder constructor. |
19 | * $tpl is initialized at false for lazy loading. | 24 | * $tpl is initialized at false for lazy loading. |
25 | * | ||
26 | * @param ConfigManager $conf Configuration Manager instance (reference). | ||
20 | */ | 27 | */ |
21 | function __construct() | 28 | function __construct(&$conf) |
22 | { | 29 | { |
23 | $this->tpl = false; | 30 | $this->tpl = false; |
31 | $this->conf = $conf; | ||
24 | } | 32 | } |
25 | 33 | ||
26 | /** | 34 | /** |
@@ -33,17 +41,17 @@ class PageBuilder | |||
33 | try { | 41 | try { |
34 | $version = ApplicationUtils::checkUpdate( | 42 | $version = ApplicationUtils::checkUpdate( |
35 | shaarli_version, | 43 | shaarli_version, |
36 | $GLOBALS['config']['UPDATECHECK_FILENAME'], | 44 | $this->conf->get('resource.update_check'), |
37 | $GLOBALS['config']['UPDATECHECK_INTERVAL'], | 45 | $this->conf->get('updates.check_updates_interval'), |
38 | $GLOBALS['config']['ENABLE_UPDATECHECK'], | 46 | $this->conf->get('updates.check_updates'), |
39 | isLoggedIn(), | 47 | isLoggedIn(), |
40 | $GLOBALS['config']['UPDATECHECK_BRANCH'] | 48 | $this->conf->get('updates.check_updates_branch') |
41 | ); | 49 | ); |
42 | $this->tpl->assign('newVersion', escape($version)); | 50 | $this->tpl->assign('newVersion', escape($version)); |
43 | $this->tpl->assign('versionError', ''); | 51 | $this->tpl->assign('versionError', ''); |
44 | 52 | ||
45 | } catch (Exception $exc) { | 53 | } catch (Exception $exc) { |
46 | logm($GLOBALS['config']['LOG_FILE'], $_SERVER['REMOTE_ADDR'], $exc->getMessage()); | 54 | logm($this->conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], $exc->getMessage()); |
47 | $this->tpl->assign('newVersion', ''); | 55 | $this->tpl->assign('newVersion', ''); |
48 | $this->tpl->assign('versionError', escape($exc->getMessage())); | 56 | $this->tpl->assign('versionError', escape($exc->getMessage())); |
49 | } | 57 | } |
@@ -60,21 +68,18 @@ class PageBuilder | |||
60 | $this->tpl->assign('source', index_url($_SERVER)); | 68 | $this->tpl->assign('source', index_url($_SERVER)); |
61 | $this->tpl->assign('version', shaarli_version); | 69 | $this->tpl->assign('version', shaarli_version); |
62 | $this->tpl->assign('scripturl', index_url($_SERVER)); | 70 | $this->tpl->assign('scripturl', index_url($_SERVER)); |
63 | $this->tpl->assign('pagetitle', 'Shaarli'); | ||
64 | $this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links? | 71 | $this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links? |
65 | if (!empty($GLOBALS['title'])) { | 72 | $this->tpl->assign('pagetitle', $this->conf->get('general.title', 'Shaarli')); |
66 | $this->tpl->assign('pagetitle', $GLOBALS['title']); | 73 | if ($this->conf->exists('general.header_link')) { |
67 | } | 74 | $this->tpl->assign('titleLink', $this->conf->get('general.header_link')); |
68 | if (!empty($GLOBALS['titleLink'])) { | ||
69 | $this->tpl->assign('titleLink', $GLOBALS['titleLink']); | ||
70 | } | ||
71 | if (!empty($GLOBALS['pagetitle'])) { | ||
72 | $this->tpl->assign('pagetitle', $GLOBALS['pagetitle']); | ||
73 | } | ||
74 | $this->tpl->assign('shaarlititle', empty($GLOBALS['title']) ? 'Shaarli': $GLOBALS['title']); | ||
75 | if (!empty($GLOBALS['plugin_errors'])) { | ||
76 | $this->tpl->assign('plugin_errors', $GLOBALS['plugin_errors']); | ||
77 | } | 75 | } |
76 | $this->tpl->assign('shaarlititle', $this->conf->get('general.title', 'Shaarli')); | ||
77 | $this->tpl->assign('openshaarli', $this->conf->get('security.open_shaarli', false)); | ||
78 | $this->tpl->assign('showatom', $this->conf->get('feed.show_atom', false)); | ||
79 | $this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false)); | ||
80 | $this->tpl->assign('token', getToken($this->conf)); | ||
81 | // To be removed with a proper theme configuration. | ||
82 | $this->tpl->assign('conf', $this->conf); | ||
78 | } | 83 | } |
79 | 84 | ||
80 | /** | 85 | /** |
@@ -85,7 +90,6 @@ class PageBuilder | |||
85 | */ | 90 | */ |
86 | public function assign($placeholder, $value) | 91 | public function assign($placeholder, $value) |
87 | { | 92 | { |
88 | // Lazy initialization | ||
89 | if ($this->tpl === false) { | 93 | if ($this->tpl === false) { |
90 | $this->initialize(); | 94 | $this->initialize(); |
91 | } | 95 | } |
@@ -101,7 +105,6 @@ class PageBuilder | |||
101 | */ | 105 | */ |
102 | public function assignAll($data) | 106 | public function assignAll($data) |
103 | { | 107 | { |
104 | // Lazy initialization | ||
105 | if ($this->tpl === false) { | 108 | if ($this->tpl === false) { |
106 | $this->initialize(); | 109 | $this->initialize(); |
107 | } | 110 | } |
@@ -113,6 +116,7 @@ class PageBuilder | |||
113 | foreach ($data as $key => $value) { | 116 | foreach ($data as $key => $value) { |
114 | $this->assign($key, $value); | 117 | $this->assign($key, $value); |
115 | } | 118 | } |
119 | return true; | ||
116 | } | 120 | } |
117 | 121 | ||
118 | /** | 122 | /** |
@@ -123,10 +127,10 @@ class PageBuilder | |||
123 | */ | 127 | */ |
124 | public function renderPage($page) | 128 | public function renderPage($page) |
125 | { | 129 | { |
126 | // Lazy initialization | 130 | if ($this->tpl === false) { |
127 | if ($this->tpl===false) { | ||
128 | $this->initialize(); | 131 | $this->initialize(); |
129 | } | 132 | } |
133 | |||
130 | $this->tpl->draw($page); | 134 | $this->tpl->draw($page); |
131 | } | 135 | } |
132 | 136 | ||
diff --git a/application/PluginManager.php b/application/PluginManager.php index 787ac6a9..59ece4fa 100644 --- a/application/PluginManager.php +++ b/application/PluginManager.php | |||
@@ -4,18 +4,10 @@ | |||
4 | * Class PluginManager | 4 | * Class PluginManager |
5 | * | 5 | * |
6 | * Use to manage, load and execute plugins. | 6 | * Use to manage, load and execute plugins. |
7 | * | ||
8 | * Using Singleton design pattern. | ||
9 | */ | 7 | */ |
10 | class PluginManager | 8 | class PluginManager |
11 | { | 9 | { |
12 | /** | 10 | /** |
13 | * PluginManager singleton instance. | ||
14 | * @var PluginManager $instance | ||
15 | */ | ||
16 | private static $instance; | ||
17 | |||
18 | /** | ||
19 | * List of authorized plugins from configuration file. | 11 | * List of authorized plugins from configuration file. |
20 | * @var array $authorizedPlugins | 12 | * @var array $authorizedPlugins |
21 | */ | 13 | */ |
@@ -28,45 +20,36 @@ class PluginManager | |||
28 | private $loadedPlugins = array(); | 20 | private $loadedPlugins = array(); |
29 | 21 | ||
30 | /** | 22 | /** |
31 | * Plugins subdirectory. | 23 | * @var ConfigManager Configuration Manager instance. |
32 | * @var string $PLUGINS_PATH | ||
33 | */ | 24 | */ |
34 | public static $PLUGINS_PATH = 'plugins'; | 25 | protected $conf; |
35 | 26 | ||
36 | /** | 27 | /** |
37 | * Plugins meta files extension. | 28 | * @var array List of plugin errors. |
38 | * @var string $META_EXT | ||
39 | */ | 29 | */ |
40 | public static $META_EXT = 'meta'; | 30 | protected $errors; |
41 | 31 | ||
42 | /** | 32 | /** |
43 | * Private constructor: new instances not allowed. | 33 | * Plugins subdirectory. |
34 | * @var string $PLUGINS_PATH | ||
44 | */ | 35 | */ |
45 | private function __construct() | 36 | public static $PLUGINS_PATH = 'plugins'; |
46 | { | ||
47 | } | ||
48 | 37 | ||
49 | /** | 38 | /** |
50 | * Cloning isn't allowed either. | 39 | * Plugins meta files extension. |
51 | * | 40 | * @var string $META_EXT |
52 | * @return void | ||
53 | */ | 41 | */ |
54 | private function __clone() | 42 | public static $META_EXT = 'meta'; |
55 | { | ||
56 | } | ||
57 | 43 | ||
58 | /** | 44 | /** |
59 | * Return existing instance of PluginManager, or create it. | 45 | * Constructor. |
60 | * | 46 | * |
61 | * @return PluginManager instance. | 47 | * @param ConfigManager $conf Configuration Manager instance. |
62 | */ | 48 | */ |
63 | public static function getInstance() | 49 | public function __construct(&$conf) |
64 | { | 50 | { |
65 | if (!(self::$instance instanceof self)) { | 51 | $this->conf = $conf; |
66 | self::$instance = new self(); | 52 | $this->errors = array(); |
67 | } | ||
68 | |||
69 | return self::$instance; | ||
70 | } | 53 | } |
71 | 54 | ||
72 | /** | 55 | /** |
@@ -102,9 +85,9 @@ class PluginManager | |||
102 | /** | 85 | /** |
103 | * Execute all plugins registered hook. | 86 | * Execute all plugins registered hook. |
104 | * | 87 | * |
105 | * @param string $hook name of the hook to trigger. | 88 | * @param string $hook name of the hook to trigger. |
106 | * @param array $data list of data to manipulate passed by reference. | 89 | * @param array $data list of data to manipulate passed by reference. |
107 | * @param array $params additional parameters such as page target. | 90 | * @param array $params additional parameters such as page target. |
108 | * | 91 | * |
109 | * @return void | 92 | * @return void |
110 | */ | 93 | */ |
@@ -122,13 +105,14 @@ class PluginManager | |||
122 | $hookFunction = $this->buildHookName($hook, $plugin); | 105 | $hookFunction = $this->buildHookName($hook, $plugin); |
123 | 106 | ||
124 | if (function_exists($hookFunction)) { | 107 | if (function_exists($hookFunction)) { |
125 | $data = call_user_func($hookFunction, $data); | 108 | $data = call_user_func($hookFunction, $data, $this->conf); |
126 | } | 109 | } |
127 | } | 110 | } |
128 | } | 111 | } |
129 | 112 | ||
130 | /** | 113 | /** |
131 | * Load a single plugin from its files. | 114 | * Load a single plugin from its files. |
115 | * Call the init function if it exists, and collect errors. | ||
132 | * Add them in $loadedPlugins if successful. | 116 | * Add them in $loadedPlugins if successful. |
133 | * | 117 | * |
134 | * @param string $dir plugin's directory. | 118 | * @param string $dir plugin's directory. |
@@ -148,8 +132,17 @@ class PluginManager | |||
148 | throw new PluginFileNotFoundException($pluginName); | 132 | throw new PluginFileNotFoundException($pluginName); |
149 | } | 133 | } |
150 | 134 | ||
135 | $conf = $this->conf; | ||
151 | include_once $pluginFilePath; | 136 | include_once $pluginFilePath; |
152 | 137 | ||
138 | $initFunction = $pluginName . '_init'; | ||
139 | if (function_exists($initFunction)) { | ||
140 | $errors = call_user_func($initFunction, $this->conf); | ||
141 | if (!empty($errors)) { | ||
142 | $this->errors = array_merge($this->errors, $errors); | ||
143 | } | ||
144 | } | ||
145 | |||
153 | $this->loadedPlugins[] = $pluginName; | 146 | $this->loadedPlugins[] = $pluginName; |
154 | } | 147 | } |
155 | 148 | ||
@@ -207,12 +200,26 @@ class PluginManager | |||
207 | continue; | 200 | continue; |
208 | } | 201 | } |
209 | 202 | ||
210 | $metaData[$plugin]['parameters'][$param] = ''; | 203 | $metaData[$plugin]['parameters'][$param]['value'] = ''; |
204 | // Optional parameter description in parameter.PARAM_NAME= | ||
205 | if (isset($metaData[$plugin]['parameter.'. $param])) { | ||
206 | $metaData[$plugin]['parameters'][$param]['desc'] = $metaData[$plugin]['parameter.'. $param]; | ||
207 | } | ||
211 | } | 208 | } |
212 | } | 209 | } |
213 | 210 | ||
214 | return $metaData; | 211 | return $metaData; |
215 | } | 212 | } |
213 | |||
214 | /** | ||
215 | * Return the list of encountered errors. | ||
216 | * | ||
217 | * @return array List of errors (empty array if none exists). | ||
218 | */ | ||
219 | public function getErrors() | ||
220 | { | ||
221 | return $this->errors; | ||
222 | } | ||
216 | } | 223 | } |
217 | 224 | ||
218 | /** | 225 | /** |
@@ -232,4 +239,4 @@ class PluginFileNotFoundException extends Exception | |||
232 | { | 239 | { |
233 | $this->message = 'Plugin "'. $pluginName .'" files not found.'; | 240 | $this->message = 'Plugin "'. $pluginName .'" files not found.'; |
234 | } | 241 | } |
235 | } \ No newline at end of file | 242 | } |
diff --git a/application/Router.php b/application/Router.php index 2c3934b0..caed4a28 100644 --- a/application/Router.php +++ b/application/Router.php | |||
@@ -138,4 +138,4 @@ class Router | |||
138 | 138 | ||
139 | return self::$PAGE_LINKLIST; | 139 | return self::$PAGE_LINKLIST; |
140 | } | 140 | } |
141 | } \ No newline at end of file | 141 | } |
diff --git a/application/TimeZone.php b/application/TimeZone.php index 26f2232d..36a8fb12 100644 --- a/application/TimeZone.php +++ b/application/TimeZone.php | |||
@@ -7,9 +7,9 @@ | |||
7 | * Example: preselect Europe/Paris | 7 | * Example: preselect Europe/Paris |
8 | * list($htmlform, $js) = generateTimeZoneForm('Europe/Paris'); | 8 | * list($htmlform, $js) = generateTimeZoneForm('Europe/Paris'); |
9 | * | 9 | * |
10 | * @param string $preselected_timezone preselected timezone (optional) | 10 | * @param string $preselectedTimezone preselected timezone (optional) |
11 | * | 11 | * |
12 | * @return an array containing the generated HTML form and Javascript code | 12 | * @return array containing the generated HTML form and Javascript code |
13 | **/ | 13 | **/ |
14 | function generateTimeZoneForm($preselectedTimezone='') | 14 | function generateTimeZoneForm($preselectedTimezone='') |
15 | { | 15 | { |
@@ -27,10 +27,6 @@ function generateTimeZoneForm($preselectedTimezone='') | |||
27 | $pcity = substr($preselectedTimezone, $spos+1); | 27 | $pcity = substr($preselectedTimezone, $spos+1); |
28 | } | 28 | } |
29 | 29 | ||
30 | // Display config form: | ||
31 | $timezoneForm = ''; | ||
32 | $timezoneJs = ''; | ||
33 | |||
34 | // The list is in the form 'Europe/Paris', 'America/Argentina/Buenos_Aires' | 30 | // The list is in the form 'Europe/Paris', 'America/Argentina/Buenos_Aires' |
35 | // We split the list in continents/cities. | 31 | // We split the list in continents/cities. |
36 | $continents = array(); | 32 | $continents = array(); |
@@ -97,7 +93,7 @@ function generateTimeZoneForm($preselectedTimezone='') | |||
97 | * @param string $continent the timezone continent | 93 | * @param string $continent the timezone continent |
98 | * @param string $city the timezone city | 94 | * @param string $city the timezone city |
99 | * | 95 | * |
100 | * @return whether continent/city is a valid timezone | 96 | * @return bool whether continent/city is a valid timezone |
101 | */ | 97 | */ |
102 | function isTimeZoneValid($continent, $city) | 98 | function isTimeZoneValid($continent, $city) |
103 | { | 99 | { |
diff --git a/application/Updater.php b/application/Updater.php index 58c13c07..555d4c25 100644 --- a/application/Updater.php +++ b/application/Updater.php | |||
@@ -13,14 +13,14 @@ class Updater | |||
13 | protected $doneUpdates; | 13 | protected $doneUpdates; |
14 | 14 | ||
15 | /** | 15 | /** |
16 | * @var array Shaarli's configuration array. | 16 | * @var LinkDB instance. |
17 | */ | 17 | */ |
18 | protected $config; | 18 | protected $linkDB; |
19 | 19 | ||
20 | /** | 20 | /** |
21 | * @var LinkDB instance. | 21 | * @var ConfigManager $conf Configuration Manager instance. |
22 | */ | 22 | */ |
23 | protected $linkDB; | 23 | protected $conf; |
24 | 24 | ||
25 | /** | 25 | /** |
26 | * @var bool True if the user is logged in, false otherwise. | 26 | * @var bool True if the user is logged in, false otherwise. |
@@ -35,16 +35,16 @@ class Updater | |||
35 | /** | 35 | /** |
36 | * Object constructor. | 36 | * Object constructor. |
37 | * | 37 | * |
38 | * @param array $doneUpdates Updates which are already done. | 38 | * @param array $doneUpdates Updates which are already done. |
39 | * @param array $config Shaarli's configuration array. | 39 | * @param LinkDB $linkDB LinkDB instance. |
40 | * @param LinkDB $linkDB LinkDB instance. | 40 | * @param ConfigManager $conf Configuration Manager instance. |
41 | * @param boolean $isLoggedIn True if the user is logged in. | 41 | * @param boolean $isLoggedIn True if the user is logged in. |
42 | */ | 42 | */ |
43 | public function __construct($doneUpdates, $config, $linkDB, $isLoggedIn) | 43 | public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn) |
44 | { | 44 | { |
45 | $this->doneUpdates = $doneUpdates; | 45 | $this->doneUpdates = $doneUpdates; |
46 | $this->config = $config; | ||
47 | $this->linkDB = $linkDB; | 46 | $this->linkDB = $linkDB; |
47 | $this->conf = $conf; | ||
48 | $this->isLoggedIn = $isLoggedIn; | 48 | $this->isLoggedIn = $isLoggedIn; |
49 | 49 | ||
50 | // Retrieve all update methods. | 50 | // Retrieve all update methods. |
@@ -114,19 +114,19 @@ class Updater | |||
114 | */ | 114 | */ |
115 | public function updateMethodMergeDeprecatedConfigFile() | 115 | public function updateMethodMergeDeprecatedConfigFile() |
116 | { | 116 | { |
117 | $config_file = $this->config['config']['CONFIG_FILE']; | 117 | if (is_file($this->conf->get('resource.data_dir') . '/options.php')) { |
118 | 118 | include $this->conf->get('resource.data_dir') . '/options.php'; | |
119 | if (is_file($this->config['config']['DATADIR'].'/options.php')) { | ||
120 | include $this->config['config']['DATADIR'].'/options.php'; | ||
121 | 119 | ||
122 | // Load GLOBALS into config | 120 | // Load GLOBALS into config |
121 | $allowedKeys = array_merge(ConfigPhp::$ROOT_KEYS); | ||
122 | $allowedKeys[] = 'config'; | ||
123 | foreach ($GLOBALS as $key => $value) { | 123 | foreach ($GLOBALS as $key => $value) { |
124 | $this->config[$key] = $value; | 124 | if (in_array($key, $allowedKeys)) { |
125 | $this->conf->set($key, $value); | ||
126 | } | ||
125 | } | 127 | } |
126 | $this->config['config']['CONFIG_FILE'] = $config_file; | 128 | $this->conf->write($this->isLoggedIn); |
127 | writeConfig($this->config, $this->isLoggedIn); | 129 | unlink($this->conf->get('resource.data_dir').'/options.php'); |
128 | |||
129 | unlink($this->config['config']['DATADIR'].'/options.php'); | ||
130 | } | 130 | } |
131 | 131 | ||
132 | return true; | 132 | return true; |
@@ -138,12 +138,144 @@ 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->savedb($this->config['config']['PAGECACHE']); | 146 | $this->linkDB->save($this->conf->get('resource.page_cache')); |
147 | return true; | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * Move old configuration in PHP to the new config system in JSON format. | ||
152 | * | ||
153 | * Will rename 'config.php' into 'config.save.php' and create 'config.json.php'. | ||
154 | * It will also convert legacy setting keys to the new ones. | ||
155 | */ | ||
156 | public function updateMethodConfigToJson() | ||
157 | { | ||
158 | // JSON config already exists, nothing to do. | ||
159 | if ($this->conf->getConfigIO() instanceof ConfigJson) { | ||
160 | return true; | ||
161 | } | ||
162 | |||
163 | $configPhp = new ConfigPhp(); | ||
164 | $configJson = new ConfigJson(); | ||
165 | $oldConfig = $configPhp->read($this->conf->getConfigFile() . '.php'); | ||
166 | rename($this->conf->getConfigFileExt(), $this->conf->getConfigFile() . '.save.php'); | ||
167 | $this->conf->setConfigIO($configJson); | ||
168 | $this->conf->reload(); | ||
169 | |||
170 | $legacyMap = array_flip(ConfigPhp::$LEGACY_KEYS_MAPPING); | ||
171 | foreach (ConfigPhp::$ROOT_KEYS as $key) { | ||
172 | $this->conf->set($legacyMap[$key], $oldConfig[$key]); | ||
173 | } | ||
174 | |||
175 | // Set sub config keys (config and plugins) | ||
176 | $subConfig = array('config', 'plugins'); | ||
177 | foreach ($subConfig as $sub) { | ||
178 | foreach ($oldConfig[$sub] as $key => $value) { | ||
179 | if (isset($legacyMap[$sub .'.'. $key])) { | ||
180 | $configKey = $legacyMap[$sub .'.'. $key]; | ||
181 | } else { | ||
182 | $configKey = $sub .'.'. $key; | ||
183 | } | ||
184 | $this->conf->set($configKey, $value); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | try{ | ||
189 | $this->conf->write($this->isLoggedIn); | ||
190 | return true; | ||
191 | } catch (IOException $e) { | ||
192 | error_log($e->getMessage()); | ||
193 | return false; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * Escape settings which have been manually escaped in every request in previous versions: | ||
199 | * - general.title | ||
200 | * - general.header_link | ||
201 | * - redirector.url | ||
202 | * | ||
203 | * @return bool true if the update is successful, false otherwise. | ||
204 | */ | ||
205 | public function updateMethodEscapeUnescapedConfig() | ||
206 | { | ||
207 | try { | ||
208 | $this->conf->set('general.title', escape($this->conf->get('general.title'))); | ||
209 | $this->conf->set('general.header_link', escape($this->conf->get('general.header_link'))); | ||
210 | $this->conf->set('redirector.url', escape($this->conf->get('redirector.url'))); | ||
211 | $this->conf->write($this->isLoggedIn); | ||
212 | } catch (Exception $e) { | ||
213 | error_log($e->getMessage()); | ||
214 | return false; | ||
215 | } | ||
216 | return true; | ||
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 | } | ||
259 | |||
260 | /** | ||
261 | * * `markdown_escape` is a new setting, set to true as default. | ||
262 | * | ||
263 | * If the markdown plugin was already enabled, escaping is disabled to avoid | ||
264 | * breaking existing entries. | ||
265 | */ | ||
266 | public function updateMethodEscapeMarkdown() | ||
267 | { | ||
268 | if ($this->conf->exists('security.markdown_escape')) { | ||
269 | return true; | ||
270 | } | ||
271 | |||
272 | if (in_array('markdown', $this->conf->get('general.enabled_plugins'))) { | ||
273 | $this->conf->set('security.markdown_escape', false); | ||
274 | } else { | ||
275 | $this->conf->set('security.markdown_escape', true); | ||
276 | } | ||
277 | $this->conf->write($this->isLoggedIn); | ||
278 | |||
147 | return true; | 279 | return true; |
148 | } | 280 | } |
149 | } | 281 | } |
@@ -203,7 +335,6 @@ class UpdaterException extends Exception | |||
203 | } | 335 | } |
204 | } | 336 | } |
205 | 337 | ||
206 | |||
207 | /** | 338 | /** |
208 | * Read the updates file, and return already done updates. | 339 | * Read the updates file, and return already done updates. |
209 | * | 340 | * |
diff --git a/application/Url.php b/application/Url.php index 77447c8d..c5c7dd18 100644 --- a/application/Url.php +++ b/application/Url.php | |||
@@ -62,21 +62,7 @@ function add_trailing_slash($url) | |||
62 | { | 62 | { |
63 | return $url . (!endsWith($url, '/') ? '/' : ''); | 63 | return $url . (!endsWith($url, '/') ? '/' : ''); |
64 | } | 64 | } |
65 | /** | ||
66 | * Converts an URL with an IDN host to a ASCII one. | ||
67 | * | ||
68 | * @param string $url Input URL. | ||
69 | * | ||
70 | * @return string converted URL. | ||
71 | */ | ||
72 | function url_with_idn_to_ascii($url) | ||
73 | { | ||
74 | $parts = parse_url($url); | ||
75 | $parts['host'] = idn_to_ascii($parts['host']); | ||
76 | 65 | ||
77 | $httpUrl = new \http\Url($parts); | ||
78 | return $httpUrl->toString(); | ||
79 | } | ||
80 | /** | 66 | /** |
81 | * URL representation and cleanup utilities | 67 | * URL representation and cleanup utilities |
82 | * | 68 | * |
@@ -99,6 +85,7 @@ class Url | |||
99 | 'action_type_map=', | 85 | 'action_type_map=', |
100 | 'fb_', | 86 | 'fb_', |
101 | 'fb=', | 87 | 'fb=', |
88 | 'PHPSESSID=', | ||
102 | 89 | ||
103 | // Scoop.it | 90 | // Scoop.it |
104 | '__scoop', | 91 | '__scoop', |
diff --git a/application/Utils.php b/application/Utils.php index da521cce..0a5b476e 100644 --- a/application/Utils.php +++ b/application/Utils.php | |||
@@ -31,7 +31,15 @@ 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. | ||
39 | * | ||
40 | * @param string $text Create a hash from this text. | ||
41 | * | ||
42 | * @return string generated small hash. | ||
35 | */ | 43 | */ |
36 | function smallHash($text) | 44 | function smallHash($text) |
37 | { | 45 | { |
@@ -106,7 +114,9 @@ function unescape($str) | |||
106 | } | 114 | } |
107 | 115 | ||
108 | /** | 116 | /** |
109 | * Link sanitization before templating | 117 | * Sanitize link before rendering. |
118 | * | ||
119 | * @param array $link Link to escape. | ||
110 | */ | 120 | */ |
111 | function sanitizeLink(&$link) | 121 | function sanitizeLink(&$link) |
112 | { | 122 | { |
@@ -198,59 +208,6 @@ function is_session_id_valid($sessionId) | |||
198 | } | 208 | } |
199 | 209 | ||
200 | /** | 210 | /** |
201 | * In a string, converts URLs to clickable links. | ||
202 | * | ||
203 | * @param string $text input string. | ||
204 | * @param string $redirector if a redirector is set, use it to gerenate links. | ||
205 | * | ||
206 | * @return string returns $text with all links converted to HTML links. | ||
207 | * | ||
208 | * @see Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 | ||
209 | */ | ||
210 | function text2clickable($text, $redirector) | ||
211 | { | ||
212 | $regex = '!(((?:https?|ftp|file)://|apt:|magnet:)\S+[[:alnum:]]/?)!si'; | ||
213 | |||
214 | if (empty($redirector)) { | ||
215 | return preg_replace($regex, '<a href="$1">$1</a>', $text); | ||
216 | } | ||
217 | // Redirector is set, urlencode the final URL. | ||
218 | return preg_replace_callback( | ||
219 | $regex, | ||
220 | function ($matches) use ($redirector) { | ||
221 | return '<a href="' . $redirector . urlencode($matches[1]) .'">'. $matches[1] .'</a>'; | ||
222 | }, | ||
223 | $text | ||
224 | ); | ||
225 | } | ||
226 | |||
227 | /** | ||
228 | * This function inserts where relevant so that multiple spaces are properly displayed in HTML | ||
229 | * even in the absence of <pre> (This is used in description to keep text formatting). | ||
230 | * | ||
231 | * @param string $text input text. | ||
232 | * | ||
233 | * @return string formatted text. | ||
234 | */ | ||
235 | function space2nbsp($text) | ||
236 | { | ||
237 | return preg_replace('/(^| ) /m', '$1 ', $text); | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * Format Shaarli's description | ||
242 | * TODO: Move me to ApplicationUtils when it's ready. | ||
243 | * | ||
244 | * @param string $description shaare's description. | ||
245 | * @param string $redirector if a redirector is set, use it to gerenate links. | ||
246 | * | ||
247 | * @return string formatted description. | ||
248 | */ | ||
249 | function format_description($description, $redirector = false) { | ||
250 | return nl2br(space2nbsp(text2clickable($description, $redirector))); | ||
251 | } | ||
252 | |||
253 | /** | ||
254 | * Sniff browser language to set the locale automatically. | 211 | * Sniff browser language to set the locale automatically. |
255 | * Note that is may not work on your server if the corresponding locale is not installed. | 212 | * Note that is may not work on your server if the corresponding locale is not installed. |
256 | * | 213 | * |
@@ -273,4 +230,4 @@ function autoLocale($headerLocale) | |||
273 | } | 230 | } |
274 | } | 231 | } |
275 | setlocale(LC_ALL, $attempts); | 232 | setlocale(LC_ALL, $attempts); |
276 | } \ No newline at end of file | 233 | } |
diff --git a/application/config/ConfigIO.php b/application/config/ConfigIO.php new file mode 100644 index 00000000..2b68fe6a --- /dev/null +++ b/application/config/ConfigIO.php | |||
@@ -0,0 +1,33 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Interface ConfigIO | ||
5 | * | ||
6 | * This describes how Config types should store their configuration. | ||
7 | */ | ||
8 | interface ConfigIO | ||
9 | { | ||
10 | /** | ||
11 | * Read configuration. | ||
12 | * | ||
13 | * @param string $filepath Config file absolute path. | ||
14 | * | ||
15 | * @return array All configuration in an array. | ||
16 | */ | ||
17 | function read($filepath); | ||
18 | |||
19 | /** | ||
20 | * Write configuration. | ||
21 | * | ||
22 | * @param string $filepath Config file absolute path. | ||
23 | * @param array $conf All configuration in an array. | ||
24 | */ | ||
25 | function write($filepath, $conf); | ||
26 | |||
27 | /** | ||
28 | * Get config file extension according to config type. | ||
29 | * | ||
30 | * @return string Config file extension. | ||
31 | */ | ||
32 | function getExtension(); | ||
33 | } | ||
diff --git a/application/config/ConfigJson.php b/application/config/ConfigJson.php new file mode 100644 index 00000000..30007eb4 --- /dev/null +++ b/application/config/ConfigJson.php | |||
@@ -0,0 +1,78 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Class ConfigJson (ConfigIO implementation) | ||
5 | * | ||
6 | * Handle Shaarli's JSON configuration file. | ||
7 | */ | ||
8 | class ConfigJson implements ConfigIO | ||
9 | { | ||
10 | /** | ||
11 | * @inheritdoc | ||
12 | */ | ||
13 | function read($filepath) | ||
14 | { | ||
15 | if (! is_readable($filepath)) { | ||
16 | return array(); | ||
17 | } | ||
18 | $data = file_get_contents($filepath); | ||
19 | $data = str_replace(self::getPhpHeaders(), '', $data); | ||
20 | $data = str_replace(self::getPhpSuffix(), '', $data); | ||
21 | $data = json_decode($data, true); | ||
22 | if ($data === null) { | ||
23 | $error = json_last_error(); | ||
24 | throw new Exception('An error occurred while parsing JSON file: error code #'. $error); | ||
25 | } | ||
26 | return $data; | ||
27 | } | ||
28 | |||
29 | /** | ||
30 | * @inheritdoc | ||
31 | */ | ||
32 | function write($filepath, $conf) | ||
33 | { | ||
34 | // JSON_PRETTY_PRINT is available from PHP 5.4. | ||
35 | $print = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; | ||
36 | $data = self::getPhpHeaders() . json_encode($conf, $print) . self::getPhpSuffix(); | ||
37 | if (!file_put_contents($filepath, $data)) { | ||
38 | throw new IOException( | ||
39 | $filepath, | ||
40 | 'Shaarli could not create the config file. | ||
41 | Please make sure Shaarli has the right to write in the folder is it installed in.' | ||
42 | ); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * @inheritdoc | ||
48 | */ | ||
49 | function getExtension() | ||
50 | { | ||
51 | return '.json.php'; | ||
52 | } | ||
53 | |||
54 | /** | ||
55 | * The JSON data is wrapped in a PHP file for security purpose. | ||
56 | * This way, even if the file is accessible, credentials and configuration won't be exposed. | ||
57 | * | ||
58 | * Note: this isn't a static field because concatenation isn't supported in field declaration before PHP 5.6. | ||
59 | * | ||
60 | * @return string PHP start tag and comment tag. | ||
61 | */ | ||
62 | public static function getPhpHeaders() | ||
63 | { | ||
64 | return '<?php /*'. PHP_EOL; | ||
65 | } | ||
66 | |||
67 | /** | ||
68 | * Get PHP comment closing tags. | ||
69 | * | ||
70 | * Static method for consistency with getPhpHeaders. | ||
71 | * | ||
72 | * @return string PHP comment closing. | ||
73 | */ | ||
74 | public static function getPhpSuffix() | ||
75 | { | ||
76 | return PHP_EOL . '*/ ?>'; | ||
77 | } | ||
78 | } | ||
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php new file mode 100644 index 00000000..f5f753f8 --- /dev/null +++ b/application/config/ConfigManager.php | |||
@@ -0,0 +1,394 @@ | |||
1 | <?php | ||
2 | |||
3 | // FIXME! Namespaces... | ||
4 | require_once 'ConfigIO.php'; | ||
5 | require_once 'ConfigJson.php'; | ||
6 | require_once 'ConfigPhp.php'; | ||
7 | |||
8 | /** | ||
9 | * Class ConfigManager | ||
10 | * | ||
11 | * Manages all Shaarli's settings. | ||
12 | * See the documentation for more information on settings: | ||
13 | * - doc/Shaarli-configuration.html | ||
14 | * - https://github.com/shaarli/Shaarli/wiki/Shaarli-configuration | ||
15 | */ | ||
16 | class ConfigManager | ||
17 | { | ||
18 | /** | ||
19 | * @var string Flag telling a setting is not found. | ||
20 | */ | ||
21 | protected static $NOT_FOUND = 'NOT_FOUND'; | ||
22 | |||
23 | /** | ||
24 | * @var string Config folder. | ||
25 | */ | ||
26 | protected $configFile; | ||
27 | |||
28 | /** | ||
29 | * @var array Loaded config array. | ||
30 | */ | ||
31 | protected $loadedConfig; | ||
32 | |||
33 | /** | ||
34 | * @var ConfigIO implementation instance. | ||
35 | */ | ||
36 | protected $configIO; | ||
37 | |||
38 | /** | ||
39 | * Constructor. | ||
40 | * | ||
41 | * @param string $configFile Configuration file path without extension. | ||
42 | */ | ||
43 | public function __construct($configFile = 'data/config') | ||
44 | { | ||
45 | $this->configFile = $configFile; | ||
46 | $this->initialize(); | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * Reset the ConfigManager instance. | ||
51 | */ | ||
52 | public function reset() | ||
53 | { | ||
54 | $this->initialize(); | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Rebuild the loaded config array from config files. | ||
59 | */ | ||
60 | public function reload() | ||
61 | { | ||
62 | $this->load(); | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Initialize the ConfigIO and loaded the conf. | ||
67 | */ | ||
68 | protected function initialize() | ||
69 | { | ||
70 | if (file_exists($this->configFile . '.php')) { | ||
71 | $this->configIO = new ConfigPhp(); | ||
72 | } else { | ||
73 | $this->configIO = new ConfigJson(); | ||
74 | } | ||
75 | $this->load(); | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * Load configuration in the ConfigurationManager. | ||
80 | */ | ||
81 | protected function load() | ||
82 | { | ||
83 | $this->loadedConfig = $this->configIO->read($this->getConfigFileExt()); | ||
84 | $this->setDefaultValues(); | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * Get a setting. | ||
89 | * | ||
90 | * Supports nested settings with dot separated keys. | ||
91 | * Eg. 'config.stuff.option' will find $conf[config][stuff][option], | ||
92 | * or in JSON: | ||
93 | * { "config": { "stuff": {"option": "mysetting" } } } } | ||
94 | * | ||
95 | * @param string $setting Asked setting, keys separated with dots. | ||
96 | * @param string $default Default value if not found. | ||
97 | * | ||
98 | * @return mixed Found setting, or the default value. | ||
99 | */ | ||
100 | public function get($setting, $default = '') | ||
101 | { | ||
102 | // During the ConfigIO transition, map legacy settings to the new ones. | ||
103 | if ($this->configIO instanceof ConfigPhp && isset(ConfigPhp::$LEGACY_KEYS_MAPPING[$setting])) { | ||
104 | $setting = ConfigPhp::$LEGACY_KEYS_MAPPING[$setting]; | ||
105 | } | ||
106 | |||
107 | $settings = explode('.', $setting); | ||
108 | $value = self::getConfig($settings, $this->loadedConfig); | ||
109 | if ($value === self::$NOT_FOUND) { | ||
110 | return $default; | ||
111 | } | ||
112 | return $value; | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * Set a setting, and eventually write it. | ||
117 | * | ||
118 | * Supports nested settings with dot separated keys. | ||
119 | * | ||
120 | * @param string $setting Asked setting, keys separated with dots. | ||
121 | * @param string $value Value to set. | ||
122 | * @param bool $write Write the new setting in the config file, default false. | ||
123 | * @param bool $isLoggedIn User login state, default false. | ||
124 | * | ||
125 | * @throws Exception Invalid | ||
126 | */ | ||
127 | public function set($setting, $value, $write = false, $isLoggedIn = false) | ||
128 | { | ||
129 | if (empty($setting) || ! is_string($setting)) { | ||
130 | throw new Exception('Invalid setting key parameter. String expected, got: '. gettype($setting)); | ||
131 | } | ||
132 | |||
133 | // During the ConfigIO transition, map legacy settings to the new ones. | ||
134 | if ($this->configIO instanceof ConfigPhp && isset(ConfigPhp::$LEGACY_KEYS_MAPPING[$setting])) { | ||
135 | $setting = ConfigPhp::$LEGACY_KEYS_MAPPING[$setting]; | ||
136 | } | ||
137 | |||
138 | $settings = explode('.', $setting); | ||
139 | self::setConfig($settings, $value, $this->loadedConfig); | ||
140 | if ($write) { | ||
141 | $this->write($isLoggedIn); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * Check if a settings exists. | ||
147 | * | ||
148 | * Supports nested settings with dot separated keys. | ||
149 | * | ||
150 | * @param string $setting Asked setting, keys separated with dots. | ||
151 | * | ||
152 | * @return bool true if the setting exists, false otherwise. | ||
153 | */ | ||
154 | public function exists($setting) | ||
155 | { | ||
156 | // During the ConfigIO transition, map legacy settings to the new ones. | ||
157 | if ($this->configIO instanceof ConfigPhp && isset(ConfigPhp::$LEGACY_KEYS_MAPPING[$setting])) { | ||
158 | $setting = ConfigPhp::$LEGACY_KEYS_MAPPING[$setting]; | ||
159 | } | ||
160 | |||
161 | $settings = explode('.', $setting); | ||
162 | $value = self::getConfig($settings, $this->loadedConfig); | ||
163 | if ($value === self::$NOT_FOUND) { | ||
164 | return false; | ||
165 | } | ||
166 | return true; | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * Call the config writer. | ||
171 | * | ||
172 | * @param bool $isLoggedIn User login state. | ||
173 | * | ||
174 | * @return bool True if the configuration has been successfully written, false otherwise. | ||
175 | * | ||
176 | * @throws MissingFieldConfigException: a mandatory field has not been provided in $conf. | ||
177 | * @throws UnauthorizedConfigException: user is not authorize to change configuration. | ||
178 | * @throws IOException: an error occurred while writing the new config file. | ||
179 | */ | ||
180 | public function write($isLoggedIn) | ||
181 | { | ||
182 | // These fields are required in configuration. | ||
183 | $mandatoryFields = array( | ||
184 | 'credentials.login', | ||
185 | 'credentials.hash', | ||
186 | 'credentials.salt', | ||
187 | 'security.session_protection_disabled', | ||
188 | 'general.timezone', | ||
189 | 'general.title', | ||
190 | 'general.header_link', | ||
191 | 'privacy.default_private_links', | ||
192 | 'redirector.url', | ||
193 | ); | ||
194 | |||
195 | // Only logged in user can alter config. | ||
196 | if (is_file($this->getConfigFileExt()) && !$isLoggedIn) { | ||
197 | throw new UnauthorizedConfigException(); | ||
198 | } | ||
199 | |||
200 | // Check that all mandatory fields are provided in $conf. | ||
201 | foreach ($mandatoryFields as $field) { | ||
202 | if (! $this->exists($field)) { | ||
203 | throw new MissingFieldConfigException($field); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | return $this->configIO->write($this->getConfigFileExt(), $this->loadedConfig); | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * Set the config file path (without extension). | ||
212 | * | ||
213 | * @param string $configFile File path. | ||
214 | */ | ||
215 | public function setConfigFile($configFile) | ||
216 | { | ||
217 | $this->configFile = $configFile; | ||
218 | } | ||
219 | |||
220 | /** | ||
221 | * Return the configuration file path (without extension). | ||
222 | * | ||
223 | * @return string Config path. | ||
224 | */ | ||
225 | public function getConfigFile() | ||
226 | { | ||
227 | return $this->configFile; | ||
228 | } | ||
229 | |||
230 | /** | ||
231 | * Get the configuration file path with its extension. | ||
232 | * | ||
233 | * @return string Config file path. | ||
234 | */ | ||
235 | public function getConfigFileExt() | ||
236 | { | ||
237 | return $this->configFile . $this->configIO->getExtension(); | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * Recursive function which find asked setting in the loaded config. | ||
242 | * | ||
243 | * @param array $settings Ordered array which contains keys to find. | ||
244 | * @param array $conf Loaded settings, then sub-array. | ||
245 | * | ||
246 | * @return mixed Found setting or NOT_FOUND flag. | ||
247 | */ | ||
248 | protected static function getConfig($settings, $conf) | ||
249 | { | ||
250 | if (!is_array($settings) || count($settings) == 0) { | ||
251 | return self::$NOT_FOUND; | ||
252 | } | ||
253 | |||
254 | $setting = array_shift($settings); | ||
255 | if (!isset($conf[$setting])) { | ||
256 | return self::$NOT_FOUND; | ||
257 | } | ||
258 | |||
259 | if (count($settings) > 0) { | ||
260 | return self::getConfig($settings, $conf[$setting]); | ||
261 | } | ||
262 | return $conf[$setting]; | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * Recursive function which find asked setting in the loaded config. | ||
267 | * | ||
268 | * @param array $settings Ordered array which contains keys to find. | ||
269 | * @param mixed $value | ||
270 | * @param array $conf Loaded settings, then sub-array. | ||
271 | * | ||
272 | * @return mixed Found setting or NOT_FOUND flag. | ||
273 | */ | ||
274 | protected static function setConfig($settings, $value, &$conf) | ||
275 | { | ||
276 | if (!is_array($settings) || count($settings) == 0) { | ||
277 | return self::$NOT_FOUND; | ||
278 | } | ||
279 | |||
280 | $setting = array_shift($settings); | ||
281 | if (count($settings) > 0) { | ||
282 | return self::setConfig($settings, $value, $conf[$setting]); | ||
283 | } | ||
284 | $conf[$setting] = $value; | ||
285 | } | ||
286 | |||
287 | /** | ||
288 | * Set a bunch of default values allowing Shaarli to start without a config file. | ||
289 | */ | ||
290 | protected function setDefaultValues() | ||
291 | { | ||
292 | $this->setEmpty('resource.data_dir', 'data'); | ||
293 | $this->setEmpty('resource.config', 'data/config.php'); | ||
294 | $this->setEmpty('resource.datastore', 'data/datastore.php'); | ||
295 | $this->setEmpty('resource.ban_file', 'data/ipbans.php'); | ||
296 | $this->setEmpty('resource.updates', 'data/updates.txt'); | ||
297 | $this->setEmpty('resource.log', 'data/log.txt'); | ||
298 | $this->setEmpty('resource.update_check', 'data/lastupdatecheck.txt'); | ||
299 | $this->setEmpty('resource.raintpl_tpl', 'tpl/'); | ||
300 | $this->setEmpty('resource.raintpl_tmp', 'tmp/'); | ||
301 | $this->setEmpty('resource.thumbnails_cache', 'cache'); | ||
302 | $this->setEmpty('resource.page_cache', 'pagecache'); | ||
303 | |||
304 | $this->setEmpty('security.ban_after', 4); | ||
305 | $this->setEmpty('security.ban_duration', 1800); | ||
306 | $this->setEmpty('security.session_protection_disabled', false); | ||
307 | $this->setEmpty('security.open_shaarli', false); | ||
308 | |||
309 | $this->setEmpty('general.header_link', '?'); | ||
310 | $this->setEmpty('general.links_per_page', 20); | ||
311 | $this->setEmpty('general.enabled_plugins', array('qrcode')); | ||
312 | |||
313 | $this->setEmpty('updates.check_updates', false); | ||
314 | $this->setEmpty('updates.check_updates_branch', 'stable'); | ||
315 | $this->setEmpty('updates.check_updates_interval', 86400); | ||
316 | |||
317 | $this->setEmpty('feed.rss_permalinks', true); | ||
318 | $this->setEmpty('feed.show_atom', false); | ||
319 | |||
320 | $this->setEmpty('privacy.default_private_links', false); | ||
321 | $this->setEmpty('privacy.hide_public_links', false); | ||
322 | $this->setEmpty('privacy.hide_timestamps', false); | ||
323 | |||
324 | $this->setEmpty('thumbnail.enable_thumbnails', true); | ||
325 | $this->setEmpty('thumbnail.enable_localcache', true); | ||
326 | |||
327 | $this->setEmpty('redirector.url', ''); | ||
328 | $this->setEmpty('redirector.encode_url', true); | ||
329 | |||
330 | $this->setEmpty('plugins', array()); | ||
331 | } | ||
332 | |||
333 | /** | ||
334 | * Set only if the setting does not exists. | ||
335 | * | ||
336 | * @param string $key Setting key. | ||
337 | * @param mixed $value Setting value. | ||
338 | */ | ||
339 | public function setEmpty($key, $value) | ||
340 | { | ||
341 | if (! $this->exists($key)) { | ||
342 | $this->set($key, $value); | ||
343 | } | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * @return ConfigIO | ||
348 | */ | ||
349 | public function getConfigIO() | ||
350 | { | ||
351 | return $this->configIO; | ||
352 | } | ||
353 | |||
354 | /** | ||
355 | * @param ConfigIO $configIO | ||
356 | */ | ||
357 | public function setConfigIO($configIO) | ||
358 | { | ||
359 | $this->configIO = $configIO; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /** | ||
364 | * Exception used if a mandatory field is missing in given configuration. | ||
365 | */ | ||
366 | class MissingFieldConfigException extends Exception | ||
367 | { | ||
368 | public $field; | ||
369 | |||
370 | /** | ||
371 | * Construct exception. | ||
372 | * | ||
373 | * @param string $field field name missing. | ||
374 | */ | ||
375 | public function __construct($field) | ||
376 | { | ||
377 | $this->field = $field; | ||
378 | $this->message = 'Configuration value is required for '. $this->field; | ||
379 | } | ||
380 | } | ||
381 | |||
382 | /** | ||
383 | * Exception used if an unauthorized attempt to edit configuration has been made. | ||
384 | */ | ||
385 | class UnauthorizedConfigException extends Exception | ||
386 | { | ||
387 | /** | ||
388 | * Construct exception. | ||
389 | */ | ||
390 | public function __construct() | ||
391 | { | ||
392 | $this->message = 'You are not authorized to alter config.'; | ||
393 | } | ||
394 | } | ||
diff --git a/application/config/ConfigPhp.php b/application/config/ConfigPhp.php new file mode 100644 index 00000000..27187b66 --- /dev/null +++ b/application/config/ConfigPhp.php | |||
@@ -0,0 +1,132 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Class ConfigPhp (ConfigIO implementation) | ||
5 | * | ||
6 | * Handle Shaarli's legacy PHP configuration file. | ||
7 | * Note: this is only designed to support the transition to JSON configuration. | ||
8 | */ | ||
9 | class ConfigPhp implements ConfigIO | ||
10 | { | ||
11 | /** | ||
12 | * @var array List of config key without group. | ||
13 | */ | ||
14 | public static $ROOT_KEYS = array( | ||
15 | 'login', | ||
16 | 'hash', | ||
17 | 'salt', | ||
18 | 'timezone', | ||
19 | 'title', | ||
20 | 'titleLink', | ||
21 | 'redirector', | ||
22 | 'disablesessionprotection', | ||
23 | 'privateLinkByDefault', | ||
24 | ); | ||
25 | |||
26 | /** | ||
27 | * Map legacy config keys with the new ones. | ||
28 | * If ConfigPhp is used, getting <newkey> will actually look for <legacykey>. | ||
29 | * The Updater will use this array to transform keys when switching to JSON. | ||
30 | * | ||
31 | * @var array current key => legacy key. | ||
32 | */ | ||
33 | public static $LEGACY_KEYS_MAPPING = array( | ||
34 | 'credentials.login' => 'login', | ||
35 | 'credentials.hash' => 'hash', | ||
36 | 'credentials.salt' => 'salt', | ||
37 | 'resource.data_dir' => 'config.DATADIR', | ||
38 | 'resource.config' => 'config.CONFIG_FILE', | ||
39 | 'resource.datastore' => 'config.DATASTORE', | ||
40 | 'resource.updates' => 'config.UPDATES_FILE', | ||
41 | 'resource.log' => 'config.LOG_FILE', | ||
42 | 'resource.update_check' => 'config.UPDATECHECK_FILENAME', | ||
43 | 'resource.raintpl_tpl' => 'config.RAINTPL_TPL', | ||
44 | 'resource.raintpl_tmp' => 'config.RAINTPL_TMP', | ||
45 | 'resource.thumbnails_cache' => 'config.CACHEDIR', | ||
46 | 'resource.page_cache' => 'config.PAGECACHE', | ||
47 | 'resource.ban_file' => 'config.IPBANS_FILENAME', | ||
48 | 'security.session_protection_disabled' => 'disablesessionprotection', | ||
49 | 'security.ban_after' => 'config.BAN_AFTER', | ||
50 | 'security.ban_duration' => 'config.BAN_DURATION', | ||
51 | 'general.title' => 'title', | ||
52 | 'general.timezone' => 'timezone', | ||
53 | 'general.header_link' => 'titleLink', | ||
54 | 'updates.check_updates' => 'config.ENABLE_UPDATECHECK', | ||
55 | 'updates.check_updates_branch' => 'config.UPDATECHECK_BRANCH', | ||
56 | 'updates.check_updates_interval' => 'config.UPDATECHECK_INTERVAL', | ||
57 | 'privacy.default_private_links' => 'privateLinkByDefault', | ||
58 | 'feed.rss_permalinks' => 'config.ENABLE_RSS_PERMALINKS', | ||
59 | 'general.links_per_page' => 'config.LINKS_PER_PAGE', | ||
60 | 'thumbnail.enable_thumbnails' => 'config.ENABLE_THUMBNAILS', | ||
61 | 'thumbnail.enable_localcache' => 'config.ENABLE_LOCALCACHE', | ||
62 | 'general.enabled_plugins' => 'config.ENABLED_PLUGINS', | ||
63 | 'redirector.url' => 'redirector', | ||
64 | 'redirector.encode_url' => 'config.REDIRECTOR_URLENCODE', | ||
65 | 'feed.show_atom' => 'config.SHOW_ATOM', | ||
66 | 'privacy.hide_public_links' => 'config.HIDE_PUBLIC_LINKS', | ||
67 | 'privacy.hide_timestamps' => 'config.HIDE_TIMESTAMPS', | ||
68 | 'security.open_shaarli' => 'config.OPEN_SHAARLI', | ||
69 | ); | ||
70 | |||
71 | /** | ||
72 | * @inheritdoc | ||
73 | */ | ||
74 | function read($filepath) | ||
75 | { | ||
76 | if (! file_exists($filepath) || ! is_readable($filepath)) { | ||
77 | return array(); | ||
78 | } | ||
79 | |||
80 | include $filepath; | ||
81 | |||
82 | $out = array(); | ||
83 | foreach (self::$ROOT_KEYS as $key) { | ||
84 | $out[$key] = $GLOBALS[$key]; | ||
85 | } | ||
86 | $out['config'] = $GLOBALS['config']; | ||
87 | $out['plugins'] = !empty($GLOBALS['plugins']) ? $GLOBALS['plugins'] : array(); | ||
88 | return $out; | ||
89 | } | ||
90 | |||
91 | /** | ||
92 | * @inheritdoc | ||
93 | */ | ||
94 | function write($filepath, $conf) | ||
95 | { | ||
96 | $configStr = '<?php '. PHP_EOL; | ||
97 | foreach (self::$ROOT_KEYS as $key) { | ||
98 | if (isset($conf[$key])) { | ||
99 | $configStr .= '$GLOBALS[\'' . $key . '\'] = ' . var_export($conf[$key], true) . ';' . PHP_EOL; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | // Store all $conf['config'] | ||
104 | foreach ($conf['config'] as $key => $value) { | ||
105 | $configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($conf['config'][$key], true).';'. PHP_EOL; | ||
106 | } | ||
107 | |||
108 | if (isset($conf['plugins'])) { | ||
109 | foreach ($conf['plugins'] as $key => $value) { | ||
110 | $configStr .= '$GLOBALS[\'plugins\'][\''. $key .'\'] = '.var_export($conf['plugins'][$key], true).';'. PHP_EOL; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | if (!file_put_contents($filepath, $configStr) | ||
115 | || strcmp(file_get_contents($filepath), $configStr) != 0 | ||
116 | ) { | ||
117 | throw new IOException( | ||
118 | $filepath, | ||
119 | 'Shaarli could not create the config file. | ||
120 | Please make sure Shaarli has the right to write in the folder is it installed in.' | ||
121 | ); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * @inheritdoc | ||
127 | */ | ||
128 | function getExtension() | ||
129 | { | ||
130 | return '.php'; | ||
131 | } | ||
132 | } | ||
diff --git a/application/config/ConfigPlugin.php b/application/config/ConfigPlugin.php new file mode 100644 index 00000000..cb0b6fce --- /dev/null +++ b/application/config/ConfigPlugin.php | |||
@@ -0,0 +1,124 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * Plugin configuration helper functions. | ||
4 | * | ||
5 | * Note: no access to configuration files here. | ||
6 | */ | ||
7 | |||
8 | /** | ||
9 | * Process plugin administration form data and save it in an array. | ||
10 | * | ||
11 | * @param array $formData Data sent by the plugin admin form. | ||
12 | * | ||
13 | * @return array New list of enabled plugin, ordered. | ||
14 | * | ||
15 | * @throws PluginConfigOrderException Plugins can't be sorted because their order is invalid. | ||
16 | */ | ||
17 | function save_plugin_config($formData) | ||
18 | { | ||
19 | // Make sure there are no duplicates in orders. | ||
20 | if (!validate_plugin_order($formData)) { | ||
21 | throw new PluginConfigOrderException(); | ||
22 | } | ||
23 | |||
24 | $plugins = array(); | ||
25 | $newEnabledPlugins = array(); | ||
26 | foreach ($formData as $key => $data) { | ||
27 | if (startsWith($key, 'order')) { | ||
28 | continue; | ||
29 | } | ||
30 | |||
31 | // If there is no order, it means a disabled plugin has been enabled. | ||
32 | if (isset($formData['order_' . $key])) { | ||
33 | $plugins[(int) $formData['order_' . $key]] = $key; | ||
34 | } | ||
35 | else { | ||
36 | $newEnabledPlugins[] = $key; | ||
37 | } | ||
38 | } | ||
39 | |||
40 | // New enabled plugins will be added at the end of order. | ||
41 | $plugins = array_merge($plugins, $newEnabledPlugins); | ||
42 | |||
43 | // Sort plugins by order. | ||
44 | if (!ksort($plugins)) { | ||
45 | throw new PluginConfigOrderException(); | ||
46 | } | ||
47 | |||
48 | $finalPlugins = array(); | ||
49 | // Make plugins order continuous. | ||
50 | foreach ($plugins as $plugin) { | ||
51 | $finalPlugins[] = $plugin; | ||
52 | } | ||
53 | |||
54 | return $finalPlugins; | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Validate plugin array submitted. | ||
59 | * Will fail if there is duplicate orders value. | ||
60 | * | ||
61 | * @param array $formData Data from submitted form. | ||
62 | * | ||
63 | * @return bool true if ok, false otherwise. | ||
64 | */ | ||
65 | function validate_plugin_order($formData) | ||
66 | { | ||
67 | $orders = array(); | ||
68 | foreach ($formData as $key => $value) { | ||
69 | // No duplicate order allowed. | ||
70 | if (in_array($value, $orders)) { | ||
71 | return false; | ||
72 | } | ||
73 | |||
74 | if (startsWith($key, 'order')) { | ||
75 | $orders[] = $value; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | return true; | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * Affect plugin parameters values from the ConfigManager into plugins array. | ||
84 | * | ||
85 | * @param mixed $plugins Plugins array: | ||
86 | * $plugins[<plugin_name>]['parameters'][<param_name>] = [ | ||
87 | * 'value' => <value>, | ||
88 | * 'desc' => <description> | ||
89 | * ] | ||
90 | * @param mixed $conf Plugins configuration. | ||
91 | * | ||
92 | * @return mixed Updated $plugins array. | ||
93 | */ | ||
94 | function load_plugin_parameter_values($plugins, $conf) | ||
95 | { | ||
96 | $out = $plugins; | ||
97 | foreach ($plugins as $name => $plugin) { | ||
98 | if (empty($plugin['parameters'])) { | ||
99 | continue; | ||
100 | } | ||
101 | |||
102 | foreach ($plugin['parameters'] as $key => $param) { | ||
103 | if (!empty($conf[$key])) { | ||
104 | $out[$name]['parameters'][$key]['value'] = $conf[$key]; | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | return $out; | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * Exception used if an error occur while saving plugin configuration. | ||
114 | */ | ||
115 | class PluginConfigOrderException extends Exception | ||
116 | { | ||
117 | /** | ||
118 | * Construct exception. | ||
119 | */ | ||
120 | public function __construct() | ||
121 | { | ||
122 | $this->message = 'An error occurred while trying to save plugins loading order.'; | ||
123 | } | ||
124 | } | ||
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/composer.json b/composer.json index 2ded0920..f7d26a31 100644 --- a/composer.json +++ b/composer.json | |||
@@ -1,11 +1,19 @@ | |||
1 | { | 1 | { |
2 | "name": "shaarli/shaarli", | 2 | "name": "shaarli/shaarli", |
3 | "description": "The personal, minimalist, super-fast, no-database delicious clone", | 3 | "description": "The personal, minimalist, super-fast, database-free bookmarking service", |
4 | "type": "project", | ||
4 | "license": "MIT", | 5 | "license": "MIT", |
6 | "homepage": "https://github.com/shaarli/Shaarli", | ||
5 | "support": { | 7 | "support": { |
6 | "issues": "https://github.com/shaarli/Shaarli/issues" | 8 | "issues": "https://github.com/shaarli/Shaarli/issues", |
9 | "wiki": "https://github.com/shaarli/Shaarli/wiki" | ||
10 | }, | ||
11 | "keywords": ["bookmark", "link", "share", "web"], | ||
12 | "require": { | ||
13 | "php": ">=5.3.4", | ||
14 | "shaarli/netscape-bookmark-parser": "1.*", | ||
15 | "erusev/parsedown": "1.6" | ||
7 | }, | 16 | }, |
8 | "require": {}, | ||
9 | "require-dev": { | 17 | "require-dev": { |
10 | "phpmd/phpmd" : "@stable", | 18 | "phpmd/phpmd" : "@stable", |
11 | "phpunit/phpunit": "4.8.*", | 19 | "phpunit/phpunit": "4.8.*", |
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/3rd-party-libraries.html b/doc/3rd-party-libraries.html index f6ff4763..946ca037 100644 --- a/doc/3rd-party-libraries.html +++ b/doc/3rd-party-libraries.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
@@ -81,6 +79,7 @@ | |||
81 | </ul> | 79 | </ul> |
82 | <h2 id="php">PHP</h2> | 80 | <h2 id="php">PHP</h2> |
83 | <ul> | 81 | <ul> |
82 | <li><a href="https://github.com/shaarli/netscape-bookmark-parser">shaarli/netscape-bookmark-parser</a> - Netscape bookmark parser<a href=".html"></a></li> | ||
84 | <li><a href="https://github.com/rainphp/raintpl">RainTPL</a> - HTML templating for PHP<a href=".html"></a></li> | 83 | <li><a href="https://github.com/rainphp/raintpl">RainTPL</a> - HTML templating for PHP<a href=".html"></a></li> |
85 | </ul> | 84 | </ul> |
86 | </body> | 85 | </body> |
diff --git a/doc/3rd-party-libraries.md b/doc/3rd-party-libraries.md index 3101c90a..e6370549 100644 --- a/doc/3rd-party-libraries.md +++ b/doc/3rd-party-libraries.md | |||
@@ -10,4 +10,5 @@ | |||
10 | - [qr.js](http://neocotic.com/qr.js/) ([GitHub](https://github.com/neocotic/qr.js)) - QR code generation[](.html) | 10 | - [qr.js](http://neocotic.com/qr.js/) ([GitHub](https://github.com/neocotic/qr.js)) - QR code generation[](.html) |
11 | 11 | ||
12 | ## PHP | 12 | ## PHP |
13 | - [shaarli/netscape-bookmark-parser](https://github.com/shaarli/netscape-bookmark-parser) - Netscape bookmark parser[](.html) | ||
13 | - [RainTPL](https://github.com/rainphp/raintpl) - HTML templating for PHP[](.html) | 14 | - [RainTPL](https://github.com/rainphp/raintpl) - HTML templating for PHP[](.html) |
diff --git a/doc/Backup,-restore,-import-and-export.html b/doc/Backup,-restore,-import-and-export.html index 4d72728e..a4a48ad7 100644 --- a/doc/Backup,-restore,-import-and-export.html +++ b/doc/Backup,-restore,-import-and-export.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,15 +96,21 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
104 | <h1 id="backup-restore-import-and-export">Backup, restore, import and export</h1> | 102 | <h1 id="backup-restore-import-and-export">Backup, restore, import and export</h1> |
103 | <ul> | ||
104 | <li><a href="#backup-and-restore-the-datastore-file">Backup and restore the datastore file</a><a href=".html"></a></li> | ||
105 | <li><a href="#export-links-as">Export links as...</a><a href=".html"></a></li> | ||
106 | <li><a href="#import-links-from">Import links from...</a><a href=".html"></a></li> | ||
107 | <li><a href="#import-shaarli-links-to-firefox">Import Shaarli links to Firefox</a><a href=".html"></a></li> | ||
108 | </ul> | ||
109 | <hr /> | ||
105 | <h2 id="backup-and-restore-the-datastore-file">Backup and restore the datastore file</h2> | 110 | <h2 id="backup-and-restore-the-datastore-file">Backup and restore the datastore file</h2> |
106 | <p>Backup the file <code>data/datastore.php</code> (by FTP or SSH). Restore by putting the file back in place.</p> | 111 | <p>Backup the file <code>data/datastore.php</code> (by FTP or SSH). Restore by putting the file back in place.</p> |
107 | <p>Example command:</p> | 112 | <p>Example command:</p> |
108 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="kw">rsync</span> -avzP my.server.com:/var/www/shaarli/data/datastore.php datastore-<span class="ot">$(</span><span class="kw">date</span> +%Y-%m-%d_%H%M<span class="ot">)</span>.php</code></pre></div> | 113 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="fu">rsync</span> -avzP my.server.com:/var/www/shaarli/data/datastore.php datastore-<span class="va">$(</span><span class="fu">date</span> +%Y-%m-%d_%H%M<span class="va">)</span>.php</code></pre></div> |
109 | <h2 id="export-links-as...">Export links as...</h2> | 114 | <h2 id="export-links-as...">Export links as...</h2> |
110 | <p>To export links as an HTML file, under <em>Tools > Export</em>, choose:</p> | 115 | <p>To export links as an HTML file, under <em>Tools > Export</em>, choose:</p> |
111 | <ul> | 116 | <ul> |
@@ -118,7 +123,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
118 | <li>This can be done using the <a href="https://github.com/nodiscc/shaarchiver">shaarchiver</a> tool.<a href=".html"></a></li> | 123 | <li>This can be done using the <a href="https://github.com/nodiscc/shaarchiver">shaarchiver</a> tool.<a href=".html"></a></li> |
119 | </ul> | 124 | </ul> |
120 | <p>Example command:</p> | 125 | <p>Example command:</p> |
121 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="kw">./export-bookmarks.py</span> --url=https://my.server.com/shaarli --username=myusername --password=mysupersecretpassword --download-dir=./ --type=all</code></pre></div> | 126 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">./export-bookmarks.py</span> --url=https://my.server.com/shaarli --username=myusername --password=mysupersecretpassword --download-dir=./ --type=all</code></pre></div> |
122 | <h2 id="import-links-from...">Import links from...</h2> | 127 | <h2 id="import-links-from...">Import links from...</h2> |
123 | <h3 id="diigo">Diigo</h3> | 128 | <h3 id="diigo">Diigo</h3> |
124 | <p>If you export your bookmark from Diigo, make sure you use the Delicious export, not the Netscape export. (Their Netscape export is broken, and they don't seem to be interested in fixing it.)</p> | 129 | <p>If you export your bookmark from Diigo, make sure you use the Delicious export, not the Netscape export. (Their Netscape export is broken, and they don't seem to be interested in fixing it.)</p> |
@@ -126,5 +131,20 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
126 | <p>See <a href="https://github.com/sebsauvage/Shaarli/issues/146">this issue</a> for import tweaks.<a href=".html"></a></p> | 131 | <p>See <a href="https://github.com/sebsauvage/Shaarli/issues/146">this issue</a> for import tweaks.<a href=".html"></a></p> |
127 | <h3 id="semanticscuttle">SemanticScuttle</h3> | 132 | <h3 id="semanticscuttle">SemanticScuttle</h3> |
128 | <p>To correctly import the tags from a <a href="http://semanticscuttle.sourceforge.net/">SemanticScuttle</a> HTML export, edit the HTML file before importing and replace all occurences of <code>tags=</code> (lowercase) to <code>TAGS=</code> (uppercase).<a href=".html"></a></p> | 133 | <p>To correctly import the tags from a <a href="http://semanticscuttle.sourceforge.net/">SemanticScuttle</a> HTML export, edit the HTML file before importing and replace all occurences of <code>tags=</code> (lowercase) to <code>TAGS=</code> (uppercase).<a href=".html"></a></p> |
134 | <h3 id="scuttle">Scuttle</h3> | ||
135 | <p>Shaarli cannot import data directly from <a href="https://github.com/scronide/scuttle">Scuttle</a>. However, you can use this third party tool: <a href="https://github.com/q2apro/scuttle-to-shaarli" class="uri">https://github.com/q2apro/scuttle-to-shaarli</a> to export the Scuttle database to the Netscape HTML format compatible with the Shaarli importer.<a href=".html"></a></p> | ||
136 | <h2 id="import-shaarli-links-to-firefox">Import Shaarli links to Firefox</h2> | ||
137 | <ul> | ||
138 | <li>Export your Shaarli links as described above.</li> | ||
139 | <li>For compatibility reasons, check <code>Prepend note permalinks with this Shaarli instance's URL (useful to import bookmarks in a web browser)</code></li> | ||
140 | <li>In Firefox, open the bookmark manager (not the sidebar! <code>Bookmarks menu > Show all bookmarks</code> or <code>Ctrl+Shift+B</code>)</li> | ||
141 | <li>Select <code>Import and Backup > Import bookmarks in HTML format</code></li> | ||
142 | </ul> | ||
143 | <p>Your bookmarks will be imported in Firefox, ready to use, with tags and descriptions retained. "Self" (notes) shaares will still point to the Shaarli instance you exported them from, but the note text can be viewed directly in the bookmark properties inside your browser. Depending on the number of bookmarks, the import can take some time.</p> | ||
144 | <p>You may be interested in these Firefox addons to manage links imported from Shaarli</p> | ||
145 | <ul> | ||
146 | <li><a href="https://addons.mozilla.org/en-US/firefox/addon/bookmark-deduplicator/">Bookmark Deduplicator</a> - provides an easy way to deduplicate your bookmarks<a href=".html"></a></li> | ||
147 | <li><a href="https://addons.mozilla.org/en-US/firefox/addon/tagsieve/">TagSieve</a> - browse your bookmarks by their tags<a href=".html"></a></li> | ||
148 | </ul> | ||
129 | </body> | 149 | </body> |
130 | </html> | 150 | </html> |
diff --git a/doc/Backup,-restore,-import-and-export.md b/doc/Backup,-restore,-import-and-export.md index cf6b9f48..9f5598ef 100644 --- a/doc/Backup,-restore,-import-and-export.md +++ b/doc/Backup,-restore,-import-and-export.md | |||
@@ -1,4 +1,12 @@ | |||
1 | #Backup, restore, import and export | 1 | #Backup, restore, import and export |
2 | * [Backup and restore the datastore file](#backup-and-restore-the-datastore-file)[](.html) | ||
3 | * [Export links as...](#export-links-as)[](.html) | ||
4 | * [Import links from...](#import-links-from)[](.html) | ||
5 | * [Import Shaarli links to Firefox](#import-shaarli-links-to-firefox)[](.html) | ||
6 | |||
7 | |||
8 | ---------------------- | ||
9 | |||
2 | ## Backup and restore the datastore file | 10 | ## Backup and restore the datastore file |
3 | 11 | ||
4 | Backup the file `data/datastore.php` (by FTP or SSH). Restore by putting the file back in place. | 12 | Backup the file `data/datastore.php` (by FTP or SSH). Restore by putting the file back in place. |
@@ -9,6 +17,7 @@ rsync -avzP my.server.com:/var/www/shaarli/data/datastore.php datastore-$(date + | |||
9 | ``` | 17 | ``` |
10 | 18 | ||
11 | ## Export links as... | 19 | ## Export links as... |
20 | |||
12 | To export links as an HTML file, under _Tools > Export_, choose: | 21 | To export links as an HTML file, under _Tools > Export_, choose: |
13 | - _Export all_ to export both public and private links | 22 | - _Export all_ to export both public and private links |
14 | - _Export public_ to export public links only | 23 | - _Export public_ to export public links only |
@@ -23,13 +32,35 @@ Example command: | |||
23 | ``` | 32 | ``` |
24 | 33 | ||
25 | ## Import links from... | 34 | ## Import links from... |
35 | |||
36 | |||
26 | ### Diigo | 37 | ### Diigo |
27 | 38 | ||
28 | If you export your bookmark from Diigo, make sure you use the Delicious export, not the Netscape export. (Their Netscape export is broken, and they don't seem to be interested in fixing it.) | 39 | If you export your bookmark from Diigo, make sure you use the Delicious export, not the Netscape export. (Their Netscape export is broken, and they don't seem to be interested in fixing it.) |
29 | 40 | ||
41 | |||
30 | ### Mister Wong | 42 | ### Mister Wong |
43 | |||
31 | See [this issue](https://github.com/sebsauvage/Shaarli/issues/146) for import tweaks.[](.html) | 44 | See [this issue](https://github.com/sebsauvage/Shaarli/issues/146) for import tweaks.[](.html) |
32 | 45 | ||
33 | ### SemanticScuttle | 46 | ### SemanticScuttle |
34 | 47 | ||
35 | To correctly import the tags from a [SemanticScuttle](http://semanticscuttle.sourceforge.net/) HTML export, edit the HTML file before importing and replace all occurences of `tags=` (lowercase) to `TAGS=` (uppercase).[](.html) | 48 | To correctly import the tags from a [SemanticScuttle](http://semanticscuttle.sourceforge.net/) HTML export, edit the HTML file before importing and replace all occurences of `tags=` (lowercase) to `TAGS=` (uppercase).[](.html) |
49 | |||
50 | ### Scuttle | ||
51 | |||
52 | Shaarli cannot import data directly from [Scuttle](https://github.com/scronide/scuttle). However, you can use this third party tool: https://github.com/q2apro/scuttle-to-shaarli to export the Scuttle database to the Netscape HTML format compatible with the Shaarli importer.[](.html) | ||
53 | |||
54 | ## Import Shaarli links to Firefox | ||
55 | |||
56 | * Export your Shaarli links as described above. | ||
57 | * For compatibility reasons, check `Prepend note permalinks with this Shaarli instance's URL (useful to import bookmarks in a web browser)` | ||
58 | * In Firefox, open the bookmark manager (not the sidebar! `Bookmarks menu > Show all bookmarks` or `Ctrl+Shift+B`) | ||
59 | * Select `Import and Backup > Import bookmarks in HTML format` | ||
60 | |||
61 | Your bookmarks will be imported in Firefox, ready to use, with tags and descriptions retained. "Self" (notes) shaares will still point to the Shaarli instance you exported them from, but the note text can be viewed directly in the bookmark properties inside your browser. Depending on the number of bookmarks, the import can take some time. | ||
62 | |||
63 | You may be interested in these Firefox addons to manage links imported from Shaarli | ||
64 | |||
65 | * [Bookmark Deduplicator](https://addons.mozilla.org/en-US/firefox/addon/bookmark-deduplicator/) - provides an easy way to deduplicate your bookmarks[](.html) | ||
66 | * [TagSieve](https://addons.mozilla.org/en-US/firefox/addon/tagsieve/) - browse your bookmarks by their tags[](.html) | ||
diff --git a/doc/Browsing-and-searching.html b/doc/Browsing-and-searching.html index 39806128..23001bcb 100644 --- a/doc/Browsing-and-searching.html +++ b/doc/Browsing-and-searching.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
diff --git a/doc/Coding-guidelines.html b/doc/Coding-guidelines.html index add69631..1a2a9351 100644 --- a/doc/Coding-guidelines.html +++ b/doc/Coding-guidelines.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
diff --git a/doc/Community-&-Related-software.html b/doc/Community-&-Related-software.html index 77b9793f..cbc73d54 100644 --- a/doc/Community-&-Related-software.html +++ b/doc/Community-&-Related-software.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
@@ -83,9 +81,11 @@ | |||
83 | <ul> | 81 | <ul> |
84 | <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> |
85 | <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> |
86 | <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> |
87 | <li><a href="https://github.com/NerosTie/emojione">emojione</a> by <a href="https://github.com/NerosTie/emojione">@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> |
88 | <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/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> | ||
89 | </ul> | 89 | </ul> |
90 | <h3 id="themes">Themes</h3> | 90 | <h3 id="themes">Themes</h3> |
91 | <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> |
@@ -96,7 +96,7 @@ | |||
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/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> |
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/BoboTiG/shaarlimages">Shaarlimages</a> - An image-oriented aggregator for Shaarlis<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/mknexen/shaarli-api">mknexen/shaarli-api</a> - A REST API for Shaarli<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. 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> |
100 | </ul> | 100 | </ul> |
101 | <h3 id="mobile-apps">Mobile Apps</h3> | 101 | <h3 id="mobile-apps">Mobile Apps</h3> |
102 | <ul> | 102 | <ul> |
@@ -108,6 +108,7 @@ | |||
108 | <ul> | 108 | <ul> |
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/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> |
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> | 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> | ||
111 | </ul> | 112 | </ul> |
112 | <h2 id="alternatives-to-shaarli">Alternatives to Shaarli</h2> | 113 | <h2 id="alternatives-to-shaarli">Alternatives to Shaarli</h2> |
113 | <ul> | 114 | <ul> |
diff --git a/doc/Community-&-Related-software.md b/doc/Community-&-Related-software.md index 03a3dea9..291bf643 100644 --- a/doc/Community-&-Related-software.md +++ b/doc/Community-&-Related-software.md | |||
@@ -20,9 +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/emojione): 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 | * [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) | ||
26 | 28 | ||
27 | 29 | ||
28 | ### Themes | 30 | ### Themes |
@@ -34,7 +36,7 @@ See [Theming](Theming.html) for the list of community-contributed themes, and an | |||
34 | - [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) |
35 | - [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) |
36 | - [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) |
37 | - [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) |
38 | 40 | ||
39 | ### Mobile Apps | 41 | ### Mobile Apps |
40 | - [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) |
@@ -44,6 +46,7 @@ See [Theming](Theming.html) for the list of community-contributed themes, and an | |||
44 | ## Integration with other platforms | 46 | ## Integration with other platforms |
45 | - [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) |
46 | - [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) | ||
47 | 50 | ||
48 | ## Alternatives to Shaarli | 51 | ## Alternatives to Shaarli |
49 | - [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/Copy-an-existing-installation-over-SSH-and-serve-it-locally.html b/doc/Copy-an-existing-installation-over-SSH-and-serve-it-locally.html index edb1555f..9efb1ad6 100644 --- a/doc/Copy-an-existing-installation-over-SSH-and-serve-it-locally.html +++ b/doc/Copy-an-existing-installation-over-SSH-and-serve-it-locally.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
@@ -111,55 +109,55 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
111 | <span class="co">#Usage: ./local-shaarli.sh</span> | 109 | <span class="co">#Usage: ./local-shaarli.sh</span> |
112 | <span class="co">#Author: nodiscc (nodiscc@gmail.com)</span> | 110 | <span class="co">#Author: nodiscc (nodiscc@gmail.com)</span> |
113 | <span class="co">#License: MIT (http://opensource.org/licenses/MIT)</span> | 111 | <span class="co">#License: MIT (http://opensource.org/licenses/MIT)</span> |
114 | <span class="kw">set</span> <span class="kw">-o</span> errexit | 112 | <span class="kw">set</span> <span class="ex">-o</span> errexit |
115 | <span class="kw">set</span> <span class="kw">-o</span> nounset | 113 | <span class="kw">set</span> <span class="ex">-o</span> nounset |
116 | 114 | ||
117 | <span class="co">##### CONFIG #################</span> | 115 | <span class="co">##### CONFIG #################</span> |
118 | <span class="co">#The port used by php's local server</span> | 116 | <span class="co">#The port used by php's local server</span> |
119 | <span class="ot">php_local_port=</span>7431 | 117 | <span class="va">php_local_port=</span>7431 |
120 | 118 | ||
121 | <span class="co">#Name of the SSH server and path where Shaarli is installed</span> | 119 | <span class="co">#Name of the SSH server and path where Shaarli is installed</span> |
122 | <span class="co">#TODO: pass these as command-line arguments</span> | 120 | <span class="co">#TODO: pass these as command-line arguments</span> |
123 | <span class="ot">remotehost=</span><span class="st">"my.ssh.server"</span> | 121 | <span class="va">remotehost=</span><span class="st">"my.ssh.server"</span> |
124 | <span class="ot">remote_shaarli_dir=</span><span class="st">"/var/www/shaarli"</span> | 122 | <span class="va">remote_shaarli_dir=</span><span class="st">"/var/www/shaarli"</span> |
125 | 123 | ||
126 | 124 | ||
127 | <span class="co">###### FUNCTIONS #############</span> | 125 | <span class="co">###### FUNCTIONS #############</span> |
128 | <span class="fu">_main()</span> <span class="kw">{</span> | 126 | <span class="fu">_main()</span> <span class="kw">{</span> |
129 | <span class="kw">_CBSyncShaarli</span> | 127 | <span class="ex">_CBSyncShaarli</span> |
130 | <span class="kw">_CBServeShaarli</span> | 128 | <span class="ex">_CBServeShaarli</span> |
131 | <span class="kw">}</span> | 129 | <span class="kw">}</span> |
132 | 130 | ||
133 | <span class="fu">_CBSyncShaarli()</span> <span class="kw">{</span> | 131 | <span class="fu">_CBSyncShaarli()</span> <span class="kw">{</span> |
134 | <span class="ot">remote_temp_dir=$(</span><span class="kw">ssh</span> <span class="ot">$remotehost</span> mktemp -d<span class="ot">)</span> | 132 | <span class="va">remote_temp_dir=$(</span><span class="fu">ssh</span> <span class="va">$remotehost</span> mktemp -d<span class="va">)</span> |
135 | <span class="ot">remote_ssh_user=$(</span><span class="kw">ssh</span> <span class="ot">$remotehost</span> whoami<span class="ot">)</span> | 133 | <span class="va">remote_ssh_user=$(</span><span class="fu">ssh</span> <span class="va">$remotehost</span> whoami<span class="va">)</span> |
136 | <span class="kw">ssh</span> -t <span class="st">"</span><span class="ot">$remotehost</span><span class="st">"</span> sudo cp -r <span class="st">"</span><span class="ot">$remote_shaarli_dir</span><span class="st">"</span> <span class="st">"</span><span class="ot">$remote_temp_dir</span><span class="st">"</span> | 134 | <span class="fu">ssh</span> -t <span class="st">"</span><span class="va">$remotehost</span><span class="st">"</span> sudo cp -r <span class="st">"</span><span class="va">$remote_shaarli_dir</span><span class="st">"</span> <span class="st">"</span><span class="va">$remote_temp_dir</span><span class="st">"</span> |
137 | <span class="kw">ssh</span> -t <span class="st">"</span><span class="ot">$remotehost</span><span class="st">"</span> sudo chown -R <span class="st">"</span><span class="ot">$remote_ssh_user</span><span class="st">"</span>:<span class="st">"</span><span class="ot">$remote_ssh_user</span><span class="st">"</span> <span class="st">"</span><span class="ot">$remote_temp_dir</span><span class="st">"</span> | 135 | <span class="fu">ssh</span> -t <span class="st">"</span><span class="va">$remotehost</span><span class="st">"</span> sudo chown -R <span class="st">"</span><span class="va">$remote_ssh_user</span><span class="st">"</span>:<span class="st">"</span><span class="va">$remote_ssh_user</span><span class="st">"</span> <span class="st">"</span><span class="va">$remote_temp_dir</span><span class="st">"</span> |
138 | <span class="kw">scp</span> -rq <span class="st">"</span><span class="ot">$remotehost</span><span class="st">"</span>:<span class="st">"</span><span class="ot">$remote_temp_dir</span><span class="st">"</span> local-shaarli | 136 | <span class="fu">scp</span> -rq <span class="st">"</span><span class="va">$remotehost</span><span class="st">"</span>:<span class="st">"</span><span class="va">$remote_temp_dir</span><span class="st">"</span> local-shaarli |
139 | <span class="kw">ssh</span> <span class="st">"</span><span class="ot">$remotehost</span><span class="st">"</span> rm -r <span class="st">"</span><span class="ot">$remote_temp_dir</span><span class="st">"</span> | 137 | <span class="fu">ssh</span> <span class="st">"</span><span class="va">$remotehost</span><span class="st">"</span> rm -r <span class="st">"</span><span class="va">$remote_temp_dir</span><span class="st">"</span> |
140 | <span class="kw">}</span> | 138 | <span class="kw">}</span> |
141 | 139 | ||
142 | <span class="fu">_CBServeShaarli()</span> <span class="kw">{</span> | 140 | <span class="fu">_CBServeShaarli()</span> <span class="kw">{</span> |
143 | <span class="co">#TODO: allow serving a previously downloaded Shaarli</span> | 141 | <span class="co">#TODO: allow serving a previously downloaded Shaarli</span> |
144 | <span class="co">#TODO: ask before overwriting local copy, if it exists</span> | 142 | <span class="co">#TODO: ask before overwriting local copy, if it exists</span> |
145 | <span class="kw">cd</span> local-shaarli/ | 143 | <span class="bu">cd</span> local-shaarli/ |
146 | <span class="kw">php</span> -S localhost:<span class="ot">${php_local_port}</span> | 144 | <span class="ex">php</span> -S localhost:<span class="va">${php_local_port}</span> |
147 | <span class="kw">echo</span> <span class="st">"Please go to http://localhost:</span><span class="ot">${php_local_port}</span><span class="st">"</span> | 145 | <span class="bu">echo</span> <span class="st">"Please go to http://localhost:</span><span class="va">${php_local_port}</span><span class="st">"</span> |
148 | <span class="kw">}</span> | 146 | <span class="kw">}</span> |
149 | 147 | ||
150 | 148 | ||
151 | <span class="co">##### MAIN #################</span> | 149 | <span class="co">##### MAIN #################</span> |
152 | 150 | ||
153 | <span class="kw">_main</span></code></pre></div> | 151 | <span class="ex">_main</span></code></pre></div> |
154 | <p>This outputs:</p> | 152 | <p>This outputs:</p> |
155 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">./local-shaarli.sh</span> | 153 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">./local-shaarli.sh</span> |
156 | <span class="kw">PHP</span> 5.6.0RC4 Development Server started at Mon Sep 1 21:56:19 2014 | 154 | <span class="ex">PHP</span> 5.6.0RC4 Development Server started at Mon Sep 1 21:56:19 2014 |
157 | <span class="kw">Listening</span> on http://localhost:7431 | 155 | <span class="ex">Listening</span> on http://localhost:7431 |
158 | <span class="kw">Document</span> root is /home/user/local-shaarli/shaarli | 156 | <span class="ex">Document</span> root is /home/user/local-shaarli/shaarli |
159 | <span class="kw">Press</span> Ctrl-C to quit. | 157 | <span class="ex">Press</span> Ctrl-C to quit. |
160 | 158 | ||
161 | [<span class="kw">Mon</span> Sep 1 21:56:27 2014] ::1:57868 [200]: /[](.html) | 159 | [<span class="ex">Mon</span> Sep 1 21:56:27 2014] ::1:57868 [200]: /[](.html) |
162 | [<span class="kw">Mon</span> Sep 1 21:56:27 2014] ::1:57869 [200]: /index.html[](.html) | 160 | [<span class="ex">Mon</span> Sep 1 21:56:27 2014] ::1:57869 [200]: /index.html[](.html) |
163 | [<span class="kw">Mon</span> Sep 1 21:56:37 2014] ::1:57881 [200]: /...[](.html)</code></pre></div> | 161 | [<span class="ex">Mon</span> Sep 1 21:56:37 2014] ::1:57881 [200]: /...[](.html)</code></pre></div> |
164 | </body> | 162 | </body> |
165 | </html> | 163 | </html> |
diff --git a/doc/Create-and-serve-multiple-Shaarlis-(farm).html b/doc/Create-and-serve-multiple-Shaarlis-(farm).html index 933144e4..672e4bf3 100644 --- a/doc/Create-and-serve-multiple-Shaarlis-(farm).html +++ b/doc/Create-and-serve-multiple-Shaarlis-(farm).html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,33 +96,32 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
104 | <h1 id="create-and-serve-multiple-shaarlis-farm">Create and serve multiple Shaarlis (farm)</h1> | 102 | <h1 id="create-and-serve-multiple-shaarlis-farm">Create and serve multiple Shaarlis (farm)</h1> |
105 | <p>Example bash script (creates multiple shaarli instances and generates an HTML index of them)</p> | 103 | <p>Example bash script (creates multiple shaarli instances and generates an HTML index of them)</p> |
106 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co">#!/bin/bash</span> | 104 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co">#!/bin/bash</span> |
107 | <span class="kw">set</span> <span class="kw">-o</span> errexit | 105 | <span class="kw">set</span> <span class="ex">-o</span> errexit |
108 | <span class="kw">set</span> <span class="kw">-o</span> nounset | 106 | <span class="kw">set</span> <span class="ex">-o</span> nounset |
109 | 107 | ||
110 | <span class="co">#config</span> | 108 | <span class="co">#config</span> |
111 | <span class="ot">shaarli_base_dir=</span><span class="st">'/var/www/shaarli'</span> | 109 | <span class="va">shaarli_base_dir=</span><span class="st">'/var/www/shaarli'</span> |
112 | <span class="ot">accounts=</span><span class="st">'bob john whatever username'</span> | 110 | <span class="va">accounts=</span><span class="st">'bob john whatever username'</span> |
113 | <span class="ot">shaarli_repo_url=</span><span class="st">'https://github.com/shaarli/Shaarli'</span> | 111 | <span class="va">shaarli_repo_url=</span><span class="st">'https://github.com/shaarli/Shaarli'</span> |
114 | <span class="ot">ref=</span><span class="st">"master"</span> | 112 | <span class="va">ref=</span><span class="st">"master"</span> |
115 | 113 | ||
116 | <span class="co">#clone multiple shaarli instances</span> | 114 | <span class="co">#clone multiple shaarli instances</span> |
117 | <span class="kw">if [</span> <span class="ot">!</span> <span class="ot">-d</span> <span class="st">"</span><span class="ot">$shaarli_base_dir</span><span class="st">"</span><span class="kw"> ]</span>; <span class="kw">then</span> <span class="kw">mkdir</span> <span class="st">"</span><span class="ot">$shaarli_base_dir</span><span class="st">"</span><span class="kw">;</span> <span class="kw">fi</span>[]<span class="kw">(.html)</span> | 115 | <span class="kw">if</span><span class="bu"> [</span> <span class="ot">!</span> <span class="ot">-d</span> <span class="st">"</span><span class="va">$shaarli_base_dir</span><span class="st">"</span><span class="bu"> ]</span>; <span class="kw">then</span> <span class="fu">mkdir</span> <span class="st">"</span><span class="va">$shaarli_base_dir</span><span class="st">"</span><span class="kw">;</span> <span class="kw">fi</span>[]<span class="kw">(</span><span class="ex">.html</span><span class="kw">)</span> |
118 | 116 | ||
119 | <span class="kw">for</span> <span class="kw">account</span> in <span class="ot">$accounts</span><span class="kw">;</span> <span class="kw">do</span> | 117 | <span class="kw">for</span> <span class="ex">account</span> in <span class="va">$accounts</span><span class="kw">;</span> <span class="kw">do</span> |
120 | <span class="kw">if [</span> <span class="ot">-d</span> <span class="st">"</span><span class="ot">$shaarli_base_dir</span><span class="st">/</span><span class="ot">$account</span><span class="st">"</span><span class="kw"> ]</span>;[]<span class="kw">(.html)</span> | 118 | <span class="kw">if</span><span class="bu"> [</span> <span class="ot">-d</span> <span class="st">"</span><span class="va">$shaarli_base_dir</span><span class="st">/</span><span class="va">$account</span><span class="st">"</span><span class="bu"> ]</span>;[]<span class="kw">(</span><span class="ex">.html</span><span class="kw">)</span> |
121 | <span class="kw">then</span> <span class="kw">echo</span> <span class="st">"[info] account </span><span class="ot">$account</span><span class="st"> already exists, skipping"</span><span class="kw">;</span>[]<span class="kw">(.html)</span> | 119 | <span class="kw">then</span> <span class="bu">echo</span> <span class="st">"[info] account </span><span class="va">$account</span><span class="st"> already exists, skipping"</span><span class="kw">;</span>[]<span class="kw">(</span><span class="ex">.html</span><span class="kw">)</span> |
122 | <span class="kw">else</span> <span class="kw">echo</span> <span class="st">"[info] creating new account </span><span class="ot">$account</span><span class="st"> ..."</span><span class="kw">;</span> <span class="kw">git</span> clone --quiet <span class="st">"</span><span class="ot">$shaarli_repo_url</span><span class="st">"</span> -b <span class="st">"</span><span class="ot">$ref</span><span class="st">"</span> <span class="st">"</span><span class="ot">$shaarli_base_dir</span><span class="st">/</span><span class="ot">$account</span><span class="st">"</span><span class="kw">;</span> <span class="kw">fi</span>[]<span class="kw">(.html)</span> | 120 | <span class="kw">else</span> <span class="bu">echo</span> <span class="st">"[info] creating new account </span><span class="va">$account</span><span class="st"> ..."</span><span class="kw">;</span> <span class="fu">git</span> clone --quiet <span class="st">"</span><span class="va">$shaarli_repo_url</span><span class="st">"</span> -b <span class="st">"</span><span class="va">$ref</span><span class="st">"</span> <span class="st">"</span><span class="va">$shaarli_base_dir</span><span class="st">/</span><span class="va">$account</span><span class="st">"</span><span class="kw">;</span> <span class="kw">fi</span>[]<span class="kw">(</span><span class="ex">.html</span><span class="kw">)</span> |
123 | <span class="kw">done</span> | 121 | <span class="kw">done</span> |
124 | 122 | ||
125 | <span class="co">#generate html index of shaarlis</span> | 123 | <span class="co">#generate html index of shaarlis</span> |
126 | <span class="ot">htmlhead=</span><span class="st">'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"></span> | 124 | <span class="va">htmlhead=</span><span class="st">'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"></span> |
127 | <span class="st"><!-- Minimal html template thanks to http://www.sitepoint.com/a-minimal-html-document/ --></span> | 125 | <span class="st"><!-- Minimal html template thanks to http://www.sitepoint.com/a-minimal-html-document/ --></span> |
128 | <span class="st"><html lang="en"></span> | 126 | <span class="st"><html lang="en"></span> |
129 | <span class="st"> <head></span> | 127 | <span class="st"> <head></span> |
@@ -136,9 +134,9 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
136 | <span class="st"> <h1>My Shaarli farm</h1></span> | 134 | <span class="st"> <h1>My Shaarli farm</h1></span> |
137 | <span class="st"> <ul style="list-style-type: none;">'</span> | 135 | <span class="st"> <ul style="list-style-type: none;">'</span> |
138 | 136 | ||
139 | <span class="ot">accountlinks=</span><span class="st">''</span> | 137 | <span class="va">accountlinks=</span><span class="st">''</span> |
140 | 138 | ||
141 | <span class="ot">htmlfooter=</span><span class="st">'</span> | 139 | <span class="va">htmlfooter=</span><span class="st">'</span> |
142 | <span class="st"> </ul></span> | 140 | <span class="st"> </ul></span> |
143 | <span class="st"> </div></span> | 141 | <span class="st"> </div></span> |
144 | <span class="st"> </body></span> | 142 | <span class="st"> </body></span> |
@@ -146,14 +144,14 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
146 | 144 | ||
147 | 145 | ||
148 | 146 | ||
149 | <span class="kw">for</span> <span class="kw">account</span> in <span class="ot">$accounts</span><span class="kw">;</span> <span class="kw">do</span> <span class="ot">accountlinks=</span><span class="st">"</span><span class="ot">$accountlinks</span><span class="st">\n<li><a href=</span><span class="dt">\"</span><span class="ot">$account</span><span class="dt">\"</span><span class="st">></span><span class="ot">$account</span><span class="st"></a></li>"</span>; <span class="kw">done</span> | 147 | <span class="kw">for</span> <span class="ex">account</span> in <span class="va">$accounts</span><span class="kw">;</span> <span class="kw">do</span> <span class="va">accountlinks=</span><span class="st">"</span><span class="va">$accountlinks</span><span class="st">\n<li><a href=</span><span class="dt">\"</span><span class="va">$account</span><span class="dt">\"</span><span class="st">></span><span class="va">$account</span><span class="st"></a></li>"</span>; <span class="kw">done</span> |
150 | <span class="kw">if [</span> <span class="ot">-d</span> <span class="st">"</span><span class="ot">$shaarli_base_dir</span><span class="st">/index.html"</span><span class="kw"> ]</span>; <span class="kw">then</span> <span class="kw">echo</span> <span class="st">"[removing old index.html]"</span><span class="kw">;</span> <span class="kw">rm</span> <span class="st">"</span><span class="ot">$shaarli_base_dir</span><span class="st">/index.html"</span> ]<span class="kw">;</span> <span class="kw">fi</span>[]<span class="kw">(.html)</span> | 148 | <span class="kw">if</span><span class="bu"> [</span> <span class="ot">-d</span> <span class="st">"</span><span class="va">$shaarli_base_dir</span><span class="st">/index.html"</span><span class="bu"> ]</span>; <span class="kw">then</span> <span class="bu">echo</span> <span class="st">"[removing old index.html]"</span><span class="kw">;</span> <span class="fu">rm</span> <span class="st">"</span><span class="va">$shaarli_base_dir</span><span class="st">/index.html"</span> ]<span class="kw">;</span> <span class="kw">fi</span>[]<span class="kw">(</span><span class="ex">.html</span><span class="kw">)</span> |
151 | <span class="kw">echo</span> <span class="st">"[info] generating new index of shaarlis"</span>[](.html) | 149 | <span class="bu">echo</span> <span class="st">"[info] generating new index of shaarlis"</span>[](.html) |
152 | <span class="kw">echo</span> -e <span class="st">"</span><span class="ot">$htmlhead</span><span class="st"> </span><span class="ot">$accountlinks</span><span class="st"> </span><span class="ot">$htmlfooter</span><span class="st">"</span> <span class="kw">></span> <span class="st">"</span><span class="ot">$shaarli_base_dir</span><span class="st">/index.html"</span> | 150 | <span class="bu">echo</span> -e <span class="st">"</span><span class="va">$htmlhead</span><span class="st"> </span><span class="va">$accountlinks</span><span class="st"> </span><span class="va">$htmlfooter</span><span class="st">"</span> <span class="op">></span> <span class="st">"</span><span class="va">$shaarli_base_dir</span><span class="st">/index.html"</span> |
153 | <span class="kw">echo</span> <span class="st">'[info] done.'</span>[](.html) | 151 | <span class="bu">echo</span> <span class="st">'[info] done.'</span>[](.html) |
154 | <span class="kw">echo</span> <span class="st">"[info] list of accounts: </span><span class="ot">$accounts</span><span class="st">"</span>[](.html) | 152 | <span class="bu">echo</span> <span class="st">"[info] list of accounts: </span><span class="va">$accounts</span><span class="st">"</span>[](.html) |
155 | <span class="kw">echo</span> <span class="st">"[info] contents of </span><span class="ot">$shaarli_base_dir</span><span class="st">:"</span>[](.html) | 153 | <span class="bu">echo</span> <span class="st">"[info] contents of </span><span class="va">$shaarli_base_dir</span><span class="st">:"</span>[](.html) |
156 | <span class="kw">tree</span> -a -L 1 <span class="st">"</span><span class="ot">$shaarli_base_dir</span><span class="st">"</span></code></pre></div> | 154 | <span class="ex">tree</span> -a -L 1 <span class="st">"</span><span class="va">$shaarli_base_dir</span><span class="st">"</span></code></pre></div> |
157 | <p>This script just serves as an example. More precise or complex (applying custom configuration, etc) automation is possible using configuration management software like <a href="https://www.ansible.com/">Ansible</a><a href=".html"></a></p> | 155 | <p>This script just serves as an example. More precise or complex (applying custom configuration, etc) automation is possible using configuration management software like <a href="https://www.ansible.com/">Ansible</a><a href=".html"></a></p> |
158 | </body> | 156 | </body> |
159 | </html> | 157 | </html> |
diff --git a/doc/Datastore-hacks.html b/doc/Datastore-hacks.html index 88639402..15da09d4 100644 --- a/doc/Datastore-hacks.html +++ b/doc/Datastore-hacks.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
diff --git a/doc/Development.html b/doc/Development.html index 2eacff94..c5776413 100644 --- a/doc/Development.html +++ b/doc/Development.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
diff --git a/doc/Directory-structure.html b/doc/Directory-structure.html index 003d4d94..404ff7c8 100644 --- a/doc/Directory-structure.html +++ b/doc/Directory-structure.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,39 +96,38 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
104 | <h1 id="directory-structure">Directory structure</h1> | 102 | <h1 id="directory-structure">Directory structure</h1> |
105 | <p>Here is the directory structure of Shaarli and the purpose of the different files:</p> | 103 | <p>Here is the directory structure of Shaarli and the purpose of the different files:</p> |
106 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"> <span class="kw">index.php</span> <span class="co"># Main program</span> | 104 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"> <span class="ex">index.php</span> <span class="co"># Main program</span> |
107 | <span class="kw">application/</span> <span class="co"># Shaarli classes</span> | 105 | <span class="ex">application/</span> <span class="co"># Shaarli classes</span> |
108 | ├── <span class="kw">LinkDB.php</span> | 106 | ├── <span class="ex">LinkDB.php</span> |
109 | └── <span class="kw">Utils.php</span> | 107 | └── <span class="ex">Utils.php</span> |
110 | <span class="kw">tests/</span> <span class="co"># Shaarli unitary & functional tests</span> | 108 | <span class="ex">tests/</span> <span class="co"># Shaarli unitary & functional tests</span> |
111 | ├── <span class="kw">LinkDBTest.php</span> | 109 | ├── <span class="ex">LinkDBTest.php</span> |
112 | ├── <span class="kw">utils</span> <span class="co"># utilities to ease testing</span> | 110 | ├── <span class="ex">utils</span> <span class="co"># utilities to ease testing</span> |
113 | │ └── <span class="kw">ReferenceLinkDB.php</span> | 111 | │ └── <span class="ex">ReferenceLinkDB.php</span> |
114 | └── <span class="kw">UtilsTest.php</span> | 112 | └── <span class="ex">UtilsTest.php</span> |
115 | <span class="kw">COPYING</span> <span class="co"># Shaarli license</span> | 113 | <span class="ex">COPYING</span> <span class="co"># Shaarli license</span> |
116 | <span class="kw">inc/</span> <span class="co"># static assets and 3rd party libraries</span> | 114 | <span class="ex">inc/</span> <span class="co"># static assets and 3rd party libraries</span> |
117 | ├── <span class="kw">awesomplete.*</span> <span class="co"># tags autocompletion library</span> | 115 | ├── <span class="ex">awesomplete.*</span> <span class="co"># tags autocompletion library</span> |
118 | ├── <span class="kw">blazy.*</span> <span class="co"># picture wall lazy image loading library</span> | 116 | ├── <span class="ex">blazy.*</span> <span class="co"># picture wall lazy image loading library</span> |
119 | ├── <span class="kw">shaarli.css</span>, reset.css <span class="co"># Shaarli stylesheet.</span> | 117 | ├── <span class="ex">shaarli.css</span>, reset.css <span class="co"># Shaarli stylesheet.</span> |
120 | ├── <span class="kw">qr.*</span> <span class="co"># qr code generation library</span> | 118 | ├── <span class="ex">qr.*</span> <span class="co"># qr code generation library</span> |
121 | └──<span class="kw">rain.tpl.class.php</span> <span class="co"># RainTPL templating library</span> | 119 | └──<span class="ex">rain.tpl.class.php</span> <span class="co"># RainTPL templating library</span> |
122 | <span class="kw">tpl/</span> <span class="co"># RainTPL templates for Shaarli. They are used to build the pages.</span> | 120 | <span class="ex">tpl/</span> <span class="co"># RainTPL templates for Shaarli. They are used to build the pages.</span> |
123 | <span class="kw">images/</span> <span class="co"># Images and icons used in Shaarli</span> | 121 | <span class="ex">images/</span> <span class="co"># Images and icons used in Shaarli</span> |
124 | <span class="kw">data/</span> <span class="co"># data storage: bookmark database, configuration, logs, banlist…</span> | 122 | <span class="ex">data/</span> <span class="co"># data storage: bookmark database, configuration, logs, banlist…</span> |
125 | ├── <span class="kw">config.php</span> <span class="co"># Shaarli configuration (login, password, timezone, title…)</span> | 123 | ├── <span class="ex">config.php</span> <span class="co"># Shaarli configuration (login, password, timezone, title…)</span> |
126 | ├── <span class="kw">datastore.php</span> <span class="co"># Your link database (compressed).</span> | 124 | ├── <span class="ex">datastore.php</span> <span class="co"># Your link database (compressed).</span> |
127 | ├── <span class="kw">ipban.php</span> <span class="co"># IP address ban system data</span> | 125 | ├── <span class="ex">ipban.php</span> <span class="co"># IP address ban system data</span> |
128 | ├── <span class="kw">lastupdatecheck.txt</span> <span class="co"># Update check timestamp file</span> | 126 | ├── <span class="ex">lastupdatecheck.txt</span> <span class="co"># Update check timestamp file</span> |
129 | └──<span class="kw">log.txt</span> <span class="co"># login/IPban log.</span> | 127 | └──<span class="ex">log.txt</span> <span class="co"># login/IPban log.</span> |
130 | <span class="kw">cache/</span> <span class="co"># thumbnails cache</span> | 128 | <span class="ex">cache/</span> <span class="co"># thumbnails cache</span> |
131 | <span class="co"># This directory is automatically created. You can erase it anytime you want.</span> | 129 | <span class="co"># This directory is automatically created. You can erase it anytime you want.</span> |
132 | <span class="kw">tmp/</span> <span class="co"># Temporary directory for compiled RainTPL templates.</span> | 130 | <span class="ex">tmp/</span> <span class="co"># Temporary directory for compiled RainTPL templates.</span> |
133 | <span class="co"># This directory is automatically created. You can erase it anytime you want.</span></code></pre></div> | 131 | <span class="co"># This directory is automatically created. You can erase it anytime you want.</span></code></pre></div> |
134 | </body> | 132 | </body> |
135 | </html> | 133 | </html> |
diff --git a/doc/Docker.html b/doc/Docker.html index a443d100..e89c90fb 100644 --- a/doc/Docker.html +++ b/doc/Docker.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
@@ -112,18 +110,18 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
112 | <p>Install <a href="https://www.docker.com/">Docker</a>, by following the instructions relevant<a href=".html"></a><br /> | 110 | <p>Install <a href="https://www.docker.com/">Docker</a>, by following the instructions relevant<a href=".html"></a><br /> |
113 | to your OS / distribution, and start the service.</p> | 111 | to your OS / distribution, and start the service.</p> |
114 | <h4 id="search-an-image-on-dockerhub">Search an image on <a href="https://hub.docker.com/">DockerHub</a><a href=".html"></a></h4> | 112 | <h4 id="search-an-image-on-dockerhub">Search an image on <a href="https://hub.docker.com/">DockerHub</a><a href=".html"></a></h4> |
115 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">docker</span> search debian | 113 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">docker</span> search debian |
116 | 114 | ||
117 | <span class="kw">NAME</span> DESCRIPTION STARS OFFICIAL AUTOMATED | 115 | <span class="ex">NAME</span> DESCRIPTION STARS OFFICIAL AUTOMATED |
118 | <span class="kw">ubuntu</span> Ubuntu is a Debian-based Linux operating s... 2065 [OK][](.html) | 116 | <span class="ex">ubuntu</span> Ubuntu is a Debian-based Linux operating s... 2065 [OK][](.html) |
119 | <span class="kw">debian</span> Debian is a Linux distribution that<span class="st">'s comp... 603 [OK][](.html)</span> | 117 | <span class="ex">debian</span> Debian is a Linux distribution that<span class="st">'s comp... 603 [OK][](.html)</span> |
120 | <span class="st">google/debian 47 [OK][](.html)</span></code></pre></div> | 118 | <span class="st">google/debian 47 [OK][](.html)</span></code></pre></div> |
121 | <h4 id="show-available-tags-for-a-repository">Show available tags for a repository</h4> | 119 | <h4 id="show-available-tags-for-a-repository">Show available tags for a repository</h4> |
122 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">curl</span> https://index.docker.io/v1/repositories/debian/tags <span class="kw">|</span> <span class="kw">python</span> -m json.tool | 120 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">curl</span> https://index.docker.io/v1/repositories/debian/tags <span class="kw">|</span> <span class="ex">python</span> -m json.tool |
123 | 121 | ||
124 | <span class="kw">%</span> Total % Received % Xferd Average Speed Time Time Time Current | 122 | <span class="ex">%</span> Total % Received % Xferd Average Speed Time Time Time Current |
125 | <span class="kw">Dload</span> Upload Total Spent Left Speed | 123 | <span class="ex">Dload</span> Upload Total Spent Left Speed |
126 | <span class="kw">100</span> 1283 0 1283 0 0 433 0 --:--:-- 0:00:02 --:--:-- 433</code></pre></div> | 124 | <span class="ex">100</span> 1283 0 1283 0 0 433 0 --:--:-- 0:00:02 --:--:-- 433</code></pre></div> |
127 | <p>Sample output:</p> | 125 | <p>Sample output:</p> |
128 | <div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="ot">[[]</span><span class="er">(.html)</span> | 126 | <div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="ot">[[]</span><span class="er">(.html)</span> |
129 | <span class="fu">{</span> | 127 | <span class="fu">{</span> |
@@ -148,14 +146,14 @@ to your OS / distribution, and start the service.</p> | |||
148 | <span class="fu">}</span> | 146 | <span class="fu">}</span> |
149 | <span class="ot">]</span></code></pre></div> | 147 | <span class="ot">]</span></code></pre></div> |
150 | <h4 id="pull-an-image-from-dockerhub">Pull an image from DockerHub</h4> | 148 | <h4 id="pull-an-image-from-dockerhub">Pull an image from DockerHub</h4> |
151 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">docker</span> pull repository[:tag][](.html) | 149 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">docker</span> pull repository[:tag][](.html) |
152 | 150 | ||
153 | $ <span class="kw">docker</span> pull debian:wheezy | 151 | $ <span class="ex">docker</span> pull debian:wheezy |
154 | <span class="kw">wheezy</span>: Pulling from debian | 152 | <span class="ex">wheezy</span>: Pulling from debian |
155 | <span class="kw">4c8cbfd2973e</span>: Pull complete | 153 | <span class="ex">4c8cbfd2973e</span>: Pull complete |
156 | <span class="kw">60c52dbe9d91</span>: Pull complete | 154 | <span class="ex">60c52dbe9d91</span>: Pull complete |
157 | <span class="kw">Digest</span>: sha256:c584131da2ac1948aa3e66468a4424b6aea2f33acba7cec0b631bdb56254c4fe | 155 | <span class="ex">Digest</span>: sha256:c584131da2ac1948aa3e66468a4424b6aea2f33acba7cec0b631bdb56254c4fe |
158 | <span class="kw">Status</span>: Downloaded newer image for debian:wheezy</code></pre></div> | 156 | <span class="ex">Status</span>: Downloaded newer image for debian:wheezy</code></pre></div> |
159 | <h2 id="get-and-run-a-shaarli-image">Get and run a Shaarli image</h2> | 157 | <h2 id="get-and-run-a-shaarli-image">Get and run a Shaarli image</h2> |
160 | <h3 id="dockerhub-repository">DockerHub repository</h3> | 158 | <h3 id="dockerhub-repository">DockerHub repository</h3> |
161 | <p>The images can be found in the <a href="https://hub.docker.com/r/shaarli/shaarli/"><code>shaarli/shaarli</code></a><a href=".html"></a><br /> | 159 | <p>The images can be found in the <a href="https://hub.docker.com/r/shaarli/shaarli/"><code>shaarli/shaarli</code></a><a href=".html"></a><br /> |
@@ -173,53 +171,53 @@ repository.</p> | |||
173 | <li><a href="http://nginx.org/">Nginx</a><a href=".html"></a></li> | 171 | <li><a href="http://nginx.org/">Nginx</a><a href=".html"></a></li> |
174 | </ul> | 172 | </ul> |
175 | <h3 id="download-from-dockerhub">Download from DockerHub</h3> | 173 | <h3 id="download-from-dockerhub">Download from DockerHub</h3> |
176 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">docker</span> pull shaarli/shaarli | 174 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">docker</span> pull shaarli/shaarli |
177 | <span class="kw">latest</span>: Pulling from shaarli/shaarli | 175 | <span class="ex">latest</span>: Pulling from shaarli/shaarli |
178 | <span class="kw">32716d9fcddb</span>: Pull complete | 176 | <span class="ex">32716d9fcddb</span>: Pull complete |
179 | <span class="kw">84899d045435</span>: Pull complete | 177 | <span class="ex">84899d045435</span>: Pull complete |
180 | <span class="kw">4b6ad7444763</span>: Pull complete | 178 | <span class="ex">4b6ad7444763</span>: Pull complete |
181 | <span class="kw">e0345ef7a3e0</span>: Pull complete | 179 | <span class="ex">e0345ef7a3e0</span>: Pull complete |
182 | <span class="kw">5c1dd344094f</span>: Pull complete | 180 | <span class="ex">5c1dd344094f</span>: Pull complete |
183 | <span class="kw">6422305a200b</span>: Pull complete | 181 | <span class="ex">6422305a200b</span>: Pull complete |
184 | <span class="kw">7d63f861dbef</span>: Pull complete | 182 | <span class="ex">7d63f861dbef</span>: Pull complete |
185 | <span class="kw">3eb97210645c</span>: Pull complete | 183 | <span class="ex">3eb97210645c</span>: Pull complete |
186 | <span class="kw">869319d746ff</span>: Already exists | 184 | <span class="ex">869319d746ff</span>: Already exists |
187 | <span class="kw">869319d746ff</span>: Pulling fs layer | 185 | <span class="ex">869319d746ff</span>: Pulling fs layer |
188 | <span class="kw">902b87aaaec9</span>: Already exists | 186 | <span class="ex">902b87aaaec9</span>: Already exists |
189 | <span class="kw">Digest</span>: sha256:f836b4627b958b3f83f59c332f22f02fcd495ace3056f2be2c4912bd8704cc98 | 187 | <span class="ex">Digest</span>: sha256:f836b4627b958b3f83f59c332f22f02fcd495ace3056f2be2c4912bd8704cc98 |
190 | <span class="kw">Status</span>: Downloaded newer image for shaarli/shaarli:latest</code></pre></div> | 188 | <span class="ex">Status</span>: Downloaded newer image for shaarli/shaarli:latest</code></pre></div> |
191 | <h3 id="create-and-start-a-new-container-from-the-image">Create and start a new container from the image</h3> | 189 | <h3 id="create-and-start-a-new-container-from-the-image">Create and start a new container from the image</h3> |
192 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># map the host's :8000 port to the container's :80 port</span> | 190 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># map the host's :8000 port to the container's :80 port</span> |
193 | $ <span class="kw">docker</span> create -p 8000:80 shaarli/shaarli | 191 | $ <span class="ex">docker</span> create -p 8000:80 shaarli/shaarli |
194 | <span class="kw">d40b7af693d678958adedfb88f87d6ea0237186c23de5c4102a55a8fcb499101</span> | 192 | <span class="ex">d40b7af693d678958adedfb88f87d6ea0237186c23de5c4102a55a8fcb499101</span> |
195 | 193 | ||
196 | <span class="co"># launch the container in the background</span> | 194 | <span class="co"># launch the container in the background</span> |
197 | $ <span class="kw">docker</span> start d40b7af693d678958adedfb88f87d6ea0237186c23de5c4102a55a8fcb499101 | 195 | $ <span class="ex">docker</span> start d40b7af693d678958adedfb88f87d6ea0237186c23de5c4102a55a8fcb499101 |
198 | <span class="kw">d40b7af693d678958adedfb88f87d6ea0237186c23de5c4102a55a8fcb499101</span> | 196 | <span class="ex">d40b7af693d678958adedfb88f87d6ea0237186c23de5c4102a55a8fcb499101</span> |
199 | 197 | ||
200 | <span class="co"># list active containers</span> | 198 | <span class="co"># list active containers</span> |
201 | $ <span class="kw">docker</span> ps | 199 | $ <span class="ex">docker</span> ps |
202 | <span class="kw">CONTAINER</span> ID IMAGE COMMAND CREATED STATUS PORTS NAMES | 200 | <span class="ex">CONTAINER</span> ID IMAGE COMMAND CREATED STATUS PORTS NAMES |
203 | <span class="kw">d40b7af693d6</span> shaarli/shaarli /usr/bin/supervisor 15 seconds ago Up 4 seconds 0.0.0.0:8000-<span class="kw">></span>80/tcp backstabbing_galileo</code></pre></div> | 201 | <span class="ex">d40b7af693d6</span> shaarli/shaarli /usr/bin/supervisor 15 seconds ago Up 4 seconds 0.0.0.0:8000-<span class="op">></span>80/tcp backstabbing_galileo</code></pre></div> |
204 | <h3 id="stop-and-destroy-a-container">Stop and destroy a container</h3> | 202 | <h3 id="stop-and-destroy-a-container">Stop and destroy a container</h3> |
205 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">docker</span> stop backstabbing_galileo <span class="co"># those docker guys are really rude to physicists!</span> | 203 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">docker</span> stop backstabbing_galileo <span class="co"># those docker guys are really rude to physicists!</span> |
206 | <span class="kw">backstabbing_galileo</span> | 204 | <span class="ex">backstabbing_galileo</span> |
207 | 205 | ||
208 | <span class="co"># check the container is stopped</span> | 206 | <span class="co"># check the container is stopped</span> |
209 | $ <span class="kw">docker</span> ps | 207 | $ <span class="ex">docker</span> ps |
210 | <span class="kw">CONTAINER</span> ID IMAGE COMMAND CREATED STATUS PORTS NAMES | 208 | <span class="ex">CONTAINER</span> ID IMAGE COMMAND CREATED STATUS PORTS NAMES |
211 | 209 | ||
212 | <span class="co"># list ALL containers</span> | 210 | <span class="co"># list ALL containers</span> |
213 | $ <span class="kw">docker</span> ps -a | 211 | $ <span class="ex">docker</span> ps -a |
214 | <span class="kw">CONTAINER</span> ID IMAGE COMMAND CREATED STATUS PORTS NAMES | 212 | <span class="ex">CONTAINER</span> ID IMAGE COMMAND CREATED STATUS PORTS NAMES |
215 | <span class="kw">d40b7af693d6</span> shaarli/shaarli /usr/bin/supervisor 5 minutes ago Exited (0) <span class="kw">48</span> seconds ago backstabbing_galileo | 213 | <span class="ex">d40b7af693d6</span> shaarli/shaarli /usr/bin/supervisor 5 minutes ago Exited (0) <span class="ex">48</span> seconds ago backstabbing_galileo |
216 | 214 | ||
217 | <span class="co"># destroy the container</span> | 215 | <span class="co"># destroy the container</span> |
218 | $ <span class="kw">docker</span> rm backstabbing_galileo <span class="co"># let's put an end to these barbarian practices</span> | 216 | $ <span class="ex">docker</span> rm backstabbing_galileo <span class="co"># let's put an end to these barbarian practices</span> |
219 | <span class="kw">backstabbing_galileo</span> | 217 | <span class="ex">backstabbing_galileo</span> |
220 | 218 | ||
221 | $ <span class="kw">docker</span> ps -a | 219 | $ <span class="ex">docker</span> ps -a |
222 | <span class="kw">CONTAINER</span> ID IMAGE COMMAND CREATED STATUS PORTS NAMES</code></pre></div> | 220 | <span class="ex">CONTAINER</span> ID IMAGE COMMAND CREATED STATUS PORTS NAMES</code></pre></div> |
223 | <h2 id="resources">Resources</h2> | 221 | <h2 id="resources">Resources</h2> |
224 | <h3 id="docker-1">Docker</h3> | 222 | <h3 id="docker-1">Docker</h3> |
225 | <ul> | 223 | <ul> |
diff --git a/doc/Download-CSS-styles-from-an-OPML-list.html b/doc/Download-CSS-styles-from-an-OPML-list.html index 22771502..a4f68ac6 100644 --- a/doc/Download-CSS-styles-from-an-OPML-list.html +++ b/doc/Download-CSS-styles-from-an-OPML-list.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
@@ -209,8 +207,8 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
209 | 207 | ||
210 | <span class="co">/**</span> | 208 | <span class="co">/**</span> |
211 | <span class="co"> * Reading directory list, courtesy of http://www.laughing-buddha.net/php/dirlist/</span> | 209 | <span class="co"> * Reading directory list, courtesy of http://www.laughing-buddha.net/php/dirlist/</span> |
212 | <span class="co"> * </span><span class="kw">@param</span><span class="co"> </span><span class="kw">directory</span><span class="co"> the directory we want to list files of</span> | 210 | <span class="co"> * </span><span class="an">@param</span><span class="co"> </span><span class="cv">directory</span><span class="co"> the directory we want to list files of</span> |
213 | <span class="co"> * </span><span class="kw">@return</span><span class="co"> a simple array containing the list of absolute file paths. Notice that current file (".") and parent one("..")</span> | 211 | <span class="co"> * </span><span class="an">@return</span><span class="co"> a simple array containing the list of absolute file paths. Notice that current file (".") and parent one("..")</span> |
214 | <span class="co"> * are not listed here</span> | 212 | <span class="co"> * are not listed here</span> |
215 | <span class="co"> */</span> | 213 | <span class="co"> */</span> |
216 | <span class="kw">function</span> getDirectoryList <span class="ot">(</span><span class="kw">$directory</span><span class="ot">)</span> { | 214 | <span class="kw">function</span> getDirectoryList <span class="ot">(</span><span class="kw">$directory</span><span class="ot">)</span> { |
diff --git a/doc/Download.html b/doc/Download-and-Installation.html index 9f9f5117..b9cac360 100644 --- a/doc/Download.html +++ b/doc/Download-and-Installation.html | |||
@@ -4,7 +4,7 @@ | |||
4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
5 | <meta name="generator" content="pandoc"> | 5 | <meta name="generator" content="pandoc"> |
6 | <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> | 6 | <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> |
7 | <title>Shaarli – Download</title> | 7 | <title>Shaarli – Download and Installation</title> |
8 | <style type="text/css">code{white-space: pre;}</style> | 8 | <style type="text/css">code{white-space: pre;}</style> |
9 | <style type="text/css"> | 9 | <style type="text/css"> |
10 | div.sourceCode { overflow-x: auto; } | 10 | div.sourceCode { overflow-x: auto; } |
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,27 +96,75 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
104 | <h1 id="download">Download</h1> | 102 | <h1 id="download-and-installation">Download and Installation</h1> |
105 | <h2 id="get-shaarli">Get Shaarli!</h2> | 103 | <h1 id="get-shaarli">Get Shaarli!</h1> |
106 | <h3 id="latest-stable-revision">Latest stable revision</h3> | 104 | <p>To install Shaarli, simply place the files in a directory under your webserver's Document Root (or directly at the document root). Make sure your <a href="Server-requirements">server</a> is properly <a href="Server-configuration">configured</a>.<a href=".html"></a></p> |
107 | <p>This revision has been <a href="https://github.com/shaarli/Shaarli/releases">released</a> and tested.<a href=".html"></a></p> | 105 | <p>Several releases are available:</p> |
108 | <h4 id="clone-with-git-recommended">Clone with Git (recommended)</h4> | 106 | <hr /> |
109 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">git</span> clone https://github.com/shaarli/Shaarli.git -b stable shaarli</code></pre></div> | 107 | <h2 id="latest-release-recommended">Latest release (recommended)</h2> |
110 | <h4 id="download-as-an-archive">Download as an archive</h4> | 108 | <h3 id="download-as-an-archive">Download as an archive</h3> |
111 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">wget</span> https://github.com/shaarli/Shaarli/archive/stable.zip | 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 | $ <span class="kw">unzip</span> stable.zip | 110 | <p><strong>Download our <em>shaarli-full</em> archive</strong> to include dependencies.</p> |
113 | $ <span class="kw">mv</span> Shaarli-stable shaarli</code></pre></div> | 111 | <p>The current latest released version is <code>v0.8.0</code></p> |
114 | <p>Tarballs are also available:</p> | 112 | <p>Or in command lines:</p> |
115 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">wget</span> https://github.com/shaarli/Shaarli/archive/stable.tar.gz | 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 |
116 | $ <span class="kw">tar</span> xvf stable.tar.gz | 114 | $ <span class="fu">unzip</span> shaarli-v0.8.0-full.zip |
117 | $ <span class="kw">mv</span> Shaarli-stable shaarli</code></pre></div> | 115 | $ <span class="fu">mv</span> Shaarli /path/to/shaarli/</code></pre></div> |
118 | <h3 id="development-mainline">Development (mainline)</h3> | 116 | <table style="width:46%;"> |
117 | <colgroup> | ||
118 | <col style="width: 8%" /> | ||
119 | <col style="width: 37%" /> | ||
120 | </colgroup> | ||
121 | <thead> | ||
122 | <tr class="header"> | ||
123 | <th>!</th> | ||
124 | <th>In most cases, download Shaarli from the <a href="https://github.com/shaarli/Shaarli/releases">releases</a> page. Cloning using <code>git</code> or downloading Github branches as zip files requires additional steps (see below).</th> | ||
125 | </tr> | ||
126 | </thead> | ||
127 | <tbody> | ||
128 | </tbody> | ||
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> | ||
134 | <hr /> | ||
135 | <h2 id="stable-version">Stable version</h2> | ||
136 | <p>The stable version has been experienced by Shaarli users, and will receive security updates.</p> | ||
137 | <h3 id="download-as-an-archive-1">Download as an archive</h3> | ||
138 | <p>As a .zip archive:</p> | ||
139 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">wget</span> https://github.com/shaarli/Shaarli/archive/stable.zip | ||
140 | $ <span class="fu">unzip</span> stable.zip | ||
141 | $ <span class="fu">mv</span> Shaarli-stable /path/to/shaarli/</code></pre></div> | ||
142 | <p>As a .tar.gz archive :</p> | ||
143 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">wget</span> https://github.com/shaarli/Shaarli/archive/stable.tar.gz | ||
144 | $ <span class="fu">tar</span> xvf stable.tar.gz | ||
145 | $ <span class="fu">mv</span> Shaarli-stable /path/to/shaarli/</code></pre></div> | ||
146 | <h3 id="clone-with-git">Clone with Git</h3> | ||
147 | <p><a href="https://getcomposer.org/">Composer</a> is required to build a functional Shaarli installation when pulling from git.<a href=".html"></a></p> | ||
148 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">git</span> clone https://github.com/shaarli/Shaarli.git -b stable /path/to/shaarli/ | ||
149 | <span class="co"># install/update third-party dependencies</span> | ||
150 | $ <span class="bu">cd</span> /path/to/shaarli/ | ||
151 | $ <span class="ex">composer</span> update --no-dev</code></pre></div> | ||
152 | <hr /> | ||
153 | <h2 id="development-version-mainline">Development version (mainline)</h2> | ||
119 | <p><em>Use at your own risk!</em></p> | 154 | <p><em>Use at your own risk!</em></p> |
120 | <p>To get the latest changes:</p> | 155 | <p>To get the latest changes from the <code>master</code> branch:</p> |
121 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">git</span> clone https://github.com/shaarli/Shaarli.git shaarli</code></pre></div> | 156 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># clone the repository </span> |
157 | $ <span class="fu">git</span> clone https://github.com/shaarli/Shaarli.git master /path/to/shaarli/ | ||
158 | <span class="co"># install/update third-party dependencies</span> | ||
159 | $ <span class="bu">cd</span> /path/to/shaarli | ||
160 | $ <span class="ex">composer</span> update --no-dev</code></pre></div> | ||
161 | <hr /> | ||
162 | <h2 id="finish-installation">Finish Installation</h2> | ||
163 | <p>Once Shaarli is downloaded and files have been placed at the correct location, open it this location your favorite browser.</p> | ||
164 | <p><img src="http://i.imgur.com/wuMpDSN.png" alt="install screenshot" /><a href=".html"></a></p> | ||
165 | <p>Setup your Shaarli installation, and it's ready to use!</p> | ||
166 | <hr /> | ||
167 | <h2 id="updating-shaarli">Updating Shaarli</h2> | ||
168 | <p>See <a href="Upgrade-and-migration">Upgrade and Migration</a><a href=".html"></a></p> | ||
122 | </body> | 169 | </body> |
123 | </html> | 170 | </html> |
diff --git a/doc/Download-and-Installation.md b/doc/Download-and-Installation.md new file mode 100644 index 00000000..32df8984 --- /dev/null +++ b/doc/Download-and-Installation.md | |||
@@ -0,0 +1,102 @@ | |||
1 | #Download and Installation | ||
2 | # Get Shaarli! | ||
3 | |||
4 | To install Shaarli, simply place the files in a directory under your webserver's Document Root (or directly at the document root). Make sure your [server](Server-requirements) is properly [configured](Server-configuration).[](.html) | ||
5 | |||
6 | Several releases are available: | ||
7 | |||
8 | -------------------------------------------------------- | ||
9 | |||
10 | ## Latest release (recommended) | ||
11 | ### Download as an archive | ||
12 | Get the latest released version from the [releases](https://github.com/shaarli/Shaarli/releases) page.[](.html) | ||
13 | |||
14 | **Download our *shaarli-full* archive** to include dependencies. | ||
15 | |||
16 | The current latest released version is `v0.8.0` | ||
17 | |||
18 | Or in command lines: | ||
19 | |||
20 | ```bash | ||
21 | $ wget https://github.com/shaarli/Shaarli/releases/download/v0.8.0/shaarli-v0.8.0-full.zip | ||
22 | $ unzip shaarli-v0.8.0-full.zip | ||
23 | $ mv Shaarli /path/to/shaarli/ | ||
24 | ``` | ||
25 | |||
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) | ||
27 | |-----|--------------------------| | ||
28 | |||
29 | ### Using git | ||
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 | ``` | ||
36 | |||
37 | -------------------------------------------------------- | ||
38 | |||
39 | ## Stable version | ||
40 | |||
41 | The stable version has been experienced by Shaarli users, and will receive security updates. | ||
42 | |||
43 | ### Download as an archive | ||
44 | |||
45 | As a .zip archive: | ||
46 | |||
47 | ```bash | ||
48 | $ wget https://github.com/shaarli/Shaarli/archive/stable.zip | ||
49 | $ unzip stable.zip | ||
50 | $ mv Shaarli-stable /path/to/shaarli/ | ||
51 | ``` | ||
52 | |||
53 | As a .tar.gz archive : | ||
54 | |||
55 | ```bash | ||
56 | $ wget https://github.com/shaarli/Shaarli/archive/stable.tar.gz | ||
57 | $ tar xvf stable.tar.gz | ||
58 | $ mv Shaarli-stable /path/to/shaarli/ | ||
59 | ``` | ||
60 | |||
61 | ### Clone with Git | ||
62 | |||
63 | [Composer](https://getcomposer.org/) is required to build a functional Shaarli installation when pulling from git.[](.html) | ||
64 | |||
65 | ```bash | ||
66 | $ git clone https://github.com/shaarli/Shaarli.git -b stable /path/to/shaarli/ | ||
67 | # install/update third-party dependencies | ||
68 | $ cd /path/to/shaarli/ | ||
69 | $ composer update --no-dev | ||
70 | ``` | ||
71 | |||
72 | -------------------------------------------------------- | ||
73 | |||
74 | ## Development version (mainline) | ||
75 | |||
76 | _Use at your own risk!_ | ||
77 | |||
78 | To get the latest changes from the `master` branch: | ||
79 | |||
80 | ```bash | ||
81 | # clone the repository | ||
82 | $ git clone https://github.com/shaarli/Shaarli.git master /path/to/shaarli/ | ||
83 | # install/update third-party dependencies | ||
84 | $ cd /path/to/shaarli | ||
85 | $ composer update --no-dev | ||
86 | ``` | ||
87 | |||
88 | -------------------------------------------------------- | ||
89 | |||
90 | ## Finish Installation | ||
91 | |||
92 | Once Shaarli is downloaded and files have been placed at the correct location, open it this location your favorite browser. | ||
93 | |||
94 | [](.html) | ||
95 | |||
96 | Setup your Shaarli installation, and it's ready to use! | ||
97 | |||
98 | -------------------------------------------------------- | ||
99 | |||
100 | ## Updating Shaarli | ||
101 | |||
102 | See [Upgrade and Migration](Upgrade-and-migration)[](.html) | ||
diff --git a/doc/Download.md b/doc/Download.md deleted file mode 100644 index 7930f541..00000000 --- a/doc/Download.md +++ /dev/null | |||
@@ -1,31 +0,0 @@ | |||
1 | #Download | ||
2 | ## Get Shaarli! | ||
3 | ### Latest stable revision | ||
4 | This revision has been [released](https://github.com/shaarli/Shaarli/releases) and tested.[](.html) | ||
5 | |||
6 | #### Clone with Git (recommended) | ||
7 | ```bash | ||
8 | $ git clone https://github.com/shaarli/Shaarli.git -b stable shaarli | ||
9 | ``` | ||
10 | |||
11 | #### Download as an archive | ||
12 | ```bash | ||
13 | $ wget https://github.com/shaarli/Shaarli/archive/stable.zip | ||
14 | $ unzip stable.zip | ||
15 | $ mv Shaarli-stable shaarli | ||
16 | ``` | ||
17 | |||
18 | Tarballs are also available: | ||
19 | ```bash | ||
20 | $ wget https://github.com/shaarli/Shaarli/archive/stable.tar.gz | ||
21 | $ tar xvf stable.tar.gz | ||
22 | $ mv Shaarli-stable shaarli | ||
23 | ``` | ||
24 | |||
25 | ### Development (mainline) | ||
26 | _Use at your own risk!_ | ||
27 | |||
28 | To get the latest changes: | ||
29 | ```bash | ||
30 | $ git clone https://github.com/shaarli/Shaarli.git shaarli | ||
31 | ``` | ||
diff --git a/doc/Example-patch---add-new-via-field-for-links.html b/doc/Example-patch---add-new-via-field-for-links.html index 7db43107..133224e2 100644 --- a/doc/Example-patch---add-new-via-field-for-links.html +++ b/doc/Example-patch---add-new-via-field-for-links.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
diff --git a/doc/FAQ.html b/doc/FAQ.html index 3b6b956d..61f3475f 100644 --- a/doc/FAQ.html +++ b/doc/FAQ.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
diff --git a/doc/Firefox-share.html b/doc/Firefox-share.html index add6d4e8..d7dcc282 100644 --- a/doc/Firefox-share.html +++ b/doc/Firefox-share.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
diff --git a/doc/GnuPG-signature.html b/doc/GnuPG-signature.html index c431f9ad..50b904d5 100644 --- a/doc/GnuPG-signature.html +++ b/doc/GnuPG-signature.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
@@ -129,26 +127,26 @@ Keys</a></li> | |||
129 | <li><a href="https://help.github.com/articles/generating-a-gpg-key/">Generating a GPG key</a> (GitHub)<a href=".html"></a></li> | 127 | <li><a href="https://help.github.com/articles/generating-a-gpg-key/">Generating a GPG key</a> (GitHub)<a href=".html"></a></li> |
130 | </ul> | 128 | </ul> |
131 | <h3 id="gpg---provide-identity-information">gpg - provide identity information</h3> | 129 | <h3 id="gpg---provide-identity-information">gpg - provide identity information</h3> |
132 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">gpg</span> --gen-key | 130 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">gpg</span> --gen-key |
133 | 131 | ||
134 | <span class="kw">gpg</span> (GnuPG) <span class="kw">2.1.6;</span> <span class="kw">Copyright</span> (C) <span class="kw">2015</span> Free Software Foundation, Inc. | 132 | <span class="ex">gpg</span> (GnuPG) <span class="ex">2.1.6</span><span class="kw">;</span> <span class="ex">Copyright</span> (C) <span class="ex">2015</span> Free Software Foundation, Inc. |
135 | <span class="kw">This</span> is free software: you are free to change and redistribute it. | 133 | <span class="ex">This</span> is free software: you are free to change and redistribute it. |
136 | <span class="kw">There</span> is NO WARRANTY, to the extent permitted by law. | 134 | <span class="ex">There</span> is NO WARRANTY, to the extent permitted by law. |
137 | 135 | ||
138 | <span class="kw">Note</span>: Use <span class="st">"gpg2 --full-gen-key"</span> for a full featured key generation dialog. | 136 | <span class="ex">Note</span>: Use <span class="st">"gpg2 --full-gen-key"</span> for a full featured key generation dialog. |
139 | 137 | ||
140 | <span class="kw">GnuPG</span> needs to construct a user ID to identify your key. | 138 | <span class="ex">GnuPG</span> needs to construct a user ID to identify your key. |
141 | 139 | ||
142 | <span class="kw">Real</span> name: Marvin the Paranoid Android | 140 | <span class="ex">Real</span> name: Marvin the Paranoid Android |
143 | <span class="kw">Email</span> address: marvin@h2g2.net | 141 | <span class="ex">Email</span> address: marvin@h2g2.net |
144 | <span class="kw">You</span> selected this USER-ID: | 142 | <span class="ex">You</span> selected this USER-ID: |
145 | <span class="st">"Marvin the Paranoid Android <marvin@h2g2.net>"</span> | 143 | <span class="st">"Marvin the Paranoid Android <marvin@h2g2.net>"</span> |
146 | 144 | ||
147 | <span class="kw">Change</span> (N)<span class="kw">ame</span>, (E)<span class="kw">mail</span>, or (O)<span class="kw">kay</span>/<span class="kw">(Q)uit?</span> o | 145 | <span class="ex">Change</span> (N)<span class="ex">ame</span>, (E)<span class="ex">mail</span>, or (O)<span class="ex">kay</span>/<span class="kw">(</span><span class="ex">Q</span><span class="kw">)</span><span class="ex">uit?</span> o |
148 | <span class="kw">We</span> need to generate a lot of random bytes. It is a good idea to perform | 146 | <span class="ex">We</span> need to generate a lot of random bytes. It is a good idea to perform |
149 | <span class="kw">some</span> other action (type on the keyboard, move the mouse, utilize the | 147 | <span class="ex">some</span> other action (type on the keyboard, move the mouse, utilize the |
150 | <span class="kw">disks</span>) <span class="kw">during</span> the prime generation<span class="kw">;</span> <span class="kw">this</span> gives the random number | 148 | <span class="ex">disks</span>) <span class="ex">during</span> the prime generation<span class="kw">;</span> <span class="ex">this</span> gives the random number |
151 | <span class="kw">generator</span> a better chance to gain enough entropy.</code></pre></div> | 149 | <span class="ex">generator</span> a better chance to gain enough entropy.</code></pre></div> |
152 | <h3 id="gpg---entropy-interlude">gpg - entropy interlude</h3> | 150 | <h3 id="gpg---entropy-interlude">gpg - entropy interlude</h3> |
153 | <p>At this point, you will:</p> | 151 | <p>At this point, you will:</p> |
154 | <ul> | 152 | <ul> |
@@ -156,19 +154,19 @@ Keys</a></li> | |||
156 | <li>be asked to use your machine's input devices (mouse, keyboard, etc.) to generate random entropy; this step <em>may take some time</em></li> | 154 | <li>be asked to use your machine's input devices (mouse, keyboard, etc.) to generate random entropy; this step <em>may take some time</em></li> |
157 | </ul> | 155 | </ul> |
158 | <h3 id="gpg---key-creation-confirmation">gpg - key creation confirmation</h3> | 156 | <h3 id="gpg---key-creation-confirmation">gpg - key creation confirmation</h3> |
159 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="kw">gpg</span>: key A9D53A3E marked as ultimately trusted | 157 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gpg</span>: key A9D53A3E marked as ultimately trusted |
160 | <span class="kw">public</span> and secret key created and signed. | 158 | <span class="ex">public</span> and secret key created and signed. |
161 | 159 | ||
162 | <span class="kw">gpg</span>: checking the trustdb | 160 | <span class="ex">gpg</span>: checking the trustdb |
163 | <span class="kw">gpg</span>: 3 marginal(s) <span class="kw">needed</span>, 1 complete(s) <span class="kw">needed</span>, PGP trust model | 161 | <span class="ex">gpg</span>: 3 marginal(s) <span class="ex">needed</span>, 1 complete(s) <span class="ex">needed</span>, PGP trust model |
164 | <span class="kw">gpg</span>: depth: 0 valid: 2 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 2u | 162 | <span class="ex">gpg</span>: depth: 0 valid: 2 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 2u |
165 | <span class="kw">pub</span> rsa2048/A9D53A3E 2015-07-31 | 163 | <span class="ex">pub</span> rsa2048/A9D53A3E 2015-07-31 |
166 | <span class="kw">Key</span> fingerprint = AF2A 5381 E54B 2FD2 14C4 A9A3 0E35 ACA4 A9D5 3A3E | 164 | <span class="ex">Key</span> fingerprint = AF2A 5381 E54B 2FD2 14C4 A9A3 0E35 ACA4 A9D5 3A3E |
167 | <span class="kw">uid</span> [ultimate] Marvin the Paranoid Android <span class="kw"><</span>marvin@h2g2.net<span class="kw">></span>[](.html) | 165 | <span class="ex">uid</span> [ultimate] Marvin the Paranoid Android <span class="op"><</span>marvin@h2g2.net<span class="op">></span>[](.html) |
168 | <span class="kw">sub</span> rsa2048/8C0EACF1 2015-07-31</code></pre></div> | 166 | <span class="ex">sub</span> rsa2048/8C0EACF1 2015-07-31</code></pre></div> |
169 | <h3 id="gpg---submit-your-public-key-to-a-pgp-server-optional">gpg - submit your public key to a PGP server (Optional)</h3> | 167 | <h3 id="gpg---submit-your-public-key-to-a-pgp-server-optional">gpg - submit your public key to a PGP server (Optional)</h3> |
170 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">gpg</span> --keyserver pgp.mit.edu --send-keys A9D53A3E | 168 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">gpg</span> --keyserver pgp.mit.edu --send-keys A9D53A3E |
171 | <span class="kw">gpg</span>: sending key A9D53A3E to hkp server pgp.mit.edu</code></pre></div> | 169 | <span class="ex">gpg</span>: sending key A9D53A3E to hkp server pgp.mit.edu</code></pre></div> |
172 | <h2 id="create-and-push-a-gpg-signed-tag">Create and push a GPG-signed tag</h2> | 170 | <h2 id="create-and-push-a-gpg-signed-tag">Create and push a GPG-signed tag</h2> |
173 | <p>See <a href="Release-Shaarli.html">Release Shaarli</a>.</p> | 171 | <p>See <a href="Release-Shaarli.html">Release Shaarli</a>.</p> |
174 | </body> | 172 | </body> |
diff --git a/doc/Home.html b/doc/Home.html index 442503c5..970f547e 100644 --- a/doc/Home.html +++ b/doc/Home.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
diff --git a/doc/Plugin-System.html b/doc/Plugin-System.html index 37b26152..655536c6 100644 --- a/doc/Plugin-System.html +++ b/doc/Plugin-System.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
diff --git a/doc/Plugins.html b/doc/Plugins.html index e7df6aed..435a836f 100644 --- a/doc/Plugins.html +++ b/doc/Plugins.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
diff --git a/doc/RSS-feeds.html b/doc/RSS-feeds.html index 1b38e4e8..0f332b3d 100644 --- a/doc/RSS-feeds.html +++ b/doc/RSS-feeds.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
diff --git a/doc/Release-Shaarli.html b/doc/Release-Shaarli.html index cfaa663b..0d9fa3e1 100644 --- a/doc/Release-Shaarli.html +++ b/doc/Release-Shaarli.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,14 +96,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
104 | <h1 id="release-shaarli">Release Shaarli</h1> | 102 | <h1 id="release-shaarli">Release Shaarli</h1> |
105 | <p>See <a href="http://git-scm.com/book/en/v2/Distributed-Git-Maintaining-a-Project#Tagging-Your-Releases">Git - Maintaining a project - Tagging your [](.html)<br /> | 103 | <p>See <a href="http://git-scm.com/book/en/v2/Distributed-Git-Maintaining-a-Project#Tagging-Your-Releases">Git - Maintaining a project - Tagging your [](.html)<br /> |
106 | releases</a>.</p> | 104 | releases</a>.</p> |
107 | <h3 id="prerequisites">Prerequisites</h3> | 105 | <h2 id="prerequisites">Prerequisites</h2> |
108 | <p>This guide assumes that you have:</p> | 106 | <p>This guide assumes that you have:</p> |
109 | <ul> | 107 | <ul> |
110 | <li>a GPG key matching your GitHub authentication credentials | 108 | <li>a GPG key matching your GitHub authentication credentials |
@@ -117,54 +115,106 @@ releases</a>.</p> | |||
117 | <li><code>origin</code> pointing to your GitHub fork</li> | 115 | <li><code>origin</code> pointing to your GitHub fork</li> |
118 | <li><code>upstream</code> pointing to the main Shaarli repository</li> | 116 | <li><code>upstream</code> pointing to the main Shaarli repository</li> |
119 | </ul></li> | 117 | </ul></li> |
120 | <li>maintainer permissions on the main Shaarli repository (to push the signed tag)</li> | 118 | <li>maintainer permissions on the main Shaarli repository, to: |
121 | <li><a href="http://pandoc.org/">Pandoc</a> needs to be installed.<a href=".html"></a></li> | 119 | <ul> |
120 | <li>push the signed tag</li> | ||
121 | <li>create a new release</li> | ||
122 | </ul></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> | ||
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> | ||
122 | </ul> | 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> | ||
147 | <h2 id="increment-the-version-code-create-and-push-a-signed-tag">Increment the version code, create and push a signed tag</h2> | ||
123 | <h3 id="bump-shaarlis-version">Bump Shaarli's version</h3> | 148 | <h3 id="bump-shaarlis-version">Bump Shaarli's version</h3> |
124 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">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 |
125 | 150 | ||
126 | <span class="co"># create a new branch</span> | 151 | <span class="co"># create a new branch</span> |
127 | $ <span class="kw">git</span> fetch upstream | 152 | $ <span class="fu">git</span> fetch upstream |
128 | $ <span class="kw">git</span> checkout upstream/master -b v0.5.0 | 153 | $ <span class="fu">git</span> checkout upstream/master -b v0.5.0 |
129 | 154 | ||
130 | <span class="co"># bump the version number</span> | 155 | <span class="co"># bump the version number</span> |
131 | $ <span class="kw">vim</span> index.php shaarli_version.php | 156 | $ <span class="ex">vim</span> index.php shaarli_version.php |
132 | 157 | ||
133 | <span class="co"># rebuild the documentation from the wiki</span> | 158 | <span class="co"># rebuild the documentation from the wiki</span> |
134 | $ <span class="kw">make</span> htmldoc | 159 | $ <span class="fu">make</span> htmldoc |
135 | 160 | ||
136 | <span class="co"># commit the changes</span> | 161 | <span class="co"># commit the changes</span> |
137 | $ <span class="kw">git</span> add index.php shaarli_version.php doc | 162 | $ <span class="fu">git</span> add index.php shaarli_version.php doc |
138 | $ <span class="kw">git</span> commit -s -m <span class="st">"Bump version to v0.5.0"</span> | 163 | $ <span class="fu">git</span> commit -s -m <span class="st">"Bump version to v0.5.0"</span> |
139 | 164 | ||
140 | <span class="co"># push the commit on your GitHub fork</span> | 165 | <span class="co"># push the commit on your GitHub fork</span> |
141 | $ <span class="kw">git</span> push origin v0.5.0</code></pre></div> | 166 | $ <span class="fu">git</span> push origin v0.5.0</code></pre></div> |
142 | <h3 id="create-and-merge-a-pull-request">Create and merge a Pull Request</h3> | 167 | <h3 id="create-and-merge-a-pull-request">Create and merge a Pull Request</h3> |
143 | <p>This one is pretty straightforward ;-)</p> | 168 | <p>This one is pretty straightforward ;-)</p> |
144 | <h3 id="create-and-push-a-signed-tag">Create and push a signed tag</h3> | 169 | <h3 id="create-and-push-a-signed-tag">Create and push a signed tag</h3> |
145 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># update your local copy</span> | 170 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># update your local copy</span> |
146 | $ <span class="kw">git</span> checkout master | 171 | $ <span class="fu">git</span> checkout master |
147 | $ <span class="kw">git</span> fetch upstream | 172 | $ <span class="fu">git</span> fetch upstream |
148 | $ <span class="kw">git</span> pull upstream master | 173 | $ <span class="fu">git</span> pull upstream master |
149 | 174 | ||
150 | <span class="co"># create a signed tag</span> | 175 | <span class="co"># create a signed tag</span> |
151 | $ <span class="kw">git</span> tag -s -m <span class="st">"Release v0.5.0"</span> v0.5.0 | 176 | $ <span class="fu">git</span> tag -s -m <span class="st">"Release v0.5.0"</span> v0.5.0 |
152 | 177 | ||
153 | <span class="co"># push it to "upstream"</span> | 178 | <span class="co"># push it to "upstream"</span> |
154 | $ <span class="kw">git</span> push --tags upstream</code></pre></div> | 179 | $ <span class="fu">git</span> push --tags upstream</code></pre></div> |
155 | <h3 id="verify-a-signed-tag">Verify a signed tag</h3> | 180 | <h3 id="verify-a-signed-tag">Verify a signed tag</h3> |
156 | <p><a href="https://github.com/shaarli/Shaarli/releases/tag/v0.5.0"><code>v0.5.0</code></a> is the first GPG-signed tag pushed on the Community Shaarli.<a href=".html"></a></p> | 181 | <p><a href="https://github.com/shaarli/Shaarli/releases/tag/v0.5.0"><code>v0.5.0</code></a> is the first GPG-signed tag pushed on the Community Shaarli.<a href=".html"></a></p> |
157 | <p>Let's have a look at its signature!</p> | 182 | <p>Let's have a look at its signature!</p> |
158 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">cd</span> /path/to/shaarli | 183 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="bu">cd</span> /path/to/shaarli |
159 | $ <span class="kw">git</span> fetch upstream | 184 | $ <span class="fu">git</span> fetch upstream |
160 | 185 | ||
161 | <span class="co"># get the SHA1 reference of the tag</span> | 186 | <span class="co"># get the SHA1 reference of the tag</span> |
162 | $ <span class="kw">git</span> show-ref tags/v0.5.0 | 187 | $ <span class="fu">git</span> show-ref tags/v0.5.0 |
163 | <span class="kw">f7762cf803f03f5caf4b8078359a63783d0090c1</span> refs/tags/v0.5.0 | 188 | <span class="ex">f7762cf803f03f5caf4b8078359a63783d0090c1</span> refs/tags/v0.5.0 |
164 | 189 | ||
165 | <span class="co"># verify the tag signature information</span> | 190 | <span class="co"># verify the tag signature information</span> |
166 | $ <span class="kw">git</span> verify-tag f7762cf803f03f5caf4b8078359a63783d0090c1 | 191 | $ <span class="fu">git</span> verify-tag f7762cf803f03f5caf4b8078359a63783d0090c1 |
167 | <span class="kw">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 |
168 | <span class="kw">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> |
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> | ||
204 | <p>Users with a shared hosting may have:</p> | ||
205 | <ul> | ||
206 | <li>no SSH access</li> | ||
207 | <li>no possibility to install PHP packages or server extensions</li> | ||
208 | <li>no possibility to run scripts</li> | ||
209 | </ul> | ||
210 | <p>To ease Shaarli installations, it is possible to generate and upload additional release archives,<br /> | ||
211 | that will contain Shaarli code plus all required third-party libraries:</p> | ||
212 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">make</span> release_archive</code></pre></div> | ||
213 | <p>This will create the following archives:</p> | ||
214 | <ul> | ||
215 | <li><code>shaarli-vX.Y.Z-full.tar</code></li> | ||
216 | <li><code>shaarli-vX.Y.Z-full.zip</code></li> | ||
217 | </ul> | ||
218 | <p>The archives need to be manually uploaded on the previously created GitHub release.</p> | ||
169 | </body> | 219 | </body> |
170 | </html> | 220 | </html> |
diff --git a/doc/Release-Shaarli.md b/doc/Release-Shaarli.md index d5044fe9..556a96ee 100644 --- a/doc/Release-Shaarli.md +++ b/doc/Release-Shaarli.md | |||
@@ -2,7 +2,7 @@ | |||
2 | See [Git - Maintaining a project - Tagging your [](.html) | 2 | See [Git - Maintaining a project - Tagging your [](.html) |
3 | releases](http://git-scm.com/book/en/v2/Distributed-Git-Maintaining-a-Project#Tagging-Your-Releases). | 3 | releases](http://git-scm.com/book/en/v2/Distributed-Git-Maintaining-a-Project#Tagging-Your-Releases). |
4 | 4 | ||
5 | ### Prerequisites | 5 | ## Prerequisites |
6 | This guide assumes that you have: | 6 | This guide assumes that you have: |
7 | - a GPG key matching your GitHub authentication credentials | 7 | - a GPG key matching your GitHub authentication credentials |
8 | - i.e., the email address identified by the GPG key is the same as the one in your `~/.gitconfig` | 8 | - i.e., the email address identified by the GPG key is the same as the one in your `~/.gitconfig` |
@@ -10,9 +10,40 @@ 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 | - [Pandoc](http://pandoc.org/) needs to be installed.[](.html) | 14 | - push the signed tag |
15 | - create a new release | ||
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 | |||
46 | ## Increment the version code, create and push a signed tag | ||
16 | ### Bump Shaarli's version | 47 | ### Bump Shaarli's version |
17 | ```bash | 48 | ```bash |
18 | $ cd /path/to/shaarli | 49 | $ cd /path/to/shaarli |
@@ -70,3 +101,30 @@ $ git verify-tag f7762cf803f03f5caf4b8078359a63783d0090c1 | |||
70 | gpg: Signature made Thu 30 Jul 2015 11:46:34 CEST using RSA key ID 4100DF6F | 101 | gpg: Signature made Thu 30 Jul 2015 11:46:34 CEST using RSA key ID 4100DF6F |
71 | gpg: Good signature from "VirtualTam <virtualtam@flibidi.net>" [ultimate][](.html) | 102 | gpg: Good signature from "VirtualTam <virtualtam@flibidi.net>" [ultimate][](.html) |
72 | ``` | 103 | ``` |
104 | |||
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 | ||
114 | Users with a shared hosting may have: | ||
115 | - no SSH access | ||
116 | - no possibility to install PHP packages or server extensions | ||
117 | - no possibility to run scripts | ||
118 | |||
119 | To ease Shaarli installations, it is possible to generate and upload additional release archives, | ||
120 | that will contain Shaarli code plus all required third-party libraries: | ||
121 | |||
122 | ```bash | ||
123 | $ make release_archive | ||
124 | ``` | ||
125 | |||
126 | This will create the following archives: | ||
127 | - `shaarli-vX.Y.Z-full.tar` | ||
128 | - `shaarli-vX.Y.Z-full.zip` | ||
129 | |||
130 | The archives need to be manually uploaded on the previously created GitHub release. | ||
diff --git a/doc/Security.html b/doc/Security.html index b1969a4c..cec20590 100644 --- a/doc/Security.html +++ b/doc/Security.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
diff --git a/doc/Server-configuration.html b/doc/Server-configuration.html index 1d2276df..2f1c25b5 100644 --- a/doc/Server-configuration.html +++ b/doc/Server-configuration.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
@@ -133,7 +131,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
133 | <p>See also <a href="https://github.com/shaarli/Shaarli/issues?utf8=%E2%9C%93&q=label%3Aproxy+">proxy-related</a> issues.<a href=".html"></a></p> | 131 | <p>See also <a href="https://github.com/shaarli/Shaarli/issues?utf8=%E2%9C%93&q=label%3Aproxy+">proxy-related</a> issues.<a href=".html"></a></p> |
134 | <h2 id="apache">Apache</h2> | 132 | <h2 id="apache">Apache</h2> |
135 | <h3 id="minimal">Minimal</h3> | 133 | <h3 id="minimal">Minimal</h3> |
136 | <div class="sourceCode"><pre class="sourceCode apache"><code class="sourceCode apache"><span class="fu"><VirtualHost</span><span class="ot"> *:80</span><span class="fu">></span> | 134 | <div class="sourceCode"><pre class="sourceCode apache"><code class="sourceCode apache"><span class="fu"><VirtualHost</span><span class="at"> *:80</span><span class="fu">></span> |
137 | ServerName<span class="st"> shaarli.my-domain.org</span> | 135 | ServerName<span class="st"> shaarli.my-domain.org</span> |
138 | DocumentRoot<span class="st"> /absolute/path/to/shaarli/</span> | 136 | DocumentRoot<span class="st"> /absolute/path/to/shaarli/</span> |
139 | <span class="fu"></VirtualHost></span></code></pre></div> | 137 | <span class="fu"></VirtualHost></span></code></pre></div> |
@@ -144,11 +142,11 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
144 | <li><a href="http://stackoverflow.com/q/176">Apache/PHP - error log per VirtualHost</a> (StackOverflow)<a href=".html"></a></li> | 142 | <li><a href="http://stackoverflow.com/q/176">Apache/PHP - error log per VirtualHost</a> (StackOverflow)<a href=".html"></a></li> |
145 | <li><a href="https://ma.ttias.be/php-php_value-vs-php_admin_value-and-the-use-of-php_flag-explained/">PHP: php_value vs php_admin_value and the use of php_flag explained</a><a href=".html"></a></li> | 143 | <li><a href="https://ma.ttias.be/php-php_value-vs-php_admin_value-and-the-use-of-php_flag-explained/">PHP: php_value vs php_admin_value and the use of php_flag explained</a><a href=".html"></a></li> |
146 | </ul> | 144 | </ul> |
147 | <div class="sourceCode"><pre class="sourceCode apache"><code class="sourceCode apache"><span class="fu"><VirtualHost</span><span class="ot"> *:80</span><span class="fu">></span> | 145 | <div class="sourceCode"><pre class="sourceCode apache"><code class="sourceCode apache"><span class="fu"><VirtualHost</span><span class="at"> *:80</span><span class="fu">></span> |
148 | ServerName<span class="st"> shaarli.my-domain.org</span> | 146 | ServerName<span class="st"> shaarli.my-domain.org</span> |
149 | DocumentRoot<span class="st"> /absolute/path/to/shaarli/</span> | 147 | DocumentRoot<span class="st"> /absolute/path/to/shaarli/</span> |
150 | 148 | ||
151 | <span class="ot">LogLevel</span><span class="ch"> </span><span class="kw">warn</span> | 149 | <span class="ex">LogLevel</span><span class="ch"> </span><span class="kw">warn</span> |
152 | ErrorLog<span class="st"> /var/log/apache2/shaarli-error.log</span> | 150 | ErrorLog<span class="st"> /var/log/apache2/shaarli-error.log</span> |
153 | CustomLog<span class="st"> /var/log/apache2/shaarli-access.log combined</span> | 151 | CustomLog<span class="st"> /var/log/apache2/shaarli-access.log combined</span> |
154 | 152 | ||
@@ -158,43 +156,46 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
158 | php_value error_log /var/log/apache2/shaarli-php-error.log | 156 | php_value error_log /var/log/apache2/shaarli-php-error.log |
159 | <span class="fu"></VirtualHost></span></code></pre></div> | 157 | <span class="fu"></VirtualHost></span></code></pre></div> |
160 | <h3 id="standard---keep-access-and-error-logs">Standard - Keep access and error logs</h3> | 158 | <h3 id="standard---keep-access-and-error-logs">Standard - Keep access and error logs</h3> |
161 | <div class="sourceCode"><pre class="sourceCode apache"><code class="sourceCode apache"><span class="fu"><VirtualHost</span><span class="ot"> *:80</span><span class="fu">></span> | 159 | <div class="sourceCode"><pre class="sourceCode apache"><code class="sourceCode apache"><span class="fu"><VirtualHost</span><span class="at"> *:80</span><span class="fu">></span> |
162 | ServerName<span class="st"> shaarli.my-domain.org</span> | 160 | ServerName<span class="st"> shaarli.my-domain.org</span> |
163 | DocumentRoot<span class="st"> /absolute/path/to/shaarli/</span> | 161 | DocumentRoot<span class="st"> /absolute/path/to/shaarli/</span> |
164 | 162 | ||
165 | <span class="ot">LogLevel</span><span class="ch"> </span><span class="kw">warn</span> | 163 | <span class="ex">LogLevel</span><span class="ch"> </span><span class="kw">warn</span> |
166 | ErrorLog<span class="st"> /var/log/apache2/shaarli-error.log</span> | 164 | ErrorLog<span class="st"> /var/log/apache2/shaarli-error.log</span> |
167 | CustomLog<span class="st"> /var/log/apache2/shaarli-access.log combined</span> | 165 | CustomLog<span class="st"> /var/log/apache2/shaarli-access.log combined</span> |
168 | <span class="fu"></VirtualHost></span></code></pre></div> | 166 | <span class="fu"></VirtualHost></span></code></pre></div> |
169 | <h3 id="paranoid---redirect-http-80-to-https-443">Paranoid - Redirect HTTP (:80) to HTTPS (:443)</h3> | 167 | <h3 id="paranoid---redirect-http-80-to-https-443">Paranoid - Redirect HTTP (:80) to HTTPS (:443)</h3> |
170 | <p>See <a href="https://wiki.mozilla.org/Security/Server_Side_TLS#Apache">Server-side TLS</a> (Mozilla).<a href=".html"></a></p> | 168 | <p>See <a href="https://wiki.mozilla.org/Security/Server_Side_TLS#Apache">Server-side TLS</a> (Mozilla).<a href=".html"></a></p> |
171 | <div class="sourceCode"><pre class="sourceCode apache"><code class="sourceCode apache"><span class="fu"><VirtualHost</span><span class="ot"> *:443</span><span class="fu">></span> | 169 | <div class="sourceCode"><pre class="sourceCode apache"><code class="sourceCode apache"><span class="fu"><VirtualHost</span><span class="at"> *:443</span><span class="fu">></span> |
172 | ServerName<span class="st"> shaarli.my-domain.org</span> | 170 | ServerName<span class="st"> shaarli.my-domain.org</span> |
173 | DocumentRoot<span class="st"> /absolute/path/to/shaarli/</span> | 171 | DocumentRoot<span class="st"> /absolute/path/to/shaarli/</span> |
174 | 172 | ||
175 | <span class="ot">SSLEngine</span><span class="ch"> </span><span class="kw">on</span> | 173 | <span class="ex">SSLEngine</span><span class="ch"> </span><span class="kw">on</span> |
176 | SSLCertificateFile<span class="st"> /absolute/path/to/the/website/certificate.pem</span> | 174 | SSLCertificateFile<span class="st"> /absolute/path/to/the/website/certificate.pem</span> |
177 | SSLCertificateKeyFile<span class="st"> /absolute/path/to/the/website/key.key</span> | 175 | SSLCertificateKeyFile<span class="st"> /absolute/path/to/the/website/key.key</span> |
178 | 176 | ||
179 | <span class="fu"><Directory</span><span class="ot"> /absolute/path/to/shaarli/</span><span class="fu">></span> | 177 | <span class="fu"><Directory</span><span class="at"> /absolute/path/to/shaarli/</span><span class="fu">></span> |
180 | <span class="ot">AllowOverride</span><span class="ch"> </span><span class="kw">All</span> | 178 | <span class="ex">AllowOverride</span><span class="ch"> </span><span class="kw">All</span> |
181 | <span class="ot">Options</span><span class="ch"> </span><span class="kw">Indexes</span><span class="ch"> </span><span class="kw">FollowSymLinks</span><span class="ch"> </span><span class="kw">MultiViews</span> | 179 | <span class="ex">Options</span><span class="ch"> </span><span class="kw">Indexes</span><span class="ch"> </span><span class="kw">FollowSymLinks</span><span class="ch"> </span><span class="kw">MultiViews</span> |
182 | <span class="ot">Order</span><span class="ch"> </span><span class="kw">allow,deny</span> | 180 | <span class="ex">Order</span><span class="ch"> </span><span class="kw">allow,deny</span> |
183 | allow<span class="st"> from all</span> | 181 | allow<span class="st"> from all</span> |
184 | <span class="fu"></Directory></span> | 182 | <span class="fu"></Directory></span> |
185 | 183 | ||
186 | <span class="ot">LogLevel</span><span class="ch"> </span><span class="kw">warn</span> | 184 | <span class="ex">LogLevel</span><span class="ch"> </span><span class="kw">warn</span> |
187 | ErrorLog<span class="st"> /var/log/apache2/shaarli-error.log</span> | 185 | ErrorLog<span class="st"> /var/log/apache2/shaarli-error.log</span> |
188 | CustomLog<span class="st"> /var/log/apache2/shaarli-access.log combined</span> | 186 | CustomLog<span class="st"> /var/log/apache2/shaarli-access.log combined</span> |
189 | <span class="fu"></VirtualHost></span> | 187 | <span class="fu"></VirtualHost></span> |
190 | <span class="fu"><VirtualHost</span><span class="ot"> *:80</span><span class="fu">></span> | 188 | <span class="fu"><VirtualHost</span><span class="at"> *:80</span><span class="fu">></span> |
191 | ServerName<span class="st"> shaarli.my-domain.org</span> | 189 | ServerName<span class="st"> shaarli.my-domain.org</span> |
192 | Redirect<span class="st"> 301 / https://shaarli.my-domain.org</span> | 190 | Redirect<span class="st"> 301 / https://shaarli.my-domain.org</span> |
193 | 191 | ||
194 | <span class="ot">LogLevel</span><span class="ch"> </span><span class="kw">warn</span> | 192 | <span class="ex">LogLevel</span><span class="ch"> </span><span class="kw">warn</span> |
195 | ErrorLog<span class="st"> /var/log/apache2/shaarli-error.log</span> | 193 | ErrorLog<span class="st"> /var/log/apache2/shaarli-error.log</span> |
196 | 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> |
197 | <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> | ||
198 | <h2 id="lighthttpd">LightHttpd</h2> | 199 | <h2 id="lighthttpd">LightHttpd</h2> |
199 | <h2 id="nginx">Nginx</h2> | 200 | <h2 id="nginx">Nginx</h2> |
200 | <h3 id="foreword">Foreword</h3> | 201 | <h3 id="foreword">Foreword</h3> |
@@ -235,7 +236,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
235 | <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> |
236 | <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> |
237 | </ul> | 238 | </ul> |
238 | <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> |
239 | <ul> | 240 | <ul> |
240 | <li><code>user:group = john:users</code>,</li> | 241 | <li><code>user:group = john:users</code>,</li> |
241 | </ul> | 242 | </ul> |
@@ -253,6 +254,24 @@ user john users; | |||
253 | http { | 254 | http { |
254 | [...][](.html) | 255 | [...][](.html) |
255 | }</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> | ||
256 | <h3 id="minimal-1">Minimal</h3> | 275 | <h3 id="minimal-1">Minimal</h3> |
257 | <p><em>WARNING: Use for development only!</em></p> | 276 | <p><em>WARNING: Use for development only!</em></p> |
258 | <pre class="nginx"><code>user john users; | 277 | <pre class="nginx"><code>user john users; |
@@ -352,6 +371,11 @@ http { | |||
352 | error_log /var/log/nginx/shaarli.error.log; | 371 | error_log /var/log/nginx/shaarli.error.log; |
353 | } | 372 | } |
354 | 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 | |||
355 | include deny.conf; | 379 | include deny.conf; |
356 | include static_assets.conf; | 380 | include static_assets.conf; |
357 | include php.conf; | 381 | include php.conf; |
@@ -405,15 +429,15 @@ http { | |||
405 | error_log /var/log/nginx/shaarli.error.log; | 429 | error_log /var/log/nginx/shaarli.error.log; |
406 | } | 430 | } |
407 | 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 | |||
408 | include deny.conf; | 437 | include deny.conf; |
409 | include static_assets.conf; | 438 | include static_assets.conf; |
410 | include php.conf; | 439 | include php.conf; |
411 | } | 440 | } |
412 | }</code></pre> | 441 | }</code></pre> |
413 | <h2 id="restricting-search-engines-and-web-crawler-traffic">Restricting search engines and web crawler traffic</h2> | ||
414 | <p>Creating a <code>robots.txt</code> witht he following contents at the root of your Shaarli installation will prevent "honest" web crawlers from indexing each and every link and Daily page from a Shaarli instance, thus getting rid of a certain amount of unsollicited network traffic.</p> | ||
415 | <pre><code>User-agent: * | ||
416 | Disallow: /</code></pre> | ||
417 | <p>See: <a href="http://www.robotstxt.org/" class="uri">http://www.robotstxt.org/</a>, <a href="http://www.robotstxt.org/robotstxt.html" class="uri">http://www.robotstxt.org/robotstxt.html</a>, <a href="http://www.robotstxt.org/meta.html" class="uri">http://www.robotstxt.org/meta.html</a></p> | ||
418 | </body> | 442 | </body> |
419 | </html> | 443 | </html> |
diff --git a/doc/Server-configuration.md b/doc/Server-configuration.md index fd98a608..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,21 +365,14 @@ 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; |
334 | } | 376 | } |
335 | } | 377 | } |
336 | ``` | 378 | ``` |
337 | |||
338 | ## Restricting search engines and web crawler traffic | ||
339 | |||
340 | Creating a `robots.txt` witht he following contents at the root of your Shaarli installation will prevent "honest" web crawlers from indexing each and every link and Daily page from a Shaarli instance, thus getting rid of a certain amount of unsollicited network traffic. | ||
341 | |||
342 | ``` | ||
343 | User-agent: * | ||
344 | Disallow: / | ||
345 | ``` | ||
346 | |||
347 | See: http://www.robotstxt.org/, http://www.robotstxt.org/robotstxt.html, http://www.robotstxt.org/meta.html | ||
348 | |||
diff --git a/doc/Server-requirements.html b/doc/Server-requirements.html index 8e4deeb8..2c2545bb 100644 --- a/doc/Server-requirements.html +++ b/doc/Server-requirements.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
@@ -96,18 +94,18 @@ | |||
96 | </tr> | 94 | </tr> |
97 | <tr class="odd"> | 95 | <tr class="odd"> |
98 | <td style="text-align: center;">5.5</td> | 96 | <td style="text-align: center;">5.5</td> |
99 | <td style="text-align: center;">Supported</td> | 97 | <td style="text-align: center;">EOL: 2016-07-10</td> |
100 | <td style="text-align: center;">✅</td> | 98 | <td style="text-align: center;">✅</td> |
101 | </tr> | 99 | </tr> |
102 | <tr class="even"> | 100 | <tr class="even"> |
103 | <td style="text-align: center;">5.4</td> | 101 | <td style="text-align: center;">5.4</td> |
104 | <td style="text-align: center;">EOL: 2015-09-14</td> | 102 | <td style="text-align: center;">EOL: 2015-09-14</td> |
105 | <td style="text-align: center;">✅</td> | 103 | <td style="text-align: center;">✅ (up to Shaarli 0.8.x)</td> |
106 | </tr> | 104 | </tr> |
107 | <tr class="odd"> | 105 | <tr class="odd"> |
108 | <td style="text-align: center;">5.3</td> | 106 | <td style="text-align: center;">5.3</td> |
109 | <td style="text-align: center;">EOL: 2014-08-14</td> | 107 | <td style="text-align: center;">EOL: 2014-08-14</td> |
110 | <td style="text-align: center;">✅</td> | 108 | <td style="text-align: center;">✅ (up to Shaarli 0.8.x)</td> |
111 | </tr> | 109 | </tr> |
112 | </tbody> | 110 | </tbody> |
113 | </table> | 111 | </table> |
@@ -115,6 +113,25 @@ | |||
115 | <ul> | 113 | <ul> |
116 | <li><a href="https://github.com/shaarli/Shaarli/blob/master/.travis.yml">Travis configuration</a><a href=".html"></a></li> | 114 | <li><a href="https://github.com/shaarli/Shaarli/blob/master/.travis.yml">Travis configuration</a><a href=".html"></a></li> |
117 | </ul> | 115 | </ul> |
116 | <h3 id="dependency-management">Dependency management</h3> | ||
117 | <p>Starting with Shaarli <code>v0.8.x</code>, <a href="https://getcomposer.org/">Composer</a> is used to resolve,<a href=".html"></a><br /> | ||
118 | download and install third-party PHP dependencies.</p> | ||
119 | <table> | ||
120 | <thead> | ||
121 | <tr class="header"> | ||
122 | <th>Library</th> | ||
123 | <th style="text-align: center;">Required?</th> | ||
124 | <th>Usage</th> | ||
125 | </tr> | ||
126 | </thead> | ||
127 | <tbody> | ||
128 | <tr class="odd"> | ||
129 | <td><a href="https://packagist.org/packages/shaarli/netscape-bookmark-parser"><code>shaarli/netscape-bookmark-parser</code></a></td> | ||
130 | <td style="text-align: center;">All</td> | ||
131 | <td>Import bookmarks from Netscape files<a href=".html"></a></td> | ||
132 | </tr> | ||
133 | </tbody> | ||
134 | </table> | ||
118 | <h3 id="extensions">Extensions</h3> | 135 | <h3 id="extensions">Extensions</h3> |
119 | <table style="width:19%;"> | 136 | <table style="width:19%;"> |
120 | <colgroup> | 137 | <colgroup> |
@@ -142,13 +159,18 @@ | |||
142 | </tr> | 159 | </tr> |
143 | <tr class="odd"> | 160 | <tr class="odd"> |
144 | <td><a href="http://php.net/manual/en/book.image.php"><code>php-gd</code></a></td> | 161 | <td><a href="http://php.net/manual/en/book.image.php"><code>php-gd</code></a></td> |
145 | <td style="text-align: center;">-</td> | 162 | <td style="text-align: center;">optional</td> |
146 | <td>thumbnail resizing<a href=".html"></a></td> | 163 | <td>thumbnail resizing<a href=".html"></a></td> |
147 | </tr> | 164 | </tr> |
148 | <tr class="even"> | 165 | <tr class="even"> |
149 | <td><a href="http://php.net/manual/fr/book.intl.php"><code>php-intl</code></a></td> | 166 | <td><a href="http://php.net/manual/en/book.intl.php"><code>php-intl</code></a></td> |
150 | <td style="text-align: center;">Optional</td> | 167 | <td style="text-align: center;">optional</td> |
151 | <td>Tag cloud intelligent sorting (eg. <code>e->è->f</code>)<a href=".html"></a></td> | 168 | <td>localized text sorting (e.g. <code>e->è->f</code>)<a href=".html"></a></td> |
169 | </tr> | ||
170 | <tr class="odd"> | ||
171 | <td><a href="http://php.net/manual/en/book.curl.php"><code>php-curl</code></a></td> | ||
172 | <td style="text-align: center;">optional</td> | ||
173 | <td>using cURL for fetching webpages and thumbnails in a more robust way<a href=".html"></a></td> | ||
152 | </tr> | 174 | </tr> |
153 | </tbody> | 175 | </tbody> |
154 | </table> | 176 | </table> |
diff --git a/doc/Server-requirements.md b/doc/Server-requirements.md index 7955fddf..4962193e 100644 --- a/doc/Server-requirements.md +++ b/doc/Server-requirements.md | |||
@@ -12,17 +12,26 @@ Version | Status | Shaarli compatibility | |||
12 | :---:|:---:|:---: | 12 | :---:|:---:|:---: |
13 | 7.0 | Supported | :white_check_mark: | 13 | 7.0 | Supported | :white_check_mark: |
14 | 5.6 | Supported | :white_check_mark: | 14 | 5.6 | Supported | :white_check_mark: |
15 | 5.5 | Supported | :white_check_mark: | 15 | 5.5 | EOL: 2016-07-10 | :white_check_mark: |
16 | 5.4 | EOL: 2015-09-14 | :white_check_mark: | 16 | 5.4 | EOL: 2015-09-14 | :white_check_mark: (up to Shaarli 0.8.x) |
17 | 5.3 | EOL: 2014-08-14 | :white_check_mark: | 17 | 5.3 | EOL: 2014-08-14 | :white_check_mark: (up to Shaarli 0.8.x) |
18 | 18 | ||
19 | See also: | 19 | See also: |
20 | - [Travis configuration](https://github.com/shaarli/Shaarli/blob/master/.travis.yml)[](.html) | 20 | - [Travis configuration](https://github.com/shaarli/Shaarli/blob/master/.travis.yml)[](.html) |
21 | 21 | ||
22 | ### Dependency management | ||
23 | Starting with Shaarli `v0.8.x`, [Composer](https://getcomposer.org/) is used to resolve,[](.html) | ||
24 | download and install third-party PHP dependencies. | ||
25 | |||
26 | Library | Required? | Usage | ||
27 | ---|:---:|--- | ||
28 | [`shaarli/netscape-bookmark-parser`](https://packagist.org/packages/shaarli/netscape-bookmark-parser) | All | Import bookmarks from Netscape files[](.html) | ||
29 | |||
22 | ### Extensions | 30 | ### Extensions |
23 | Extension | Required? | Usage | 31 | Extension | Required? | Usage |
24 | ---|:---:|--- | 32 | ---|:---:|--- |
25 | [`openssl`](http://php.net/manual/en/book.openssl.php) | All | OpenSSL, HTTPS[](.html) | 33 | [`openssl`](http://php.net/manual/en/book.openssl.php) | All | OpenSSL, HTTPS[](.html) |
26 | [`php-mbstring`](http://php.net/manual/en/book.mbstring.php) | CentOS, Fedora, RHEL, Windows | multibyte (Unicode) string support[](.html) | 34 | [`php-mbstring`](http://php.net/manual/en/book.mbstring.php) | CentOS, Fedora, RHEL, Windows | multibyte (Unicode) string support[](.html) |
27 | [`php-gd`](http://php.net/manual/en/book.image.php) | - | thumbnail resizing[](.html) | 35 | [`php-gd`](http://php.net/manual/en/book.image.php) | optional | thumbnail resizing[](.html) |
28 | [`php-intl`](http://php.net/manual/fr/book.intl.php) | Optional | Tag cloud intelligent sorting (eg. `e->è->f`)[](.html) | 36 | [`php-intl`](http://php.net/manual/en/book.intl.php) | optional | localized text sorting (e.g. `e->è->f`)[](.html) |
37 | [`php-curl`](http://php.net/manual/en/book.curl.php) | optional | using cURL for fetching webpages and thumbnails in a more robust way[](.html) | ||
diff --git a/doc/Server-security.html b/doc/Server-security.html index 6b44a133..3551deff 100644 --- a/doc/Server-security.html +++ b/doc/Server-security.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
@@ -118,11 +116,11 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
118 | </ul> | 116 | </ul> |
119 | <h3 id="locate-.ini-files">Locate .ini files</h3> | 117 | <h3 id="locate-.ini-files">Locate .ini files</h3> |
120 | <h4 id="console-environment">Console environment</h4> | 118 | <h4 id="console-environment">Console environment</h4> |
121 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">php</span> --ini | 119 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">php</span> --ini |
122 | <span class="kw">Configuration</span> File (php.ini) <span class="kw">Path</span>: /etc/php | 120 | <span class="ex">Configuration</span> File (php.ini) <span class="ex">Path</span>: /etc/php |
123 | <span class="kw">Loaded</span> Configuration File: /etc/php/php.ini | 121 | <span class="ex">Loaded</span> Configuration File: /etc/php/php.ini |
124 | <span class="kw">Scan</span> for additional .ini files in: /etc/php/conf.d | 122 | <span class="ex">Scan</span> for additional .ini files in: /etc/php/conf.d |
125 | <span class="kw">Additional</span> .ini files parsed: /etc/php/conf.d/xdebug.ini</code></pre></div> | 123 | <span class="ex">Additional</span> .ini files parsed: /etc/php/conf.d/xdebug.ini</code></pre></div> |
126 | <h4 id="server-environment">Server environment</h4> | 124 | <h4 id="server-environment">Server environment</h4> |
127 | <ul> | 125 | <ul> |
128 | <li>create a <code>phpinfo.php</code> script located in a path supported by the web server, e.g. | 126 | <li>create a <code>phpinfo.php</code> script located in a path supported by the web server, e.g. |
@@ -161,5 +159,15 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
161 | <span class="kw">[Definition][]</span><span class="dt">(.html)</span> | 159 | <span class="kw">[Definition][]</span><span class="dt">(.html)</span> |
162 | <span class="dt">failregex </span><span class="ot">=</span><span class="st"> \s-\s<HOST>\s-\sLogin failed for user.*$</span> | 160 | <span class="dt">failregex </span><span class="ot">=</span><span class="st"> \s-\s<HOST>\s-\sLogin failed for user.*$</span> |
163 | <span class="dt">ignoreregex </span><span class="ot">=</span><span class="st"> </span></code></pre></div> | 161 | <span class="dt">ignoreregex </span><span class="ot">=</span><span class="st"> </span></code></pre></div> |
162 | <h2 id="robots---restricting-search-engines-and-web-crawler-traffic">Robots - Restricting search engines and web crawler traffic</h2> | ||
163 | <p>Creating a <code>robots.txt</code> with the following contents at the root of your Shaarli installation will prevent <em>honest</em> web crawlers from indexing each and every link and Daily page from a Shaarli instance, thus getting rid of a certain amount of unsollicited network traffic.</p> | ||
164 | <pre><code>User-agent: * | ||
165 | Disallow: /</code></pre> | ||
166 | <p>See:</p> | ||
167 | <ul> | ||
168 | <li><a href="http://www.robotstxt.org/" class="uri">http://www.robotstxt.org/</a></li> | ||
169 | <li><a href="http://www.robotstxt.org/robotstxt.html" class="uri">http://www.robotstxt.org/robotstxt.html</a></li> | ||
170 | <li><a href="http://www.robotstxt.org/meta.html" class="uri">http://www.robotstxt.org/meta.html</a></li> | ||
171 | </ul> | ||
164 | </body> | 172 | </body> |
165 | </html> | 173 | </html> |
diff --git a/doc/Server-security.md b/doc/Server-security.md index 0d16e284..50549a21 100644 --- a/doc/Server-security.md +++ b/doc/Server-security.md | |||
@@ -58,3 +58,17 @@ before = common.conf | |||
58 | failregex = \s-\s<HOST>\s-\sLogin failed for user.*$ | 58 | failregex = \s-\s<HOST>\s-\sLogin failed for user.*$ |
59 | ignoreregex = | 59 | ignoreregex = |
60 | ``` | 60 | ``` |
61 | |||
62 | ## Robots - Restricting search engines and web crawler traffic | ||
63 | |||
64 | Creating a `robots.txt` with the following contents at the root of your Shaarli installation will prevent _honest_ web crawlers from indexing each and every link and Daily page from a Shaarli instance, thus getting rid of a certain amount of unsollicited network traffic. | ||
65 | |||
66 | ``` | ||
67 | User-agent: * | ||
68 | Disallow: / | ||
69 | ``` | ||
70 | |||
71 | See: | ||
72 | - http://www.robotstxt.org/ | ||
73 | - http://www.robotstxt.org/robotstxt.html | ||
74 | - http://www.robotstxt.org/meta.html | ||
diff --git a/doc/Shaarli-configuration.html b/doc/Shaarli-configuration.html index 74947578..6d717c65 100644 --- a/doc/Shaarli-configuration.html +++ b/doc/Shaarli-configuration.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,18 +96,19 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
104 | <h1 id="shaarli-configuration">Shaarli configuration</h1> | 102 | <h1 id="shaarli-configuration">Shaarli configuration</h1> |
103 | <h1 id="shaarli-configuration-1">Shaarli configuration</h1> | ||
105 | <h2 id="foreword">Foreword</h2> | 104 | <h2 id="foreword">Foreword</h2> |
106 | <p><strong>Do not edit configuration options in index.php! Your changes would be lost.</strong></p> | 105 | <p><strong>Do not edit configuration options in index.php! Your changes would be lost.</strong></p> |
107 | <p>Once your Shaarli instance is installed, the file <code>data/config.php</code> is generated:</p> | 106 | <p>Once your Shaarli instance is installed, the file <code>data/config.json.php</code> is generated:</p> |
108 | <ul> | 107 | <ul> |
109 | <li>it contains all settings, and can be edited to customize values</li> | 108 | <li>it contains all settings in JSON format, and can be edited to customize values</li> |
110 | <li>it defines which <a href="Plugin-System">plugins</a> are enabled<a href=".html"></a></li> | 109 | <li>it defines which <a href="Plugin-System">plugins</a> are enabled<a href="(.html).html">(.html)</a></li> |
111 | <li>its values override those defined in <code>index.php</code></li> | 110 | <li>its values override those defined in <code>index.php</code></li> |
111 | <li>it is wrap in a PHP comment to prevent anyone accessing it, regardless of server configuration</li> | ||
112 | </ul> | 112 | </ul> |
113 | <h2 id="file-and-directory-permissions">File and directory permissions</h2> | 113 | <h2 id="file-and-directory-permissions">File and directory permissions</h2> |
114 | <p>The server process running Shaarli must have:</p> | 114 | <p>The server process running Shaarli must have:</p> |
@@ -141,120 +141,155 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
141 | <li>unzip Shaarli in the default web server location (usually <code>/var/www/</code>) and set the web server user as the owner</li> | 141 | <li>unzip Shaarli in the default web server location (usually <code>/var/www/</code>) and set the web server user as the owner</li> |
142 | <li>put users in the same group as the web server, and set the appropriate access rights</li> | 142 | <li>put users in the same group as the web server, and set the appropriate access rights</li> |
143 | </ul></li> | 143 | </ul></li> |
144 | <li>if you have a domain / subdomain to serve Shaarli, <a href="Server-configuration">configure the server</a> accordingly<a href=".html"></a></li> | 144 | <li>if you have a domain / subdomain to serve Shaarli, <a href="Server-configuration">configure the server</a> accordingly<a href="(.html).html">(.html)</a></li> |
145 | </ul> | 145 | </ul> |
146 | <h2 id="example-dataconfig.php">Example <code>data/config.php</code></h2> | 146 | <h2 id="configuration">Configuration</h2> |
147 | <p>See also <a href="Plugin-System.html">Plugin System</a>.</p> | 147 | <p>In <code>data/config.json.php</code>.</p> |
148 | <div class="sourceCode"><pre class="sourceCode php"><code class="sourceCode php"><span class="kw"><?php</span> | 148 | <p>See also <a href="Plugin-System.html">Plugin System</a>.<a href=".html"></a></p> |
149 | <span class="co">// User login</span> | 149 | <h3 id="credentials">Credentials</h3> |
150 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'login'</span><span class="ot">]</span> = <span class="st">'<login>'</span><span class="ot">;[](</span>.html<span class="ot">)</span> | 150 | <blockquote> |
151 | 151 | <p>You shouldn't edit those.</p> | |
152 | <span class="co">// User password hash</span> | 152 | </blockquote> |
153 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'hash'</span><span class="ot">]</span> = <span class="st">'200c452da46c2f889e5e48c49ef044bcacdcb095'</span><span class="ot">;[](</span>.html<span class="ot">)</span> | 153 | <p><strong>login</strong>: Login username.<br /> |
154 | 154 | <strong>hash</strong>: Generated password hash.<br /> | |
155 | <span class="co">// Password salt</span> | 155 | <strong>salt</strong>: Password salt.</p> |
156 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'salt'</span><span class="ot">]</span> = <span class="st">'13b654102321576033d8473b63a275a1bf94c0f0'</span><span class="ot">;</span> <span class="ot">[](</span>.html<span class="ot">)</span> | 156 | <h3 id="general">General</h3> |
157 | 157 | <p><strong>title</strong>: Shaarli's instance title.<br /> | |
158 | <span class="co">// Local timezone</span> | 158 | <strong>header_link</strong>: Link to the homepage.<br /> |
159 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'timezone'</span><span class="ot">]</span> = <span class="st">'Africa/Abidjan'</span><span class="ot">;[](</span>.html<span class="ot">)</span> | 159 | <strong>links_per_page</strong>: Number of shaares displayed per page.<br /> |
160 | <span class="fu">date_default_timezone_set</span><span class="ot">(</span><span class="st">'Africa/Abidjan'</span><span class="ot">);</span> | 160 | <strong>timezone</strong>: See <a href="http://php.net/manual/en/timezones.php">the list of supported timezones</a>. <a href=".html"></a><br /> |
161 | 161 | <strong>enabled_plugins</strong>: List of enabled plugins.</p> | |
162 | <span class="co">// Shaarli title</span> | 162 | <h3 id="security">Security</h3> |
163 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'title'</span><span class="ot">]</span> = <span class="st">'My Little Shaarly'</span><span class="ot">;[](</span>.html<span class="ot">)</span> | 163 | <p><strong>session_protection_disabled</strong>: Disable session cookie hijacking protection (not recommended).<br /> |
164 | 164 | It might be useful if your IP adress often changes.<br /> | |
165 | <span class="co">// Link the Shaarli title points to</span> | 165 | <strong>ban_after</strong>: Failed login attempts before being IP banned.<br /> |
166 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'titleLink'</span><span class="ot">]</span> = <span class="st">'?'</span><span class="ot">;[](</span>.html<span class="ot">)</span> | 166 | <strong>ban_duration</strong>: IP ban duration in seconds.<br /> |
167 | 167 | <strong>open_shaarli</strong>: Anyone can add a new link while logged out if enabled.<br /> | |
168 | <span class="co">// HTTP referer redirector</span> | 168 | <strong>trusted_proxies</strong>: List of trusted IP which won't be banned after failed login attemps. Useful if Shaarli is behind a reverse proxy.</p> |
169 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'redirector'</span><span class="ot">]</span> = <span class="st">''</span><span class="ot">;[](</span>.html<span class="ot">)</span> | 169 | <h3 id="resources">Resources</h3> |
170 | 170 | <p><strong>data_dir</strong>: Data directory.<br /> | |
171 | <span class="co">// Disable session hijacking</span> | 171 | <strong>datastore</strong>: Shaarli's links database file path.<br /> |
172 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'disablesessionprotection'</span><span class="ot">]</span> = <span class="kw">false</span><span class="ot">;</span> <span class="ot">[](</span>.html<span class="ot">)</span> | 172 | <strong>updates</strong>: File path for the ran updates file.<br /> |
173 | 173 | <strong>log</strong>: Log file path.<br /> | |
174 | <span class="co">// Whether new links are private by default</span> | 174 | <strong>update_check</strong>: Last update check file path.<br /> |
175 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'privateLinkByDefault'</span><span class="ot">]</span> = <span class="kw">false</span><span class="ot">;[](</span>.html<span class="ot">)</span> | 175 | <strong>raintpl_tpl</strong>: Templates directory.<br /> |
176 | 176 | <strong>raintpl_tmp</strong>: Template engine cache directory.<br /> | |
177 | <span class="co">// Enabled plugins</span> | 177 | <strong>thumbnails_cache</strong>: Thumbnails cache directory.<br /> |
178 | <span class="co">// Note: each plugin may provide further settings through its own "config.php"</span> | 178 | <strong>page_cache</strong>: Shaarli's internal cache directory.<br /> |
179 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'ENABLED_PLUGINS'</span><span class="ot">]</span> = <span class="fu">array</span><span class="ot">(</span><span class="st">'addlink_toolbar'</span><span class="ot">,</span> <span class="st">'qrcode'</span><span class="ot">);](</span><span class="st">'ENABLED_PLUGINS'</span><span class="ot">]</span>-=-<span class="fu">array</span><span class="ot">(</span><span class="st">'addlink_toolbar'</span><span class="ot">,</span>-<span class="st">'qrcode'</span><span class="ot">);</span>.html<span class="ot">)</span> | 179 | <strong>ban_file</strong>: Banned IP file path.</p> |
180 | 180 | <h3 id="updates">Updates</h3> | |
181 | <span class="co">// Subdirectory where Shaarli stores its data files.</span> | 181 | <p><strong>check_updates</strong>: Enable or disable update check to the git repository.<br /> |
182 | <span class="co">// You can change it for better security.</span> | 182 | <strong>check_updates_branch</strong>: Git branch used to check updates (e.g. <code>stable</code> or <code>master</code>).<br /> |
183 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'DATADIR'</span><span class="ot">]</span> = <span class="st">'data'</span><span class="ot">;](</span><span class="st">'DATADIR'</span><span class="ot">]</span>-=-<span class="st">'data'</span><span class="ot">;</span>.html<span class="ot">)</span> | 183 | <strong>check_updates_interval</strong>: Look for new version every N seconds (default: every day).</p> |
184 | 184 | <h3 id="privacy">Privacy</h3> | |
185 | <span class="co">// File used to store settings</span> | 185 | <p><strong>default_private_links</strong>: Check the private checkbox by default for every new link.<br /> |
186 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'CONFIG_FILE'</span><span class="ot">]</span> = <span class="st">'data/config.php'</span><span class="ot">;](</span><span class="st">'CONFIG_FILE'</span><span class="ot">]</span>-=-<span class="st">'data/config.php'</span><span class="ot">;</span>.html<span class="ot">)</span> | 186 | <strong>hide_public_links</strong>: All links are hidden while logged out.<br /> |
187 | 187 | <strong>hide_timestamps</strong>: Timestamps are hidden.</p> | |
188 | <span class="co">// File containing the link database</span> | 188 | <h3 id="feed">Feed</h3> |
189 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'DATASTORE'</span><span class="ot">]</span> = <span class="st">'data/datastore.php'</span><span class="ot">;](</span><span class="st">'DATASTORE'</span><span class="ot">]</span>-=-<span class="st">'data/datastore.php'</span><span class="ot">;</span>.html<span class="ot">)</span> | 189 | <p><strong>rss_permalinks</strong>: Enable this to redirect RSS links to Shaarli's permalinks instead of shaared URL.<br /> |
190 | 190 | <strong>show_atom</strong>: Display ATOM feed button.</p> | |
191 | <span class="co">// Number of links displayed per page</span> | 191 | <h3 id="thumbnail">Thumbnail</h3> |
192 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'LINKS_PER_PAGE'</span><span class="ot">]</span> = <span class="dv">20</span><span class="ot">;](</span><span class="st">'LINKS_PER_PAGE'</span><span class="ot">]</span>-=-<span class="dv">20</span><span class="ot">;</span>.html<span class="ot">)</span> | 192 | <p><strong>enable_thumbnails</strong>: Enable or disable thumbnail display.<br /> |
193 | 193 | <strong>enable_localcache</strong>: Enable or disable local cache.</p> | |
194 | <span class="co">// File recording failed login attempts and IP bans</span> | 194 | <h3 id="redirector">Redirector</h3> |
195 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'IPBANS_FILENAME'</span><span class="ot">]</span> = <span class="st">'data/ipbans.php'</span><span class="ot">;](</span><span class="st">'IPBANS_FILENAME'</span><span class="ot">]</span>-=-<span class="st">'data/ipbans.php'</span><span class="ot">;</span>.html<span class="ot">)</span> | 195 | <p><strong>url</strong>: Redirector URL, such as <code>anonym.to</code>.<br /> |
196 | 196 | <strong>encode_url</strong>: Enable this if the redirector needs encoded URL to work properly.</p> | |
197 | <span class="co">// Failed login attempts before being banned</span> | 197 | <h2 id="configuration-file-example">Configuration file example</h2> |
198 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'BAN_AFTER'</span><span class="ot">]</span> = <span class="dv">4</span><span class="ot">;](</span><span class="st">'BAN_AFTER'</span><span class="ot">]</span>-=-<span class="dv">4</span><span class="ot">;</span>.html<span class="ot">)</span> | 198 | <div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="er"><?php</span> <span class="er">/*</span> |
199 | 199 | <span class="fu">{</span> | |
200 | <span class="co">// Duration of an IP ban, in seconds (30 minutes)</span> | 200 | <span class="dt">"credentials"</span><span class="fu">:</span> <span class="fu">{</span> |
201 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'BAN_DURATION'</span><span class="ot">]</span> = <span class="dv">1800</span><span class="ot">;](</span><span class="st">'BAN_DURATION'</span><span class="ot">]</span>-=-<span class="dv">1800</span><span class="ot">;</span>.html<span class="ot">)</span> | 201 | <span class="dt">"login"</span><span class="fu">:</span> <span class="st">"<login>"</span><span class="fu">,</span> |
202 | 202 | <span class="dt">"hash"</span><span class="fu">:</span> <span class="st">"<password hash>"</span><span class="fu">,</span> | |
203 | <span class="co">// If set to true, everyone will be able to add, edit and remove links,</span> | 203 | <span class="dt">"salt"</span><span class="fu">:</span> <span class="st">"<password salt>"</span> |
204 | <span class="co">// as well as change configuration</span> | 204 | <span class="fu">},</span> |
205 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'OPEN_SHAARLI'</span><span class="ot">]</span> = <span class="kw">false</span><span class="ot">;](</span><span class="st">'OPEN_SHAARLI'</span><span class="ot">]</span>-=-<span class="kw">false</span><span class="ot">;</span>.html<span class="ot">)</span> | 205 | <span class="dt">"security"</span><span class="fu">:</span> <span class="fu">{</span> |
206 | 206 | <span class="dt">"ban_after"</span><span class="fu">:</span> <span class="dv">4</span><span class="fu">,</span> | |
207 | <span class="co">// Do not show link timestamps</span> | 207 | <span class="dt">"session_protection_disabled"</span><span class="fu">:</span> <span class="kw">false</span><span class="fu">,</span> |
208 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'HIDE_TIMESTAMPS'</span><span class="ot">]</span> = <span class="kw">false</span><span class="ot">;](</span><span class="st">'HIDE_TIMESTAMPS'</span><span class="ot">]</span>-=-<span class="kw">false</span><span class="ot">;</span>.html<span class="ot">)</span> | 208 | <span class="dt">"ban_duration"</span><span class="fu">:</span> <span class="dv">1800</span><span class="fu">,</span> |
209 | 209 | <span class="dt">"trusted_proxies"</span><span class="fu">:</span> <span class="ot">[[]</span><span class="er">(.html)</span> | |
210 | <span class="co">// Set to false to disable local thumbnail cache, e.g. due to limited disk quotas</span> | 210 | <span class="st">"1.2.3.4"</span><span class="ot">,</span> |
211 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'ENABLE_THUMBNAILS'</span><span class="ot">]</span> = <span class="kw">true</span><span class="ot">;](</span><span class="st">'ENABLE_THUMBNAILS'</span><span class="ot">]</span>-=-<span class="kw">true</span><span class="ot">;</span>.html<span class="ot">)</span> | 211 | <span class="st">"5.6.7.8"</span> |
212 | 212 | <span class="ot">]</span> | |
213 | <span class="co">// Thumbnail cache directory</span> | 213 | <span class="fu">},</span> |
214 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'CACHEDIR'</span><span class="ot">]</span> = <span class="st">'cache'</span><span class="ot">;](</span><span class="st">'CACHEDIR'</span><span class="ot">]</span>-=-<span class="st">'cache'</span><span class="ot">;</span>.html<span class="ot">)</span> | 214 | <span class="dt">"resources"</span><span class="fu">:</span> <span class="fu">{</span> |
215 | 215 | <span class="dt">"data_dir"</span><span class="fu">:</span> <span class="st">"data"</span><span class="fu">,</span> | |
216 | <span class="co">// Enable feed (rss, atom, dailyrss) cache</span> | 216 | <span class="dt">"config"</span><span class="fu">:</span> <span class="st">"data</span><span class="ch">\/</span><span class="st">config.php"</span><span class="fu">,</span> |
217 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'ENABLE_LOCALCACHE'</span><span class="ot">]</span> = <span class="kw">true</span><span class="ot">;](</span><span class="st">'ENABLE_LOCALCACHE'</span><span class="ot">]</span>-=-<span class="kw">true</span><span class="ot">;</span>.html<span class="ot">)</span> | 217 | <span class="dt">"datastore"</span><span class="fu">:</span> <span class="st">"data</span><span class="ch">\/</span><span class="st">datastore.php"</span><span class="fu">,</span> |
218 | 218 | <span class="dt">"ban_file"</span><span class="fu">:</span> <span class="st">"data</span><span class="ch">\/</span><span class="st">ipbans.php"</span><span class="fu">,</span> | |
219 | <span class="co">// Feed cache directory</span> | 219 | <span class="dt">"updates"</span><span class="fu">:</span> <span class="st">"data</span><span class="ch">\/</span><span class="st">updates.txt"</span><span class="fu">,</span> |
220 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'PAGECACHE'</span><span class="ot">]</span> = <span class="st">'pagecache'</span><span class="ot">;](</span><span class="st">'PAGECACHE'</span><span class="ot">]</span>-=-<span class="st">'pagecache'</span><span class="ot">;</span>.html<span class="ot">)</span> | 220 | <span class="dt">"log"</span><span class="fu">:</span> <span class="st">"data</span><span class="ch">\/</span><span class="st">log.txt"</span><span class="fu">,</span> |
221 | 221 | <span class="dt">"update_check"</span><span class="fu">:</span> <span class="st">"data</span><span class="ch">\/</span><span class="st">lastupdatecheck.txt"</span><span class="fu">,</span> | |
222 | <span class="co">// RainTPL cache directory (keep the trailing slash!)</span> | 222 | <span class="dt">"raintpl_tmp"</span><span class="fu">:</span> <span class="st">"tmp</span><span class="ch">\/</span><span class="st">"</span><span class="fu">,</span> |
223 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'RAINTPL_TMP'</span><span class="ot">]</span> = <span class="st">'tmp/'</span><span class="ot">;](</span><span class="st">'RAINTPL_TMP'</span><span class="ot">]</span>-=-<span class="st">'tmp/'</span><span class="ot">;</span>.html<span class="ot">)</span> | 223 | <span class="dt">"raintpl_tpl"</span><span class="fu">:</span> <span class="st">"tpl</span><span class="ch">\/</span><span class="st">"</span><span class="fu">,</span> |
224 | 224 | <span class="dt">"thumbnails_cache"</span><span class="fu">:</span> <span class="st">"cache"</span><span class="fu">,</span> | |
225 | <span class="co">// RainTPL template directory (keep the trailing slash!)</span> | 225 | <span class="dt">"page_cache"</span><span class="fu">:</span> <span class="st">"pagecache"</span> |
226 | <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/'</span><span class="ot">;](</span><span class="st">'RAINTPL_TPL'</span><span class="ot">]</span>-=-<span class="st">'tpl/'</span><span class="ot">;</span>.html<span class="ot">)</span> | 226 | <span class="fu">},</span> |
227 | 227 | <span class="dt">"general"</span><span class="fu">:</span> <span class="fu">{</span> | |
228 | <span class="co">// Whether Shaarli checks for new releases at https://github.com/shaarli/Shaarli</span> | 228 | <span class="dt">"check_updates"</span><span class="fu">:</span> <span class="kw">true</span><span class="fu">,</span> |
229 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'ENABLE_UPDATECHECK'</span><span class="ot">]</span> = <span class="kw">true</span><span class="ot">;](</span><span class="st">'ENABLE_UPDATECHECK'</span><span class="ot">]</span>-=-<span class="kw">true</span><span class="ot">;</span>.html<span class="ot">)</span> | 229 | <span class="dt">"rss_permalinks"</span><span class="fu">:</span> <span class="kw">true</span><span class="fu">,</span> |
230 | 230 | <span class="dt">"links_per_page"</span><span class="fu">:</span> <span class="dv">20</span><span class="fu">,</span> | |
231 | <span class="co">// File to store the latest Shaarli version</span> | 231 | <span class="dt">"default_private_links"</span><span class="fu">:</span> <span class="kw">true</span><span class="fu">,</span> |
232 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'UPDATECHECK_FILENAME'</span><span class="ot">]</span> = <span class="st">'data/lastupdatecheck.txt'</span><span class="ot">;](</span><span class="st">'UPDATECHECK_FILENAME'</span><span class="ot">]</span>-=-<span class="st">'data/lastupdatecheck.txt'</span><span class="ot">;</span>.html<span class="ot">)</span> | 232 | <span class="dt">"enable_thumbnails"</span><span class="fu">:</span> <span class="kw">true</span><span class="fu">,</span> |
233 | 233 | <span class="dt">"enable_localcache"</span><span class="fu">:</span> <span class="kw">true</span><span class="fu">,</span> | |
234 | <span class="co">// Delay between version checks (requires to be logged in) (24 hours)</span> | 234 | <span class="dt">"check_updates_branch"</span><span class="fu">:</span> <span class="st">"stable"</span><span class="fu">,</span> |
235 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'UPDATECHECK_INTERVAL'</span><span class="ot">]</span> = <span class="dv">86400</span><span class="ot">;](</span><span class="st">'UPDATECHECK_INTERVAL'</span><span class="ot">]</span>-=-<span class="dv">86400</span><span class="ot">;</span>.html<span class="ot">)</span> | 235 | <span class="dt">"check_updates_interval"</span><span class="fu">:</span> <span class="dv">86400</span><span class="fu">,</span> |
236 | 236 | <span class="dt">"enabled_plugins"</span><span class="fu">:</span> <span class="ot">[[]</span><span class="er">(.html)</span> | |
237 | <span class="co">// For each link, display a link to an archived version on archive.org</span> | 237 | <span class="st">"markdown"</span><span class="ot">,</span> |
238 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'ARCHIVE_ORG'</span><span class="ot">]</span> = <span class="kw">false</span><span class="ot">;](</span><span class="st">'ARCHIVE_ORG'</span><span class="ot">]</span>-=-<span class="kw">false</span><span class="ot">;</span>.html<span class="ot">)</span> | 238 | <span class="st">"wallabag"</span><span class="ot">,</span> |
239 | 239 | <span class="st">"archiveorg"</span> | |
240 | <span class="co">// The RSS item links point:</span> | 240 | <span class="ot">]</span><span class="fu">,</span> |
241 | <span class="co">// true => directly to the link</span> | 241 | <span class="dt">"timezone"</span><span class="fu">:</span> <span class="st">"Europe</span><span class="ch">\/</span><span class="st">Paris"</span><span class="fu">,</span> |
242 | <span class="co">// false => to the entry on Shaarli (permalink)</span> | 242 | <span class="dt">"title"</span><span class="fu">:</span> <span class="st">"My Shaarli"</span><span class="fu">,</span> |
243 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'ENABLE_RSS_PERMALINKS'</span><span class="ot">]</span> = <span class="kw">true</span><span class="ot">;](</span><span class="st">'ENABLE_RSS_PERMALINKS'</span><span class="ot">]</span>-=-<span class="kw">true</span><span class="ot">;</span>.html<span class="ot">)</span> | 243 | <span class="dt">"header_link"</span><span class="fu">:</span> <span class="st">"?"</span> |
244 | 244 | <span class="fu">},</span> | |
245 | <span class="co">// Hide all links to non-logged users</span> | 245 | <span class="dt">"extras"</span><span class="fu">:</span> <span class="fu">{</span> |
246 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'HIDE_PUBLIC_LINKS'</span><span class="ot">]</span> = <span class="kw">false</span><span class="ot">;](</span><span class="st">'HIDE_PUBLIC_LINKS'</span><span class="ot">]</span>-=-<span class="kw">false</span><span class="ot">;</span>.html<span class="ot">)</span> | 246 | <span class="dt">"show_atom"</span><span class="fu">:</span> <span class="kw">false</span><span class="fu">,</span> |
247 | 247 | <span class="dt">"hide_public_links"</span><span class="fu">:</span> <span class="kw">false</span><span class="fu">,</span> | |
248 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'PUBSUBHUB_URL'</span><span class="ot">]</span> = <span class="st">''</span><span class="ot">;](</span><span class="st">'PUBSUBHUB_URL'</span><span class="ot">]</span>-=-<span class="st">''</span><span class="ot">;</span>.html<span class="ot">)</span> | 248 | <span class="dt">"hide_timestamps"</span><span class="fu">:</span> <span class="kw">false</span><span class="fu">,</span> |
249 | 249 | <span class="dt">"open_shaarli"</span><span class="fu">:</span> <span class="kw">false</span><span class="fu">,</span> | |
250 | <span class="co">// Show an ATOM Feed button next to the Subscribe (RSS) button.</span> | 250 | <span class="dt">"redirector"</span><span class="fu">:</span> <span class="st">"http://anonym.to/?"</span><span class="fu">,</span> |
251 | <span class="co">// ATOM feeds are available at the address ?do=atom regardless of this option.</span> | 251 | <span class="dt">"redirector_encode_url"</span><span class="fu">:</span> <span class="kw">false</span> |
252 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'SHOW_ATOM'</span><span class="ot">]</span> = <span class="kw">false</span><span class="ot">;](</span><span class="st">'SHOW_ATOM'</span><span class="ot">]</span>-=-<span class="kw">false</span><span class="ot">;</span>.html<span class="ot">)</span> | 252 | <span class="fu">},</span> |
253 | 253 | <span class="dt">"general"</span><span class="fu">:</span> <span class="fu">{</span> | |
254 | <span class="co">// Set this to true if the redirector requires encoded URL, false otherwise.</span> | 254 | <span class="dt">"header_link"</span><span class="fu">:</span> <span class="st">"?"</span><span class="fu">,</span> |
255 | <span class="kw">$GLOBALS</span><span class="ot">[</span><span class="st">'config'</span><span class="ot">[</span><span class="st">'REDIRECTOR_URLENCODE'</span><span class="ot">]</span> = <span class="kw">true</span><span class="ot">;](</span><span class="st">'REDIRECTOR_URLENCODE'</span><span class="ot">]</span>-=-<span class="kw">true</span><span class="ot">;</span>.html<span class="ot">)</span> | 255 | <span class="dt">"links_per_page"</span><span class="fu">:</span> <span class="dv">20</span><span class="fu">,</span> |
256 | <span class="kw">?></span></code></pre></div> | 256 | <span class="dt">"enabled_plugins"</span><span class="fu">:</span> <span class="ot">[[]</span><span class="er">(.html)</span> |
257 | <span class="st">"markdown"</span><span class="ot">,</span> | ||
258 | <span class="st">"wallabag"</span> | ||
259 | <span class="ot">]</span><span class="fu">,</span> | ||
260 | <span class="dt">"timezone"</span><span class="fu">:</span> <span class="st">"Europe</span><span class="ch">\/</span><span class="st">Paris"</span><span class="fu">,</span> | ||
261 | <span class="dt">"title"</span><span class="fu">:</span> <span class="st">"My Shaarli"</span> | ||
262 | <span class="fu">},</span> | ||
263 | <span class="dt">"updates"</span><span class="fu">:</span> <span class="fu">{</span> | ||
264 | <span class="dt">"check_updates"</span><span class="fu">:</span> <span class="kw">true</span><span class="fu">,</span> | ||
265 | <span class="dt">"check_updates_branch"</span><span class="fu">:</span> <span class="st">"stable"</span><span class="fu">,</span> | ||
266 | <span class="dt">"check_updates_interval"</span><span class="fu">:</span> <span class="dv">86400</span> | ||
267 | <span class="fu">},</span> | ||
268 | <span class="dt">"feed"</span><span class="fu">:</span> <span class="fu">{</span> | ||
269 | <span class="dt">"rss_permalinks"</span><span class="fu">:</span> <span class="kw">true</span><span class="fu">,</span> | ||
270 | <span class="dt">"show_atom"</span><span class="fu">:</span> <span class="kw">false</span> | ||
271 | <span class="fu">},</span> | ||
272 | <span class="dt">"privacy"</span><span class="fu">:</span> <span class="fu">{</span> | ||
273 | <span class="dt">"default_private_links"</span><span class="fu">:</span> <span class="kw">true</span><span class="fu">,</span> | ||
274 | <span class="dt">"hide_public_links"</span><span class="fu">:</span> <span class="kw">false</span><span class="fu">,</span> | ||
275 | <span class="dt">"hide_timestamps"</span><span class="fu">:</span> <span class="kw">false</span> | ||
276 | <span class="fu">},</span> | ||
277 | <span class="dt">"thumbnail"</span><span class="fu">:</span> <span class="fu">{</span> | ||
278 | <span class="dt">"enable_thumbnails"</span><span class="fu">:</span> <span class="kw">true</span><span class="fu">,</span> | ||
279 | <span class="dt">"enable_localcache"</span><span class="fu">:</span> <span class="kw">true</span> | ||
280 | <span class="fu">},</span> | ||
281 | <span class="dt">"redirector"</span><span class="fu">:</span> <span class="fu">{</span> | ||
282 | <span class="dt">"url"</span><span class="fu">:</span> <span class="st">"http://anonym.to/?"</span><span class="fu">,</span> | ||
283 | <span class="dt">"encode_url"</span><span class="fu">:</span> <span class="kw">false</span> | ||
284 | <span class="fu">},</span> | ||
285 | <span class="dt">"plugins"</span><span class="fu">:</span> <span class="fu">{</span> | ||
286 | <span class="dt">"WALLABAG_URL"</span><span class="fu">:</span> <span class="st">"http://demo.wallabag.org"</span><span class="fu">,</span> | ||
287 | <span class="dt">"WALLABAG_VERSION"</span><span class="fu">:</span> <span class="st">"1"</span> | ||
288 | <span class="fu">}</span> | ||
289 | <span class="fu">}</span> <span class="er">?></span></code></pre></div> | ||
257 | <h2 id="additional-configuration">Additional configuration</h2> | 290 | <h2 id="additional-configuration">Additional configuration</h2> |
258 | <p>The playvideos plugin may require that you adapt your server's <a href="https://github.com/shaarli/Shaarli/blob/master/plugins/playvideos/README.md#troubleshooting">Content Security Policy</a> configuration to work properly.<a href=".html"></a></p> | 291 | <p>The playvideos plugin may require that you adapt your server's<br /> |
292 | <a href="https://github.com/shaarli/Shaarli/blob/master/plugins/playvideos/README.md#troubleshooting">Content Security Policy</a> <a href=".html"></a><br /> | ||
293 | configuration to work properly.<a href="(.html).html">(.html)</a></p> | ||
259 | </body> | 294 | </body> |
260 | </html> | 295 | </html> |
diff --git a/doc/Shaarli-configuration.md b/doc/Shaarli-configuration.md index d0560d79..4a783c0e 100644 --- a/doc/Shaarli-configuration.md +++ b/doc/Shaarli-configuration.md | |||
@@ -1,14 +1,18 @@ | |||
1 | #Shaarli configuration | 1 | #Shaarli configuration |
2 | # Shaarli configuration | ||
3 | |||
2 | ## Foreword | 4 | ## Foreword |
3 | 5 | ||
4 | **Do not edit configuration options in index.php! Your changes would be lost.** | 6 | **Do not edit configuration options in index.php! Your changes would be lost.** |
5 | 7 | ||
6 | Once your Shaarli instance is installed, the file `data/config.php` is generated: | 8 | Once your Shaarli instance is installed, the file `data/config.json.php` is generated: |
7 | * it contains all settings, and can be edited to customize values | 9 | * it contains all settings in JSON format, and can be edited to customize values |
8 | * it defines which [plugins](Plugin-System) are enabled[](.html) | 10 | * it defines which [plugins](Plugin-System) are enabled[(.html)]((.html).html) |
9 | * its values override those defined in `index.php` | 11 | * its values override those defined in `index.php` |
12 | * it is wrap in a PHP comment to prevent anyone accessing it, regardless of server configuration | ||
10 | 13 | ||
11 | ## File and directory permissions | 14 | ## File and directory permissions |
15 | |||
12 | The server process running Shaarli must have: | 16 | The server process running Shaarli must have: |
13 | - `read` access to the following resources: | 17 | - `read` access to the following resources: |
14 | - PHP scripts: `index.php`, `application/*.php`, `plugins/*.php` | 18 | - PHP scripts: `index.php`, `application/*.php`, `plugins/*.php` |
@@ -29,123 +33,179 @@ On a Linux distribution: | |||
29 | - to give it access to Shaarli, either: | 33 | - to give it access to Shaarli, either: |
30 | - unzip Shaarli in the default web server location (usually `/var/www/`) and set the web server user as the owner | 34 | - unzip Shaarli in the default web server location (usually `/var/www/`) and set the web server user as the owner |
31 | - put users in the same group as the web server, and set the appropriate access rights | 35 | - put users in the same group as the web server, and set the appropriate access rights |
32 | - if you have a domain / subdomain to serve Shaarli, [configure the server](Server-configuration) accordingly[](.html) | 36 | - if you have a domain / subdomain to serve Shaarli, [configure the server](Server-configuration) accordingly[(.html)]((.html).html) |
33 | 37 | ||
34 | ## Example `data/config.php` | 38 | ## Configuration |
35 | See also [Plugin System](Plugin-System.html). | 39 | |
36 | 40 | In `data/config.json.php`. | |
37 | ```php | 41 | |
38 | <?php | 42 | See also [Plugin System](Plugin-System.html).[](.html) |
39 | // User login | 43 | |
40 | $GLOBALS['login'] = '<login>';[](.html) | 44 | ### Credentials |
41 | 45 | ||
42 | // User password hash | 46 | > You shouldn't edit those. |
43 | $GLOBALS['hash'] = '200c452da46c2f889e5e48c49ef044bcacdcb095';[](.html) | 47 | |
44 | 48 | **login**: Login username. | |
45 | // Password salt | 49 | **hash**: Generated password hash. |
46 | $GLOBALS['salt'] = '13b654102321576033d8473b63a275a1bf94c0f0'; [](.html) | 50 | **salt**: Password salt. |
47 | 51 | ||
48 | // Local timezone | 52 | ### General |
49 | $GLOBALS['timezone'] = 'Africa/Abidjan';[](.html) | 53 | |
50 | date_default_timezone_set('Africa/Abidjan'); | 54 | **title**: Shaarli's instance title. |
51 | 55 | **header_link**: Link to the homepage. | |
52 | // Shaarli title | 56 | **links_per_page**: Number of shaares displayed per page. |
53 | $GLOBALS['title'] = 'My Little Shaarly';[](.html) | 57 | **timezone**: See [the list of supported timezones](http://php.net/manual/en/timezones.php). [](.html) |
54 | 58 | **enabled_plugins**: List of enabled plugins. | |
55 | // Link the Shaarli title points to | 59 | |
56 | $GLOBALS['titleLink'] = '?';[](.html) | 60 | ### Security |
57 | 61 | ||
58 | // HTTP referer redirector | 62 | **session_protection_disabled**: Disable session cookie hijacking protection (not recommended). |
59 | $GLOBALS['redirector'] = '';[](.html) | 63 | It might be useful if your IP adress often changes. |
60 | 64 | **ban_after**: Failed login attempts before being IP banned. | |
61 | // Disable session hijacking | 65 | **ban_duration**: IP ban duration in seconds. |
62 | $GLOBALS['disablesessionprotection'] = false; [](.html) | 66 | **open_shaarli**: Anyone can add a new link while logged out if enabled. |
63 | 67 | **trusted_proxies**: List of trusted IP which won't be banned after failed login attemps. Useful if Shaarli is behind a reverse proxy. | |
64 | // Whether new links are private by default | 68 | |
65 | $GLOBALS['privateLinkByDefault'] = false;[](.html) | 69 | ### Resources |
66 | 70 | ||
67 | // Enabled plugins | 71 | **data_dir**: Data directory. |
68 | // Note: each plugin may provide further settings through its own "config.php" | 72 | **datastore**: Shaarli's links database file path. |
69 | $GLOBALS['config'['ENABLED_PLUGINS'] = array('addlink_toolbar', 'qrcode');]('ENABLED_PLUGINS']-=-array('addlink_toolbar',-'qrcode');.html) | 73 | **updates**: File path for the ran updates file. |
70 | 74 | **log**: Log file path. | |
71 | // Subdirectory where Shaarli stores its data files. | 75 | **update_check**: Last update check file path. |
72 | // You can change it for better security. | 76 | **raintpl_tpl**: Templates directory. |
73 | $GLOBALS['config'['DATADIR'] = 'data';]('DATADIR']-=-'data';.html) | 77 | **raintpl_tmp**: Template engine cache directory. |
74 | 78 | **thumbnails_cache**: Thumbnails cache directory. | |
75 | // File used to store settings | 79 | **page_cache**: Shaarli's internal cache directory. |
76 | $GLOBALS['config'['CONFIG_FILE'] = 'data/config.php';]('CONFIG_FILE']-=-'data/config.php';.html) | 80 | **ban_file**: Banned IP file path. |
77 | 81 | ||
78 | // File containing the link database | 82 | ### Updates |
79 | $GLOBALS['config'['DATASTORE'] = 'data/datastore.php';]('DATASTORE']-=-'data/datastore.php';.html) | 83 | |
80 | 84 | **check_updates**: Enable or disable update check to the git repository. | |
81 | // Number of links displayed per page | 85 | **check_updates_branch**: Git branch used to check updates (e.g. `stable` or `master`). |
82 | $GLOBALS['config'['LINKS_PER_PAGE'] = 20;]('LINKS_PER_PAGE']-=-20;.html) | 86 | **check_updates_interval**: Look for new version every N seconds (default: every day). |
83 | 87 | ||
84 | // File recording failed login attempts and IP bans | 88 | ### Privacy |
85 | $GLOBALS['config'['IPBANS_FILENAME'] = 'data/ipbans.php';]('IPBANS_FILENAME']-=-'data/ipbans.php';.html) | 89 | |
86 | 90 | **default_private_links**: Check the private checkbox by default for every new link. | |
87 | // Failed login attempts before being banned | 91 | **hide_public_links**: All links are hidden while logged out. |
88 | $GLOBALS['config'['BAN_AFTER'] = 4;]('BAN_AFTER']-=-4;.html) | 92 | **hide_timestamps**: Timestamps are hidden. |
89 | 93 | ||
90 | // Duration of an IP ban, in seconds (30 minutes) | 94 | ### Feed |
91 | $GLOBALS['config'['BAN_DURATION'] = 1800;]('BAN_DURATION']-=-1800;.html) | 95 | |
92 | 96 | **rss_permalinks**: Enable this to redirect RSS links to Shaarli's permalinks instead of shaared URL. | |
93 | // If set to true, everyone will be able to add, edit and remove links, | 97 | **show_atom**: Display ATOM feed button. |
94 | // as well as change configuration | 98 | |
95 | $GLOBALS['config'['OPEN_SHAARLI'] = false;]('OPEN_SHAARLI']-=-false;.html) | 99 | ### Thumbnail |
96 | 100 | ||
97 | // Do not show link timestamps | 101 | **enable_thumbnails**: Enable or disable thumbnail display. |
98 | $GLOBALS['config'['HIDE_TIMESTAMPS'] = false;]('HIDE_TIMESTAMPS']-=-false;.html) | 102 | **enable_localcache**: Enable or disable local cache. |
99 | 103 | ||
100 | // Set to false to disable local thumbnail cache, e.g. due to limited disk quotas | 104 | ### Redirector |
101 | $GLOBALS['config'['ENABLE_THUMBNAILS'] = true;]('ENABLE_THUMBNAILS']-=-true;.html) | 105 | |
102 | 106 | **url**: Redirector URL, such as `anonym.to`. | |
103 | // Thumbnail cache directory | 107 | **encode_url**: Enable this if the redirector needs encoded URL to work properly. |
104 | $GLOBALS['config'['CACHEDIR'] = 'cache';]('CACHEDIR']-=-'cache';.html) | 108 | |
105 | 109 | ## Configuration file example | |
106 | // Enable feed (rss, atom, dailyrss) cache | 110 | |
107 | $GLOBALS['config'['ENABLE_LOCALCACHE'] = true;]('ENABLE_LOCALCACHE']-=-true;.html) | 111 | ```json |
108 | 112 | <?php /* | |
109 | // Feed cache directory | 113 | { |
110 | $GLOBALS['config'['PAGECACHE'] = 'pagecache';]('PAGECACHE']-=-'pagecache';.html) | 114 | "credentials": { |
111 | 115 | "login": "<login>", | |
112 | // RainTPL cache directory (keep the trailing slash!) | 116 | "hash": "<password hash>", |
113 | $GLOBALS['config'['RAINTPL_TMP'] = 'tmp/';]('RAINTPL_TMP']-=-'tmp/';.html) | 117 | "salt": "<password salt>" |
114 | 118 | }, | |
115 | // RainTPL template directory (keep the trailing slash!) | 119 | "security": { |
116 | $GLOBALS['config'['RAINTPL_TPL'] = 'tpl/';]('RAINTPL_TPL']-=-'tpl/';.html) | 120 | "ban_after": 4, |
117 | 121 | "session_protection_disabled": false, | |
118 | // Whether Shaarli checks for new releases at https://github.com/shaarli/Shaarli | 122 | "ban_duration": 1800, |
119 | $GLOBALS['config'['ENABLE_UPDATECHECK'] = true;]('ENABLE_UPDATECHECK']-=-true;.html) | 123 | "trusted_proxies": [[](.html) |
120 | 124 | "1.2.3.4", | |
121 | // File to store the latest Shaarli version | 125 | "5.6.7.8" |
122 | $GLOBALS['config'['UPDATECHECK_FILENAME'] = 'data/lastupdatecheck.txt';]('UPDATECHECK_FILENAME']-=-'data/lastupdatecheck.txt';.html) | 126 | ] |
123 | 127 | }, | |
124 | // Delay between version checks (requires to be logged in) (24 hours) | 128 | "resources": { |
125 | $GLOBALS['config'['UPDATECHECK_INTERVAL'] = 86400;]('UPDATECHECK_INTERVAL']-=-86400;.html) | 129 | "data_dir": "data", |
126 | 130 | "config": "data\/config.php", | |
127 | // For each link, display a link to an archived version on archive.org | 131 | "datastore": "data\/datastore.php", |
128 | $GLOBALS['config'['ARCHIVE_ORG'] = false;]('ARCHIVE_ORG']-=-false;.html) | 132 | "ban_file": "data\/ipbans.php", |
129 | 133 | "updates": "data\/updates.txt", | |
130 | // The RSS item links point: | 134 | "log": "data\/log.txt", |
131 | // true => directly to the link | 135 | "update_check": "data\/lastupdatecheck.txt", |
132 | // false => to the entry on Shaarli (permalink) | 136 | "raintpl_tmp": "tmp\/", |
133 | $GLOBALS['config'['ENABLE_RSS_PERMALINKS'] = true;]('ENABLE_RSS_PERMALINKS']-=-true;.html) | 137 | "raintpl_tpl": "tpl\/", |
134 | 138 | "thumbnails_cache": "cache", | |
135 | // Hide all links to non-logged users | 139 | "page_cache": "pagecache" |
136 | $GLOBALS['config'['HIDE_PUBLIC_LINKS'] = false;]('HIDE_PUBLIC_LINKS']-=-false;.html) | 140 | }, |
137 | 141 | "general": { | |
138 | $GLOBALS['config'['PUBSUBHUB_URL'] = '';]('PUBSUBHUB_URL']-=-'';.html) | 142 | "check_updates": true, |
139 | 143 | "rss_permalinks": true, | |
140 | // Show an ATOM Feed button next to the Subscribe (RSS) button. | 144 | "links_per_page": 20, |
141 | // ATOM feeds are available at the address ?do=atom regardless of this option. | 145 | "default_private_links": true, |
142 | $GLOBALS['config'['SHOW_ATOM'] = false;]('SHOW_ATOM']-=-false;.html) | 146 | "enable_thumbnails": true, |
143 | 147 | "enable_localcache": true, | |
144 | // Set this to true if the redirector requires encoded URL, false otherwise. | 148 | "check_updates_branch": "stable", |
145 | $GLOBALS['config'['REDIRECTOR_URLENCODE'] = true;]('REDIRECTOR_URLENCODE']-=-true;.html) | 149 | "check_updates_interval": 86400, |
146 | ?> | 150 | "enabled_plugins": [[](.html) |
151 | "markdown", | ||
152 | "wallabag", | ||
153 | "archiveorg" | ||
154 | ], | ||
155 | "timezone": "Europe\/Paris", | ||
156 | "title": "My Shaarli", | ||
157 | "header_link": "?" | ||
158 | }, | ||
159 | "extras": { | ||
160 | "show_atom": false, | ||
161 | "hide_public_links": false, | ||
162 | "hide_timestamps": false, | ||
163 | "open_shaarli": false, | ||
164 | "redirector": "http://anonym.to/?", | ||
165 | "redirector_encode_url": false | ||
166 | }, | ||
167 | "general": { | ||
168 | "header_link": "?", | ||
169 | "links_per_page": 20, | ||
170 | "enabled_plugins": [[](.html) | ||
171 | "markdown", | ||
172 | "wallabag" | ||
173 | ], | ||
174 | "timezone": "Europe\/Paris", | ||
175 | "title": "My Shaarli" | ||
176 | }, | ||
177 | "updates": { | ||
178 | "check_updates": true, | ||
179 | "check_updates_branch": "stable", | ||
180 | "check_updates_interval": 86400 | ||
181 | }, | ||
182 | "feed": { | ||
183 | "rss_permalinks": true, | ||
184 | "show_atom": false | ||
185 | }, | ||
186 | "privacy": { | ||
187 | "default_private_links": true, | ||
188 | "hide_public_links": false, | ||
189 | "hide_timestamps": false | ||
190 | }, | ||
191 | "thumbnail": { | ||
192 | "enable_thumbnails": true, | ||
193 | "enable_localcache": true | ||
194 | }, | ||
195 | "redirector": { | ||
196 | "url": "http://anonym.to/?", | ||
197 | "encode_url": false | ||
198 | }, | ||
199 | "plugins": { | ||
200 | "WALLABAG_URL": "http://demo.wallabag.org", | ||
201 | "WALLABAG_VERSION": "1" | ||
202 | } | ||
203 | } ?> | ||
147 | ``` | 204 | ``` |
148 | 205 | ||
149 | ## Additional configuration | 206 | ## Additional configuration |
150 | 207 | ||
151 | The playvideos plugin may require that you adapt your server's [Content Security Policy](https://github.com/shaarli/Shaarli/blob/master/plugins/playvideos/README.md#troubleshooting) configuration to work properly.[](.html) | 208 | The playvideos plugin may require that you adapt your server's |
209 | [Content Security Policy](https://github.com/shaarli/Shaarli/blob/master/plugins/playvideos/README.md#troubleshooting) [](.html) | ||
210 | configuration to work properly.[(.html)]((.html).html) | ||
211 | |||
diff --git a/doc/Shaarli-installation.html b/doc/Shaarli-installation.html deleted file mode 100644 index 487ec1db..00000000 --- a/doc/Shaarli-installation.html +++ /dev/null | |||
@@ -1,72 +0,0 @@ | |||
1 | <!DOCTYPE html> | ||
2 | <html> | ||
3 | <head> | ||
4 | <meta charset="utf-8"> | ||
5 | <meta name="generator" content="pandoc"> | ||
6 | <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> | ||
7 | <title>Shaarli – Shaarli installation</title> | ||
8 | <style type="text/css">code{white-space: pre;}</style> | ||
9 | <link rel="stylesheet" href="github-markdown.css"> | ||
10 | <!--[if lt IE 9]> | ||
11 | <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> | ||
12 | <![endif]--> | ||
13 | </head> | ||
14 | <body> | ||
15 | <div id="local-sidebar"> | ||
16 | <ul> | ||
17 | <li><a href="Home.html">Home</a></li> | ||
18 | <li>Installation | ||
19 | <ul> | ||
20 | <li><a href="Download.html">Download</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | ||
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | ||
23 | <li><a href="Server-security.html">Server security</a></li> | ||
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | ||
26 | <li><a href="Plugins.html">Plugins</a></li> | ||
27 | </ul></li> | ||
28 | <li><a href="Docker.html">Docker</a></li> | ||
29 | <li><a href="Usage.html">Usage</a> | ||
30 | <ul> | ||
31 | <li><a href="Sharing-button.html">Sharing button</a> (bookmarklet)</li> | ||
32 | <li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li> | ||
33 | <li><a href="Firefox-share.html">Firefox share</a></li> | ||
34 | <li><a href="RSS-feeds.html">RSS feeds</a></li> | ||
35 | </ul></li> | ||
36 | <li>How To | ||
37 | <ul> | ||
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | ||
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | ||
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | ||
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | ||
43 | <li><a href="Datastore-hacks.html">Datastore hacks</a></li> | ||
44 | </ul></li> | ||
45 | <li><a href="Troubleshooting.html">Troubleshooting</a></li> | ||
46 | <li><a href="Development.html">Development</a> | ||
47 | <ul> | ||
48 | <li><a href="GnuPG-signature.html">GnuPG signature</a></li> | ||
49 | <li><a href="Coding-guidelines.html">Coding guidelines</a></li> | ||
50 | <li><a href="Directory-structure.html">Directory structure</a></li> | ||
51 | <li><a href="3rd-party-libraries.html">3rd party libraries</a></li> | ||
52 | <li><a href="Plugin-System.html">Plugin System</a></li> | ||
53 | <li><a href="Release-Shaarli.html">Release Shaarli</a></li> | ||
54 | <li><a href="Security.html">Security</a></li> | ||
55 | <li><a href="Static-analysis.html">Static analysis</a></li> | ||
56 | <li><a href="Theming.html">Theming</a></li> | ||
57 | <li><a href="Unit-tests.html">Unit tests</a></li> | ||
58 | </ul></li> | ||
59 | <li>About | ||
60 | <ul> | ||
61 | <li><a href="FAQ.html">FAQ</a></li> | ||
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | ||
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | ||
65 | </ul> | ||
66 | </div> | ||
67 | <h1 id="shaarli-installation">Shaarli installation</h1> | ||
68 | <p>Once Shaarli is downloaded and installed behind a web server, open it in your favorite browser.</p> | ||
69 | <p><img src="http://i.imgur.com/wuMpDSN.png" alt="install screenshot" /><a href=".html"></a></p> | ||
70 | <p>Setup your Shaarli installation, and it's ready to use!</p> | ||
71 | </body> | ||
72 | </html> | ||
diff --git a/doc/Shaarli-installation.md b/doc/Shaarli-installation.md deleted file mode 100644 index be9726e0..00000000 --- a/doc/Shaarli-installation.md +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | #Shaarli installation | ||
2 | Once Shaarli is downloaded and installed behind a web server, open it in your favorite browser. | ||
3 | |||
4 | [](.html) | ||
5 | |||
6 | Setup your Shaarli installation, and it's ready to use! | ||
diff --git a/doc/Sharing-button.html b/doc/Sharing-button.html index 3770d8ad..93710efe 100644 --- a/doc/Sharing-button.html +++ b/doc/Sharing-button.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
diff --git a/doc/Static-analysis.html b/doc/Static-analysis.html index 86cb4696..d964e917 100644 --- a/doc/Static-analysis.html +++ b/doc/Static-analysis.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
diff --git a/doc/TODO.html b/doc/TODO.html deleted file mode 100644 index 04224dbf..00000000 --- a/doc/TODO.html +++ /dev/null | |||
@@ -1,74 +0,0 @@ | |||
1 | <!DOCTYPE html> | ||
2 | <html> | ||
3 | <head> | ||
4 | <meta charset="utf-8"> | ||
5 | <meta name="generator" content="pandoc"> | ||
6 | <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> | ||
7 | <title>Shaarli – TODO</title> | ||
8 | <style type="text/css">code{white-space: pre;}</style> | ||
9 | <link rel="stylesheet" href="github-markdown.css"> | ||
10 | <!--[if lt IE 9]> | ||
11 | <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> | ||
12 | <![endif]--> | ||
13 | </head> | ||
14 | <body> | ||
15 | <div id="local-sidebar"> | ||
16 | <ul> | ||
17 | <li><a href="Home.html">Home</a></li> | ||
18 | <li>Installation | ||
19 | <ul> | ||
20 | <li><a href="Download.html">Download</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | ||
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | ||
23 | <li><a href="Server-security.html">Server security</a></li> | ||
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | ||
26 | <li><a href="Plugins.html">Plugins</a></li> | ||
27 | </ul></li> | ||
28 | <li><a href="Docker.html">Docker</a></li> | ||
29 | <li><a href="Usage.html">Usage</a> | ||
30 | <ul> | ||
31 | <li><a href="Sharing-button.html">Sharing button</a> (bookmarklet)</li> | ||
32 | <li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li> | ||
33 | <li><a href="Firefox-share.html">Firefox share</a></li> | ||
34 | <li><a href="RSS-feeds.html">RSS feeds</a></li> | ||
35 | </ul></li> | ||
36 | <li>How To | ||
37 | <ul> | ||
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | ||
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | ||
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | ||
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | ||
43 | <li><a href="Datastore-hacks.html">Datastore hacks</a></li> | ||
44 | </ul></li> | ||
45 | <li><a href="Troubleshooting.html">Troubleshooting</a></li> | ||
46 | <li><a href="Development.html">Development</a> | ||
47 | <ul> | ||
48 | <li><a href="GnuPG-signature.html">GnuPG signature</a></li> | ||
49 | <li><a href="Coding-guidelines.html">Coding guidelines</a></li> | ||
50 | <li><a href="Directory-structure.html">Directory structure</a></li> | ||
51 | <li><a href="3rd-party-libraries.html">3rd party libraries</a></li> | ||
52 | <li><a href="Plugin-System.html">Plugin System</a></li> | ||
53 | <li><a href="Release-Shaarli.html">Release Shaarli</a></li> | ||
54 | <li><a href="Security.html">Security</a></li> | ||
55 | <li><a href="Static-analysis.html">Static analysis</a></li> | ||
56 | <li><a href="Theming.html">Theming</a></li> | ||
57 | <li><a href="Unit-tests.html">Unit tests</a></li> | ||
58 | </ul></li> | ||
59 | <li>About | ||
60 | <ul> | ||
61 | <li><a href="FAQ.html">FAQ</a></li> | ||
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | ||
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | ||
65 | </ul> | ||
66 | </div> | ||
67 | <h1 id="todo">TODO</h1> | ||
68 | <ul> | ||
69 | <li>add more screenshots</li> | ||
70 | <li>improve developer documentation: storage architecture, classes and functions, security handling...</li> | ||
71 | <li>add server configuration examples: lighthttpd</li> | ||
72 | </ul> | ||
73 | </body> | ||
74 | </html> | ||
diff --git a/doc/TODO.md b/doc/TODO.md deleted file mode 100644 index fb72fd57..00000000 --- a/doc/TODO.md +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | #TODO | ||
2 | * add more screenshots | ||
3 | * improve developer documentation: storage architecture, classes and functions, security handling... | ||
4 | * add server configuration examples: lighthttpd | ||
diff --git a/doc/Theming.html b/doc/Theming.html index 27c5d863..7cbf7aef 100644 --- a/doc/Theming.html +++ b/doc/Theming.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
@@ -121,18 +119,20 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
121 | <ul> | 119 | <ul> |
122 | <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> |
123 | </ul></li> | 121 | </ul></li> |
124 | <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> |
125 | <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> |
126 | </ul> | 124 | </ul> |
127 | <h2 id="community-themes-templates">Community themes & templates</h2> | 125 | <h2 id="community-themes-templates">Community themes & templates</h2> |
128 | <ul> | 126 | <ul> |
129 | <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> |
130 | <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> | ||
131 | <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> |
132 | <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> |
133 | <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> | ||
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> | 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> |
135 | <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/mrjovanovic/serious-theme-shaarli">mrjovanovic/serious-theme-shaarli</a> - A serious theme for SHaarli.<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 | <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> |
137 | </ul> | 137 | </ul> |
138 | <h3 id="example-installation-albinomouse-template">Example installation: AlbinoMouse template</h3> | 138 | <h3 id="example-installation-albinomouse-template">Example installation: AlbinoMouse template</h3> |
@@ -142,17 +142,17 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
142 | <li>user sites are enabled, e.g. <code>/home/user/public_html/somedir</code> is served as <code>http://localhost/~user/somedir</code></li> | 142 | <li>user sites are enabled, e.g. <code>/home/user/public_html/somedir</code> is served as <code>http://localhost/~user/somedir</code></li> |
143 | <li><code>http</code> is the name of the Apache user</li> | 143 | <li><code>http</code> is the name of the Apache user</li> |
144 | </ul> | 144 | </ul> |
145 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">cd</span> ~/public_html | 145 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="bu">cd</span> ~/public_html |
146 | 146 | ||
147 | <span class="co"># clone repositories</span> | 147 | <span class="co"># clone repositories</span> |
148 | $ <span class="kw">git</span> clone https://github.com/shaarli/Shaarli.git shaarli | 148 | $ <span class="fu">git</span> clone https://github.com/shaarli/Shaarli.git shaarli |
149 | $ <span class="kw">pushd</span> shaarli/tpl | 149 | $ <span class="bu">pushd</span> shaarli/tpl |
150 | $ <span class="kw">git</span> clone https://github.com/alexisju/albinomouse-template.git | 150 | $ <span class="fu">git</span> clone https://github.com/alexisju/albinomouse-template.git |
151 | $ <span class="kw">popd</span> | 151 | $ <span class="bu">popd</span> |
152 | 152 | ||
153 | <span class="co"># set access rights for Apache</span> | 153 | <span class="co"># set access rights for Apache</span> |
154 | $ <span class="kw">chgrp</span> -R http shaarli | 154 | $ <span class="fu">chgrp</span> -R http shaarli |
155 | $ <span class="kw">chmod</span> g+rwx shaarli shaarli/cache shaarli/data shaarli/pagecache shaarli/tmp</code></pre></div> | 155 | $ <span class="fu">chmod</span> g+rwx shaarli shaarli/cache shaarli/data shaarli/pagecache shaarli/tmp</code></pre></div> |
156 | <p>Get config written:</p> | 156 | <p>Get config written:</p> |
157 | <ul> | 157 | <ul> |
158 | <li>go to the freshly installed site</li> | 158 | <li>go to the freshly installed site</li> |
@@ -161,6 +161,6 @@ $ <span class="kw">chmod</span> g+rwx shaarli shaarli/cache shaarli/data shaarli | |||
161 | </ul> | 161 | </ul> |
162 | <p>Edit Shaarli's <a href="configuration%7CShaarli-configuration.html">configuration|Shaarli configuration</a>:</p> | 162 | <p>Edit Shaarli's <a href="configuration%7CShaarli-configuration.html">configuration|Shaarli configuration</a>:</p> |
163 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># the file should be owned by Apache, thus not writeable => sudo</span> | 163 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># the file should be owned by Apache, thus not writeable => sudo</span> |
164 | $ <span class="kw">sudo</span> sed -i s=tpl=tpl/albinomouse-template=g shaarli/data/config.php</code></pre></div> | 164 | $ <span class="fu">sudo</span> sed -i s=tpl=tpl/albinomouse-template=g shaarli/data/config.php</code></pre></div> |
165 | </body> | 165 | </body> |
166 | </html> | 166 | </html> |
diff --git a/doc/Theming.md b/doc/Theming.md index 9dfdcf9f..a21899c2 100644 --- a/doc/Theming.md +++ b/doc/Theming.md | |||
@@ -16,19 +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 | - [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 | - [mrjovanovic/serious-theme-shaarli](https://github.com/mrjovanovic/serious-theme-shaarli) - A serious theme for SHaarli.[](.html) |
32 | - [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) |
33 | 35 | ||
34 | ### Example installation: AlbinoMouse template | 36 | ### Example installation: AlbinoMouse template |
diff --git a/doc/Troubleshooting.html b/doc/Troubleshooting.html index 3de8ad1e..ed1c6f09 100644 --- a/doc/Troubleshooting.html +++ b/doc/Troubleshooting.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
@@ -132,6 +130,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
132 | <ul> | 130 | <ul> |
133 | <li>false (default): real referer</li> | 131 | <li>false (default): real referer</li> |
134 | <li>true: spoof referer (use target URI as referer)</li> | 132 | <li>true: spoof referer (use target URI as referer)</li> |
133 | <li>known to break some functionality in Shaarli</li> | ||
135 | </ul> | 134 | </ul> |
136 | <p><code>network.http.referer.trimmingPolicy</code> - trim the URI not to send a full Referer</p> | 135 | <p><code>network.http.referer.trimmingPolicy</code> - trim the URI not to send a full Referer</p> |
137 | <ul> | 136 | <ul> |
@@ -140,7 +139,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
140 | <li>2: scheme+host+port</li> | 139 | <li>2: scheme+host+port</li> |
141 | </ul> | 140 | </ul> |
142 | <h3 id="firefox-localhost-and-redirections">Firefox, localhost and redirections</h3> | 141 | <h3 id="firefox-localhost-and-redirections">Firefox, localhost and redirections</h3> |
143 | <p><code>localhost</code> is not a proper Fully Qualified Domain Name (FQDN); if Firefox has been set up to spoof referers, or anly accept requests from the same base domain/host, Shaarli redirections will not work properly.</p> | 142 | <p><code>localhost</code> is not a proper Fully Qualified Domain Name (FQDN); if Firefox has been set up to spoof referers, or only accept requests from the same base domain/host, Shaarli redirections will not work properly.</p> |
144 | <p>To solve this, assign a local domain to your host, e.g.</p> | 143 | <p>To solve this, assign a local domain to your host, e.g.</p> |
145 | <pre><code>127.0.0.1 localhost desktop localhost.lan | 144 | <pre><code>127.0.0.1 localhost desktop localhost.lan |
146 | ::1 localhost desktop localhost.lan</code></pre> | 145 | ::1 localhost desktop localhost.lan</code></pre> |
diff --git a/doc/Troubleshooting.md b/doc/Troubleshooting.md index e91fe846..8e30fce5 100644 --- a/doc/Troubleshooting.md +++ b/doc/Troubleshooting.md | |||
@@ -25,6 +25,7 @@ HTTP settings are available by browsing `about:config`, here are the available s | |||
25 | `network.http.referer.spoofSource` - Referer spoofing (~faking) | 25 | `network.http.referer.spoofSource` - Referer spoofing (~faking) |
26 | - false (default): real referer | 26 | - false (default): real referer |
27 | - true: spoof referer (use target URI as referer) | 27 | - true: spoof referer (use target URI as referer) |
28 | - known to break some functionality in Shaarli | ||
28 | 29 | ||
29 | `network.http.referer.trimmingPolicy` - trim the URI not to send a full Referer | 30 | `network.http.referer.trimmingPolicy` - trim the URI not to send a full Referer |
30 | - 0 (default): send full URI | 31 | - 0 (default): send full URI |
@@ -32,7 +33,7 @@ HTTP settings are available by browsing `about:config`, here are the available s | |||
32 | - 2: scheme+host+port | 33 | - 2: scheme+host+port |
33 | 34 | ||
34 | ### Firefox, localhost and redirections | 35 | ### Firefox, localhost and redirections |
35 | `localhost` is not a proper Fully Qualified Domain Name (FQDN); if Firefox has been set up to spoof referers, or anly accept requests from the same base domain/host, Shaarli redirections will not work properly. | 36 | `localhost` is not a proper Fully Qualified Domain Name (FQDN); if Firefox has been set up to spoof referers, or only accept requests from the same base domain/host, Shaarli redirections will not work properly. |
36 | 37 | ||
37 | To solve this, assign a local domain to your host, e.g. | 38 | To solve this, assign a local domain to your host, e.g. |
38 | ``` | 39 | ``` |
diff --git a/doc/Unit-tests.html b/doc/Unit-tests.html index 7934e346..266fd33a 100644 --- a/doc/Unit-tests.html +++ b/doc/Unit-tests.html | |||
@@ -52,13 +52,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
52 | <div id="local-sidebar"> | 52 | <div id="local-sidebar"> |
53 | <ul> | 53 | <ul> |
54 | <li><a href="Home.html">Home</a></li> | 54 | <li><a href="Home.html">Home</a></li> |
55 | <li>Installation | 55 | <li>Setup |
56 | <ul> | 56 | <ul> |
57 | <li><a href="Download.html">Download</a></li> | 57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
58 | <li><a href="Server-requirements.html">Server requirements</a></li> | 59 | <li><a href="Server-requirements.html">Server requirements</a></li> |
59 | <li><a href="Server-configuration.html">Server configuration</a></li> | 60 | <li><a href="Server-configuration.html">Server configuration</a></li> |
60 | <li><a href="Server-security.html">Server security</a></li> | 61 | <li><a href="Server-security.html">Server security</a></li> |
61 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
63 | <li><a href="Plugins.html">Plugins</a></li> | 63 | <li><a href="Plugins.html">Plugins</a></li> |
64 | </ul></li> | 64 | </ul></li> |
@@ -73,7 +73,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
73 | <li>How To | 73 | <li>How To |
74 | <ul> | 74 | <ul> |
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
76 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
77 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
78 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
79 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -97,7 +96,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
97 | <ul> | 96 | <ul> |
98 | <li><a href="FAQ.html">FAQ</a></li> | 97 | <li><a href="FAQ.html">FAQ</a></li> |
99 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
100 | <li><a href="TODO.html">TODO</a></li> | ||
101 | </ul></li> | 99 | </ul></li> |
102 | </ul> | 100 | </ul> |
103 | </div> | 101 | </div> |
@@ -111,87 +109,87 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf | |||
111 | </ul> | 109 | </ul> |
112 | <h4 id="sample-usage">Sample usage</h4> | 110 | <h4 id="sample-usage">Sample usage</h4> |
113 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># system-wide version</span> | 111 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># system-wide version</span> |
114 | $ <span class="kw">composer</span> install | 112 | $ <span class="ex">composer</span> install |
115 | $ <span class="kw">composer</span> update | 113 | $ <span class="ex">composer</span> update |
116 | 114 | ||
117 | <span class="co"># local version</span> | 115 | <span class="co"># local version</span> |
118 | $ <span class="kw">php</span> composer.phar self-update | 116 | $ <span class="ex">php</span> composer.phar self-update |
119 | $ <span class="kw">php</span> composer.phar install | 117 | $ <span class="ex">php</span> composer.phar install |
120 | $ <span class="kw">php</span> composer.phar update</code></pre></div> | 118 | $ <span class="ex">php</span> composer.phar update</code></pre></div> |
121 | <h4 id="install-shaarli-dev-dependencies">Install Shaarli dev dependencies</h4> | 119 | <h4 id="install-shaarli-dev-dependencies">Install Shaarli dev dependencies</h4> |
122 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">cd</span> /path/to/shaarli | 120 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="bu">cd</span> /path/to/shaarli |
123 | $ <span class="kw">composer</span> update</code></pre></div> | 121 | $ <span class="ex">composer</span> update</code></pre></div> |
124 | <h4 id="install-and-enable-xdebug-to-generate-phpunit-coverage-reports">Install and enable Xdebug to generate PHPUnit coverage reports</h4> | 122 | <h4 id="install-and-enable-xdebug-to-generate-phpunit-coverage-reports">Install and enable Xdebug to generate PHPUnit coverage reports</h4> |
125 | <p>For Debian-based distros:</p> | 123 | <p>For Debian-based distros:</p> |
126 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">aptitude</span> install php5-xdebug</code></pre></div> | 124 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">aptitude</span> install php5-xdebug</code></pre></div> |
127 | <p>For ArchLinux:</p> | 125 | <p>For ArchLinux:</p> |
128 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">pacman</span> -S xdebug</code></pre></div> | 126 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">pacman</span> -S xdebug</code></pre></div> |
129 | <p>Then add the following line to <code>/etc/php/php.ini</code>:</p> | 127 | <p>Then add the following line to <code>/etc/php/php.ini</code>:</p> |
130 | <div class="sourceCode"><pre class="sourceCode ini"><code class="sourceCode ini"><span class="dt">zend_extension</span><span class="ot">=</span><span class="st">xdebug.so</span></code></pre></div> | 128 | <div class="sourceCode"><pre class="sourceCode ini"><code class="sourceCode ini"><span class="dt">zend_extension</span><span class="ot">=</span><span class="st">xdebug.so</span></code></pre></div> |
131 | <h4 id="run-unit-tests">Run unit tests</h4> | 129 | <h4 id="run-unit-tests">Run unit tests</h4> |
132 | <p>Successful test suite:</p> | 130 | <p>Successful test suite:</p> |
133 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">make</span> test | 131 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">make</span> test |
134 | 132 | ||
135 | <span class="kw">-------</span> | 133 | <span class="ex">-------</span> |
136 | <span class="kw">PHPUNIT</span> | 134 | <span class="ex">PHPUNIT</span> |
137 | <span class="kw">-------</span> | 135 | <span class="ex">-------</span> |
138 | <span class="kw">PHPUnit</span> 4.6.9 by Sebastian Bergmann and contributors. | 136 | <span class="ex">PHPUnit</span> 4.6.9 by Sebastian Bergmann and contributors. |
139 | 137 | ||
140 | <span class="kw">Configuration</span> read from /home/virtualtam/public_html/shaarli/phpunit.xml | 138 | <span class="ex">Configuration</span> read from /home/virtualtam/public_html/shaarli/phpunit.xml |
141 | 139 | ||
142 | <span class="kw">....................................</span> | 140 | <span class="ex">....................................</span> |
143 | 141 | ||
144 | <span class="kw">Time</span>: 759 ms, Memory: 8.25Mb | 142 | <span class="ex">Time</span>: 759 ms, Memory: 8.25Mb |
145 | 143 | ||
146 | <span class="kw">OK</span> (36 tests, 65 assertions)</code></pre></div> | 144 | <span class="ex">OK</span> (36 tests, 65 assertions)</code></pre></div> |
147 | <p>Test suite with failures and errors:</p> | 145 | <p>Test suite with failures and errors:</p> |
148 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">make</span> test | 146 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">make</span> test |
149 | <span class="kw">-------</span> | 147 | <span class="ex">-------</span> |
150 | <span class="kw">PHPUNIT</span> | 148 | <span class="ex">PHPUNIT</span> |
151 | <span class="kw">-------</span> | 149 | <span class="ex">-------</span> |
152 | <span class="kw">PHPUnit</span> 4.6.9 by Sebastian Bergmann and contributors. | 150 | <span class="ex">PHPUnit</span> 4.6.9 by Sebastian Bergmann and contributors. |
153 | 151 | ||
154 | <span class="kw">Configuration</span> read from /home/virtualtam/public_html/shaarli/phpunit.xml | 152 | <span class="ex">Configuration</span> read from /home/virtualtam/public_html/shaarli/phpunit.xml |
155 | 153 | ||
156 | <span class="kw">E..FF...............................</span> | 154 | <span class="ex">E..FF...............................</span> |
157 | 155 | ||
158 | <span class="kw">Time</span>: 802 ms, Memory: 8.25Mb | 156 | <span class="ex">Time</span>: 802 ms, Memory: 8.25Mb |
159 | 157 | ||
160 | <span class="kw">There</span> was 1 error: | 158 | <span class="ex">There</span> was 1 error: |
161 | 159 | ||
162 | <span class="kw">1</span>) <span class="kw">LinkDBTest</span>::testConstructLoggedIn | 160 | <span class="ex">1</span>) <span class="ex">LinkDBTest</span>::testConstructLoggedIn |
163 | <span class="kw">Missing</span> argument 2 for LinkDB::__construct(), <span class="kw">called</span> in /home/virtualtam/public_html/shaarli/tests/Link\ | 161 | <span class="ex">Missing</span> argument 2 for LinkDB::__construct(), <span class="ex">called</span> in /home/virtualtam/public_html/shaarli/tests/Link\ |
164 | DBTest.php on line 79 and defined | 162 | DBTest.php on line 79 and defined |
165 | 163 | ||
166 | <span class="kw">/home/virtualtam/public_html/shaarli/application</span>/LinkDB.php:<span class="kw">58</span> | 164 | <span class="ex">/home/virtualtam/public_html/shaarli/application</span>/LinkDB.php:<span class="ex">58</span> |
167 | <span class="kw">/home/virtualtam/public_html/shaarli/tests</span>/LinkDBTest.php:<span class="kw">79</span> | 165 | <span class="ex">/home/virtualtam/public_html/shaarli/tests</span>/LinkDBTest.php:<span class="ex">79</span> |
168 | 166 | ||
169 | <span class="kw">--</span> | 167 | <span class="ex">--</span> |
170 | 168 | ||
171 | <span class="kw">There</span> were 2 failures: | 169 | <span class="ex">There</span> were 2 failures: |
172 | 170 | ||
173 | <span class="kw">1</span>) <span class="kw">LinkDBTest</span>::testCheckDBNew | 171 | <span class="ex">1</span>) <span class="ex">LinkDBTest</span>::testCheckDBNew |
174 | <span class="kw">Failed</span> asserting that two strings are equal. | 172 | <span class="ex">Failed</span> asserting that two strings are equal. |
175 | <span class="kw">---</span> Expected | 173 | <span class="ex">---</span> Expected |
176 | <span class="kw">+++</span> Actual | 174 | <span class="ex">+++</span> Actual |
177 | <span class="kw">@@</span> @@ | 175 | <span class="ex">@@</span> @@ |
178 | <span class="kw">-</span><span class="st">'e3edea8ea7bb50be4bcb404df53fbb4546a7156e'</span> | 176 | <span class="ex">-</span><span class="st">'e3edea8ea7bb50be4bcb404df53fbb4546a7156e'</span> |
179 | <span class="kw">+</span><span class="st">'85eab0c610d4f68025f6ed6e6b6b5fabd4b55834'</span> | 177 | <span class="ex">+</span><span class="st">'85eab0c610d4f68025f6ed6e6b6b5fabd4b55834'</span> |
180 | 178 | ||
181 | <span class="kw">/home/virtualtam/public_html/shaarli/tests</span>/LinkDBTest.php:<span class="kw">121</span> | 179 | <span class="ex">/home/virtualtam/public_html/shaarli/tests</span>/LinkDBTest.php:<span class="ex">121</span> |
182 | 180 | ||
183 | <span class="kw">2</span>) <span class="kw">LinkDBTest</span>::testCheckDBLoad | 181 | <span class="ex">2</span>) <span class="ex">LinkDBTest</span>::testCheckDBLoad |
184 | <span class="kw">Failed</span> asserting that two strings are equal. | 182 | <span class="ex">Failed</span> asserting that two strings are equal. |
185 | <span class="kw">---</span> Expected | 183 | <span class="ex">---</span> Expected |
186 | <span class="kw">+++</span> Actual | 184 | <span class="ex">+++</span> Actual |
187 | <span class="kw">@@</span> @@ | 185 | <span class="ex">@@</span> @@ |
188 | <span class="kw">-</span><span class="st">'e3edea8ea7bb50be4bcb404df53fbb4546a7156e'</span> | 186 | <span class="ex">-</span><span class="st">'e3edea8ea7bb50be4bcb404df53fbb4546a7156e'</span> |
189 | <span class="kw">+</span><span class="st">'85eab0c610d4f68025f6ed6e6b6b5fabd4b55834'</span> | 187 | <span class="ex">+</span><span class="st">'85eab0c610d4f68025f6ed6e6b6b5fabd4b55834'</span> |
190 | 188 | ||
191 | <span class="kw">/home/virtualtam/public_html/shaarli/tests</span>/LinkDBTest.php:<span class="kw">133</span> | 189 | <span class="ex">/home/virtualtam/public_html/shaarli/tests</span>/LinkDBTest.php:<span class="ex">133</span> |
192 | 190 | ||
193 | <span class="kw">FAILURES</span>! | 191 | <span class="ex">FAILURES</span>! |
194 | <span class="kw">Tests</span>: 36, Assertions: 63, Errors: 1, Failures: 2.</code></pre></div> | 192 | <span class="ex">Tests</span>: 36, Assertions: 63, Errors: 1, Failures: 2.</code></pre></div> |
195 | <h4 id="test-results-and-coverage">Test results and coverage</h4> | 193 | <h4 id="test-results-and-coverage">Test results and coverage</h4> |
196 | <p>By default, PHPUnit will run all suitable tests found under the <code>tests</code> directory.</p> | 194 | <p>By default, PHPUnit will run all suitable tests found under the <code>tests</code> directory.</p> |
197 | <p>Each test has 3 possible outcomes:</p> | 195 | <p>Each test has 3 possible outcomes:</p> |
diff --git a/doc/Upgrade-and-migration.html b/doc/Upgrade-and-migration.html new file mode 100644 index 00000000..a5b041d5 --- /dev/null +++ b/doc/Upgrade-and-migration.html | |||
@@ -0,0 +1,242 @@ | |||
1 | <!DOCTYPE html> | ||
2 | <html> | ||
3 | <head> | ||
4 | <meta charset="utf-8"> | ||
5 | <meta name="generator" content="pandoc"> | ||
6 | <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> | ||
7 | <title>Shaarli – Upgrade and migration</title> | ||
8 | <style type="text/css">code{white-space: pre;}</style> | ||
9 | <style type="text/css"> | ||
10 | div.sourceCode { overflow-x: auto; } | ||
11 | table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode { | ||
12 | margin: 0; padding: 0; vertical-align: baseline; border: none; } | ||
13 | table.sourceCode { width: 100%; line-height: 100%; } | ||
14 | td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; } | ||
15 | td.sourceCode { padding-left: 5px; } | ||
16 | code > span.kw { color: #007020; font-weight: bold; } /* Keyword */ | ||
17 | code > span.dt { color: #902000; } /* DataType */ | ||
18 | code > span.dv { color: #40a070; } /* DecVal */ | ||
19 | code > span.bn { color: #40a070; } /* BaseN */ | ||
20 | code > span.fl { color: #40a070; } /* Float */ | ||
21 | code > span.ch { color: #4070a0; } /* Char */ | ||
22 | code > span.st { color: #4070a0; } /* String */ | ||
23 | code > span.co { color: #60a0b0; font-style: italic; } /* Comment */ | ||
24 | code > span.ot { color: #007020; } /* Other */ | ||
25 | code > span.al { color: #ff0000; font-weight: bold; } /* Alert */ | ||
26 | code > span.fu { color: #06287e; } /* Function */ | ||
27 | code > span.er { color: #ff0000; font-weight: bold; } /* Error */ | ||
28 | code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */ | ||
29 | code > span.cn { color: #880000; } /* Constant */ | ||
30 | code > span.sc { color: #4070a0; } /* SpecialChar */ | ||
31 | code > span.vs { color: #4070a0; } /* VerbatimString */ | ||
32 | code > span.ss { color: #bb6688; } /* SpecialString */ | ||
33 | code > span.im { } /* Import */ | ||
34 | code > span.va { color: #19177c; } /* Variable */ | ||
35 | code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */ | ||
36 | code > span.op { color: #666666; } /* Operator */ | ||
37 | code > span.bu { } /* BuiltIn */ | ||
38 | code > span.ex { } /* Extension */ | ||
39 | code > span.pp { color: #bc7a00; } /* Preprocessor */ | ||
40 | code > span.at { color: #7d9029; } /* Attribute */ | ||
41 | code > span.do { color: #ba2121; font-style: italic; } /* Documentation */ | ||
42 | code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */ | ||
43 | code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */ | ||
44 | code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */ | ||
45 | </style> | ||
46 | <link rel="stylesheet" href="github-markdown.css"> | ||
47 | <!--[if lt IE 9]> | ||
48 | <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> | ||
49 | <![endif]--> | ||
50 | </head> | ||
51 | <body> | ||
52 | <div id="local-sidebar"> | ||
53 | <ul> | ||
54 | <li><a href="Home.html">Home</a></li> | ||
55 | <li>Setup | ||
56 | <ul> | ||
57 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> | ||
58 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
59 | <li><a href="Server-requirements.html">Server requirements</a></li> | ||
60 | <li><a href="Server-configuration.html">Server configuration</a></li> | ||
61 | <li><a href="Server-security.html">Server security</a></li> | ||
62 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | ||
63 | <li><a href="Plugins.html">Plugins</a></li> | ||
64 | </ul></li> | ||
65 | <li><a href="Docker.html">Docker</a></li> | ||
66 | <li><a href="Usage.html">Usage</a> | ||
67 | <ul> | ||
68 | <li><a href="Sharing-button.html">Sharing button</a> (bookmarklet)</li> | ||
69 | <li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li> | ||
70 | <li><a href="Firefox-share.html">Firefox share</a></li> | ||
71 | <li><a href="RSS-feeds.html">RSS feeds</a></li> | ||
72 | </ul></li> | ||
73 | <li>How To | ||
74 | <ul> | ||
75 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | ||
76 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | ||
77 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | ||
78 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | ||
79 | <li><a href="Datastore-hacks.html">Datastore hacks</a></li> | ||
80 | </ul></li> | ||
81 | <li><a href="Troubleshooting.html">Troubleshooting</a></li> | ||
82 | <li><a href="Development.html">Development</a> | ||
83 | <ul> | ||
84 | <li><a href="GnuPG-signature.html">GnuPG signature</a></li> | ||
85 | <li><a href="Coding-guidelines.html">Coding guidelines</a></li> | ||
86 | <li><a href="Directory-structure.html">Directory structure</a></li> | ||
87 | <li><a href="3rd-party-libraries.html">3rd party libraries</a></li> | ||
88 | <li><a href="Plugin-System.html">Plugin System</a></li> | ||
89 | <li><a href="Release-Shaarli.html">Release Shaarli</a></li> | ||
90 | <li><a href="Security.html">Security</a></li> | ||
91 | <li><a href="Static-analysis.html">Static analysis</a></li> | ||
92 | <li><a href="Theming.html">Theming</a></li> | ||
93 | <li><a href="Unit-tests.html">Unit tests</a></li> | ||
94 | </ul></li> | ||
95 | <li>About | ||
96 | <ul> | ||
97 | <li><a href="FAQ.html">FAQ</a></li> | ||
98 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | ||
99 | </ul></li> | ||
100 | </ul> | ||
101 | </div> | ||
102 | <h1 id="upgrade-and-migration">Upgrade and migration</h1> | ||
103 | <h2 id="preparation">Preparation</h2> | ||
104 | <h3 id="backup-your-data">Backup your data</h3> | ||
105 | <p>Shaarli stores all user data under the <code>data</code> directory:</p> | ||
106 | <ul> | ||
107 | <li><code>data/config.php</code> - main configuration file</li> | ||
108 | <li><code>data/datastore.php</code> - bookmarked links</li> | ||
109 | <li><code>data/ipbans.php</code> - banned IP addresses</li> | ||
110 | </ul> | ||
111 | <p>See <a href="Shaarli-configuration.html">Shaarli configuration</a> for more information about Shaarli resources.</p> | ||
112 | <p>It is recommended to backup this repository <em>before</em> starting updating/upgrading Shaarli:</p> | ||
113 | <ul> | ||
114 | <li>users with SSH access: copy or archive the directory to a temporary location</li> | ||
115 | <li>users with FTP access: download a local copy of your Shaarli installation using your favourite client</li> | ||
116 | </ul> | ||
117 | <h3 id="migrating-data-from-a-previous-installation">Migrating data from a previous installation</h3> | ||
118 | <p>As all user data is kept under <code>data</code>, this is the only directory you need to worry about when migrating to a new installation, which corresponds to the following steps:</p> | ||
119 | <ul> | ||
120 | <li>backup the <code>data</code> directory</li> | ||
121 | <li>install or update Shaarli: | ||
122 | <ul> | ||
123 | <li>fresh installation - see <a href="Download-and-installation.html">Download and installation</a></li> | ||
124 | <li>update - see the following sections</li> | ||
125 | </ul></li> | ||
126 | <li>check or restore the <code>data</code> directory</li> | ||
127 | </ul> | ||
128 | <h2 id="upgrading-from-release-archives">Upgrading from release archives</h2> | ||
129 | <p>All tagged revisions can be downloaded as tarballs or ZIP archives from the <a href="https://github.com/shaarli/Shaarli/releases">releases</a> page.<a href=".html"></a></p> | ||
130 | <p>We <em>recommend</em> using the releases from the <code>stable</code> branch, which are available as:</p> | ||
131 | <ul> | ||
132 | <li>gzipped tarball - <a href="https://github.com/shaarli/Shaarli/archive/stable.tar.gz" class="uri">https://github.com/shaarli/Shaarli/archive/stable.tar.gz</a></li> | ||
133 | <li>ZIP archive - <a href="https://github.com/shaarli/Shaarli/archive/stable.zip" class="uri">https://github.com/shaarli/Shaarli/archive/stable.zip</a></li> | ||
134 | </ul> | ||
135 | <p>Once downloaded, extract the archive locally and update your remote installation (e.g. via FTP) -be sure you keep the contents of the <code>data</code> directory!</p> | ||
136 | <p>After upgrading, access your fresh Shaarli installation from a web browser; the configuration will then be automatically updated, and new settings added to <code>data/config.php</code> (see <a href="Shaarli-configuration.html">Shaarli configuration</a> for more details).</p> | ||
137 | <h2 id="upgrading-with-git">Upgrading with Git</h2> | ||
138 | <h3 id="updating-a-community-shaarli">Updating a community Shaarli</h3> | ||
139 | <p>If you have installed Shaarli from the <a href="Download#clone-with-git-recommended">community Git repository</a>, simply <a href="https://www.git-scm.com/docs/git-pull">pull new changes</a> from your local clone:<a href=".html"></a></p> | ||
140 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="bu">cd</span> /path/to/shaarli | ||
141 | $ <span class="fu">git</span> pull | ||
142 | |||
143 | <span class="ex">From</span> github.com:shaarli/Shaarli | ||
144 | <span class="ex">*</span> branch master -<span class="op">></span> FETCH_HEAD | ||
145 | <span class="ex">Updating</span> ebd67c6..521f0e6 | ||
146 | <span class="ex">Fast-forward</span> | ||
147 | <span class="ex">application/Url.php</span> <span class="kw">|</span> <span class="ex">1</span> + | ||
148 | <span class="ex">shaarli_version.php</span> <span class="kw">|</span> <span class="ex">2</span> +- | ||
149 | <span class="ex">tests/Url/UrlTest.php</span> <span class="kw">|</span> <span class="ex">1</span> + | ||
150 | <span class="ex">3</span> files changed, 3 insertions(+), <span class="ex">1</span> deletion(-)</code></pre></div> | ||
151 | <p>Shaarli >= <code>v0.8.x</code>: install/update third-party PHP dependencies using <a href="https://getcomposer.org/">Composer</a>:<a href=".html"></a></p> | ||
152 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">composer</span> update --no-dev | ||
153 | |||
154 | <span class="ex">Loading</span> composer repositories with package information | ||
155 | <span class="ex">Updating</span> dependencies | ||
156 | <span class="ex">-</span> Installing shaarli/netscape-bookmark-parser (v1.0.1) | ||
157 | <span class="ex">Downloading</span>: 100%</code></pre></div> | ||
158 | <h3 id="migrating-and-upgrading-from-sebsauvages-repository">Migrating and upgrading from Sebsauvage's repository</h3> | ||
159 | <p>If you have installed Shaarli from <a href="https://github.com/sebsauvage/Shaarli">Sebsauvage's original Git repository</a>, you can use <a href="https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes">Git remotes</a> to update your working copy.<a href=".html"></a></p> | ||
160 | <p>The following guide assumes that:</p> | ||
161 | <ul> | ||
162 | <li>you have a basic knowledge of Git <a href="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell">branching</a> and <a href="https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes">remote repositories</a><a href=".html"></a></li> | ||
163 | <li>the default remote is named <code>origin</code> and points to Sebsauvage's repository</li> | ||
164 | <li>the current branch is <code>master</code> | ||
165 | <ul> | ||
166 | <li>if you have personal branches containing customizations, you will need to <a href="https://git-scm.com/book/en/v2/Git-Branching-Rebasing">rebase them</a> after the upgrade; beware though, a lot of changes have been made since the community fork has been created, so things are very likely to break<embed src=".html" /></li> | ||
167 | </ul></li> | ||
168 | <li>the working copy is clean: | ||
169 | <ul> | ||
170 | <li>no versioned file has been locally modified</li> | ||
171 | <li>no untracked files are present</li> | ||
172 | </ul></li> | ||
173 | </ul> | ||
174 | <h4 id="step-0-show-repository-information">Step 0: show repository information</h4> | ||
175 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="bu">cd</span> /path/to/shaarli | ||
176 | |||
177 | $ <span class="fu">git</span> remote -v | ||
178 | <span class="ex">origin</span> https://github.com/sebsauvage/Shaarli (fetch) | ||
179 | <span class="ex">origin</span> https://github.com/sebsauvage/Shaarli (push) | ||
180 | |||
181 | $ <span class="fu">git</span> branch -vv | ||
182 | <span class="ex">*</span> master 029f75f [origin/master] Update README.md[](.html) | ||
183 | |||
184 | $ <span class="fu">git</span> status | ||
185 | <span class="ex">On</span> branch master | ||
186 | <span class="ex">Your</span> branch is up-to-date with <span class="st">'origin/master'</span>. | ||
187 | <span class="ex">nothing</span> to commit, working directory clean</code></pre></div> | ||
188 | <h4 id="step-1-update-git-remotes">Step 1: update Git remotes</h4> | ||
189 | <pre><code>$ git remote rename origin sebsauvage | ||
190 | $ git remote -v | ||
191 | sebsauvage https://github.com/sebsauvage/Shaarli (fetch) | ||
192 | sebsauvage https://github.com/sebsauvage/Shaarli (push) | ||
193 | |||
194 | $ git remote add origin https://github.com/shaarli/Shaarli | ||
195 | $ git fetch origin | ||
196 | |||
197 | remote: Counting objects: 3015, done. | ||
198 | remote: Compressing objects: 100% (19/19), done. | ||
199 | remote: Total 3015 (delta 446), reused 457 (delta 446), pack-reused 2550 | ||
200 | Receiving objects: 100% (3015/3015), 2.59 MiB | 918.00 KiB/s, done. | ||
201 | Resolving deltas: 100% (1899/1899), completed with 48 local objects. | ||
202 | From https://github.com/shaarli/Shaarli | ||
203 | * [new branch] master -> origin/master[](.html) | ||
204 | * [new branch] stable -> origin/stable[](.html) | ||
205 | [...][](.html) | ||
206 | * [new tag] v0.6.4 -> v0.6.4[](.html) | ||
207 | * [new tag] v0.7.0 -> v0.7.0[](.html)</code></pre> | ||
208 | <h4 id="step-2-use-the-stable-community-branch">Step 2: use the stable community branch</h4> | ||
209 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">git</span> checkout origin/stable -b stable | ||
210 | <span class="ex">Branch</span> stable set up to track remote branch stable from origin. | ||
211 | <span class="ex">Switched</span> to a new branch <span class="st">'stable'</span> | ||
212 | |||
213 | $ <span class="fu">git</span> branch -vv | ||
214 | <span class="ex">master</span> 029f75f [sebsauvage/master] Update README.md[](.html) | ||
215 | <span class="ex">*</span> stable 890afc3 [origin/stable] Merge pull request <span class="co">#509 from ArthurHoaro/v0.6.5[](.html)</span></code></pre></div> | ||
216 | <p>Shaarli >= <code>v0.8.x</code>: install/update third-party PHP dependencies using <a href="https://getcomposer.org/">Composer</a>:<a href=".html"></a></p> | ||
217 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">composer</span> update --no-dev | ||
218 | |||
219 | <span class="ex">Loading</span> composer repositories with package information | ||
220 | <span class="ex">Updating</span> dependencies | ||
221 | <span class="ex">-</span> Installing shaarli/netscape-bookmark-parser (v1.0.1) | ||
222 | <span class="ex">Downloading</span>: 100%</code></pre></div> | ||
223 | <p>Optionally, you can delete information related to the legacy version:</p> | ||
224 | <div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">git</span> branch -D master | ||
225 | <span class="ex">Deleted</span> branch master (was 029f75f)<span class="ex">.</span> | ||
226 | |||
227 | $ <span class="fu">git</span> remote remove sebsauvage | ||
228 | |||
229 | $ <span class="fu">git</span> remote -v | ||
230 | <span class="ex">origin</span> https://github.com/shaarli/Shaarli (fetch) | ||
231 | <span class="ex">origin</span> https://github.com/shaarli/Shaarli (push) | ||
232 | |||
233 | $ <span class="fu">git</span> gc | ||
234 | <span class="ex">Counting</span> objects: 3317, done. | ||
235 | <span class="ex">Delta</span> compression using up to 8 threads. | ||
236 | <span class="ex">Compressing</span> objects: 100% (1237/1237), <span class="kw">done</span><span class="ex">.</span> | ||
237 | <span class="ex">Writing</span> objects: 100% (3317/3317), <span class="kw">done</span><span class="ex">.</span> | ||
238 | <span class="ex">Total</span> 3317 (delta 2050), <span class="ex">reused</span> 3301 (delta 2034)<span class="ex">to</span></code></pre></div> | ||
239 | <h4 id="step-3-configuration">Step 3: configuration</h4> | ||
240 | <p>After migrating, access your fresh Shaarli installation from a web browser; the configuration will then be automatically updated, and new settings added to <code>data/config.php</code> (see <a href="Shaarli-configuration.html">Shaarli configuration</a> for more details).</p> | ||
241 | </body> | ||
242 | </html> | ||
diff --git a/doc/Upgrade-and-migration.md b/doc/Upgrade-and-migration.md new file mode 100644 index 00000000..0bc33824 --- /dev/null +++ b/doc/Upgrade-and-migration.md | |||
@@ -0,0 +1,161 @@ | |||
1 | #Upgrade and migration | ||
2 | ## Preparation | ||
3 | ### Backup your data | ||
4 | |||
5 | Shaarli stores all user data under the `data` directory: | ||
6 | - `data/config.php` - main configuration file | ||
7 | - `data/datastore.php` - bookmarked links | ||
8 | - `data/ipbans.php` - banned IP addresses | ||
9 | |||
10 | See [Shaarli configuration](Shaarli-configuration.html) for more information about Shaarli resources. | ||
11 | |||
12 | It is recommended to backup this repository _before_ starting updating/upgrading Shaarli: | ||
13 | - users with SSH access: copy or archive the directory to a temporary location | ||
14 | - users with FTP access: download a local copy of your Shaarli installation using your favourite client | ||
15 | |||
16 | ### Migrating data from a previous installation | ||
17 | As all user data is kept under `data`, this is the only directory you need to worry about when migrating to a new installation, which corresponds to the following steps: | ||
18 | |||
19 | - backup the `data` directory | ||
20 | - install or update Shaarli: | ||
21 | - fresh installation - see [Download and installation](Download-and-installation.html) | ||
22 | - update - see the following sections | ||
23 | - check or restore the `data` directory | ||
24 | |||
25 | ## Upgrading from release archives | ||
26 | All tagged revisions can be downloaded as tarballs or ZIP archives from the [releases](https://github.com/shaarli/Shaarli/releases) page.[](.html) | ||
27 | |||
28 | We _recommend_ using the releases from the `stable` branch, which are available as: | ||
29 | - gzipped tarball - https://github.com/shaarli/Shaarli/archive/stable.tar.gz | ||
30 | - ZIP archive - https://github.com/shaarli/Shaarli/archive/stable.zip | ||
31 | |||
32 | Once downloaded, extract the archive locally and update your remote installation (e.g. via FTP) -be sure you keep the contents of the `data` directory! | ||
33 | |||
34 | After upgrading, access your fresh Shaarli installation from a web browser; the configuration will then be automatically updated, and new settings added to `data/config.php` (see [Shaarli configuration](Shaarli-configuration.html) for more details). | ||
35 | |||
36 | ## Upgrading with Git | ||
37 | ### Updating a community Shaarli | ||
38 | If you have installed Shaarli from the [community Git repository](Download#clone-with-git-recommended), simply [pull new changes](https://www.git-scm.com/docs/git-pull) from your local clone:[](.html) | ||
39 | |||
40 | ```bash | ||
41 | $ cd /path/to/shaarli | ||
42 | $ git pull | ||
43 | |||
44 | From github.com:shaarli/Shaarli | ||
45 | * branch master -> FETCH_HEAD | ||
46 | Updating ebd67c6..521f0e6 | ||
47 | Fast-forward | ||
48 | application/Url.php | 1 + | ||
49 | shaarli_version.php | 2 +- | ||
50 | tests/Url/UrlTest.php | 1 + | ||
51 | 3 files changed, 3 insertions(+), 1 deletion(-) | ||
52 | ``` | ||
53 | |||
54 | Shaarli >= `v0.8.x`: install/update third-party PHP dependencies using [Composer](https://getcomposer.org/):[](.html) | ||
55 | |||
56 | ```bash | ||
57 | $ composer update --no-dev | ||
58 | |||
59 | Loading composer repositories with package information | ||
60 | Updating dependencies | ||
61 | - Installing shaarli/netscape-bookmark-parser (v1.0.1) | ||
62 | Downloading: 100% | ||
63 | ``` | ||
64 | |||
65 | ### Migrating and upgrading from Sebsauvage's repository | ||
66 | If you have installed Shaarli from [Sebsauvage's original Git repository](https://github.com/sebsauvage/Shaarli), you can use [Git remotes](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) to update your working copy.[](.html) | ||
67 | |||
68 | The following guide assumes that: | ||
69 | - you have a basic knowledge of Git [branching](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell) and [remote repositories](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes)[](.html) | ||
70 | - the default remote is named `origin` and points to Sebsauvage's repository | ||
71 | - the current branch is `master` | ||
72 | - if you have personal branches containing customizations, you will need to [rebase them](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) after the upgrade; beware though, a lot of changes have been made since the community fork has been created, so things are very likely to break | ||
73 | - the working copy is clean: | ||
74 | - no versioned file has been locally modified | ||
75 | - no untracked files are present | ||
76 | |||
77 | #### Step 0: show repository information | ||
78 | ```bash | ||
79 | $ cd /path/to/shaarli | ||
80 | |||
81 | $ git remote -v | ||
82 | origin https://github.com/sebsauvage/Shaarli (fetch) | ||
83 | origin https://github.com/sebsauvage/Shaarli (push) | ||
84 | |||
85 | $ git branch -vv | ||
86 | * master 029f75f [origin/master] Update README.md[](.html) | ||
87 | |||
88 | $ git status | ||
89 | On branch master | ||
90 | Your branch is up-to-date with 'origin/master'. | ||
91 | nothing to commit, working directory clean | ||
92 | ``` | ||
93 | |||
94 | #### Step 1: update Git remotes | ||
95 | ``` | ||
96 | $ git remote rename origin sebsauvage | ||
97 | $ git remote -v | ||
98 | sebsauvage https://github.com/sebsauvage/Shaarli (fetch) | ||
99 | sebsauvage https://github.com/sebsauvage/Shaarli (push) | ||
100 | |||
101 | $ git remote add origin https://github.com/shaarli/Shaarli | ||
102 | $ git fetch origin | ||
103 | |||
104 | remote: Counting objects: 3015, done. | ||
105 | remote: Compressing objects: 100% (19/19), done. | ||
106 | remote: Total 3015 (delta 446), reused 457 (delta 446), pack-reused 2550 | ||
107 | Receiving objects: 100% (3015/3015), 2.59 MiB | 918.00 KiB/s, done. | ||
108 | Resolving deltas: 100% (1899/1899), completed with 48 local objects. | ||
109 | From https://github.com/shaarli/Shaarli | ||
110 | * [new branch] master -> origin/master[](.html) | ||
111 | * [new branch] stable -> origin/stable[](.html) | ||
112 | [...][](.html) | ||
113 | * [new tag] v0.6.4 -> v0.6.4[](.html) | ||
114 | * [new tag] v0.7.0 -> v0.7.0[](.html) | ||
115 | ``` | ||
116 | |||
117 | #### Step 2: use the stable community branch | ||
118 | |||
119 | ```bash | ||
120 | $ git checkout origin/stable -b stable | ||
121 | Branch stable set up to track remote branch stable from origin. | ||
122 | Switched to a new branch 'stable' | ||
123 | |||
124 | $ git branch -vv | ||
125 | master 029f75f [sebsauvage/master] Update README.md[](.html) | ||
126 | * stable 890afc3 [origin/stable] Merge pull request #509 from ArthurHoaro/v0.6.5[](.html) | ||
127 | ``` | ||
128 | |||
129 | Shaarli >= `v0.8.x`: install/update third-party PHP dependencies using [Composer](https://getcomposer.org/):[](.html) | ||
130 | |||
131 | ```bash | ||
132 | $ composer update --no-dev | ||
133 | |||
134 | Loading composer repositories with package information | ||
135 | Updating dependencies | ||
136 | - Installing shaarli/netscape-bookmark-parser (v1.0.1) | ||
137 | Downloading: 100% | ||
138 | ``` | ||
139 | |||
140 | Optionally, you can delete information related to the legacy version: | ||
141 | |||
142 | ```bash | ||
143 | $ git branch -D master | ||
144 | Deleted branch master (was 029f75f). | ||
145 | |||
146 | $ git remote remove sebsauvage | ||
147 | |||
148 | $ git remote -v | ||
149 | origin https://github.com/shaarli/Shaarli (fetch) | ||
150 | origin https://github.com/shaarli/Shaarli (push) | ||
151 | |||
152 | $ git gc | ||
153 | Counting objects: 3317, done. | ||
154 | Delta compression using up to 8 threads. | ||
155 | Compressing objects: 100% (1237/1237), done. | ||
156 | Writing objects: 100% (3317/3317), done. | ||
157 | Total 3317 (delta 2050), reused 3301 (delta 2034)to | ||
158 | ``` | ||
159 | |||
160 | #### Step 3: configuration | ||
161 | After migrating, access your fresh Shaarli installation from a web browser; the configuration will then be automatically updated, and new settings added to `data/config.php` (see [Shaarli configuration](Shaarli-configuration.html) for more details). | ||
diff --git a/doc/Upgrade-from-original-sebsauvage-Shaarli.html b/doc/Upgrade-from-original-sebsauvage-Shaarli.html deleted file mode 100644 index db69a0ed..00000000 --- a/doc/Upgrade-from-original-sebsauvage-Shaarli.html +++ /dev/null | |||
@@ -1,74 +0,0 @@ | |||
1 | <!DOCTYPE html> | ||
2 | <html> | ||
3 | <head> | ||
4 | <meta charset="utf-8"> | ||
5 | <meta name="generator" content="pandoc"> | ||
6 | <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> | ||
7 | <title>Shaarli – Upgrade from original sebsauvage Shaarli</title> | ||
8 | <style type="text/css">code{white-space: pre;}</style> | ||
9 | <link rel="stylesheet" href="github-markdown.css"> | ||
10 | <!--[if lt IE 9]> | ||
11 | <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> | ||
12 | <![endif]--> | ||
13 | </head> | ||
14 | <body> | ||
15 | <div id="local-sidebar"> | ||
16 | <ul> | ||
17 | <li><a href="Home.html">Home</a></li> | ||
18 | <li>Installation | ||
19 | <ul> | ||
20 | <li><a href="Download.html">Download</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | ||
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | ||
23 | <li><a href="Server-security.html">Server security</a></li> | ||
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | ||
26 | <li><a href="Plugins.html">Plugins</a></li> | ||
27 | </ul></li> | ||
28 | <li><a href="Docker.html">Docker</a></li> | ||
29 | <li><a href="Usage.html">Usage</a> | ||
30 | <ul> | ||
31 | <li><a href="Sharing-button.html">Sharing button</a> (bookmarklet)</li> | ||
32 | <li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li> | ||
33 | <li><a href="Firefox-share.html">Firefox share</a></li> | ||
34 | <li><a href="RSS-feeds.html">RSS feeds</a></li> | ||
35 | </ul></li> | ||
36 | <li>How To | ||
37 | <ul> | ||
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | ||
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | ||
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | ||
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | ||
43 | <li><a href="Datastore-hacks.html">Datastore hacks</a></li> | ||
44 | </ul></li> | ||
45 | <li><a href="Troubleshooting.html">Troubleshooting</a></li> | ||
46 | <li><a href="Development.html">Development</a> | ||
47 | <ul> | ||
48 | <li><a href="GnuPG-signature.html">GnuPG signature</a></li> | ||
49 | <li><a href="Coding-guidelines.html">Coding guidelines</a></li> | ||
50 | <li><a href="Directory-structure.html">Directory structure</a></li> | ||
51 | <li><a href="3rd-party-libraries.html">3rd party libraries</a></li> | ||
52 | <li><a href="Plugin-System.html">Plugin System</a></li> | ||
53 | <li><a href="Release-Shaarli.html">Release Shaarli</a></li> | ||
54 | <li><a href="Security.html">Security</a></li> | ||
55 | <li><a href="Static-analysis.html">Static analysis</a></li> | ||
56 | <li><a href="Theming.html">Theming</a></li> | ||
57 | <li><a href="Unit-tests.html">Unit tests</a></li> | ||
58 | </ul></li> | ||
59 | <li>About | ||
60 | <ul> | ||
61 | <li><a href="FAQ.html">FAQ</a></li> | ||
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | ||
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | ||
65 | </ul> | ||
66 | </div> | ||
67 | <h1 id="upgrade-from-original-sebsauvage-shaarli">Upgrade from original sebsauvage Shaarli</h1> | ||
68 | <ul> | ||
69 | <li>Backup your original <code>data/</code> directory.</li> | ||
70 | <li><a href="https://github.com/shaarli/Shaarli#installation--upgrade">Install</a> and setup the Shaarli community fork.<a href=".html"></a></li> | ||
71 | <li>Copy your original <code>data</code> directory over the new installation.</li> | ||
72 | </ul> | ||
73 | </body> | ||
74 | </html> | ||
diff --git a/doc/Upgrade-from-original-sebsauvage-Shaarli.md b/doc/Upgrade-from-original-sebsauvage-Shaarli.md deleted file mode 100644 index 6ae0c67b..00000000 --- a/doc/Upgrade-from-original-sebsauvage-Shaarli.md +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | #Upgrade from original sebsauvage Shaarli | ||
2 | * Backup your original `data/` directory. | ||
3 | * [Install](https://github.com/shaarli/Shaarli#installation--upgrade) and setup the Shaarli community fork.[](.html) | ||
4 | * Copy your original `data` directory over the new installation. | ||
diff --git a/doc/Usage.html b/doc/Usage.html index 2befaa02..63f21d93 100644 --- a/doc/Usage.html +++ b/doc/Usage.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,7 +59,6 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
diff --git a/doc/_Footer.html b/doc/_Footer.html index a054cc53..e8a62d2a 100644 --- a/doc/_Footer.html +++ b/doc/_Footer.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,11 +59,10 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
67 | <h1 id="footer-shaarli-the-personal-minimalist-super-fast-no-database-delicious-clone">_Footer<br /> | 65 | <h1 id="footer-shaarli-the-personal-minimalist-super-fast-database-free-bookmarking-service">_Footer<br /> |
68 | <em>Shaarli, the personal, minimalist, super-fast, no-database delicious clone</em></h1> | 66 | <em>Shaarli, the personal, minimalist, super-fast, database-free bookmarking service</em></h1> |
69 | </body> | 67 | </body> |
70 | </html> | 68 | </html> |
diff --git a/doc/_Footer.md b/doc/_Footer.md index 29c39bb6..50fa4f56 100644 --- a/doc/_Footer.md +++ b/doc/_Footer.md | |||
@@ -1,2 +1,2 @@ | |||
1 | #_Footer | 1 | #_Footer |
2 | _Shaarli, the personal, minimalist, super-fast, no-database delicious clone_ | 2 | _Shaarli, the personal, minimalist, super-fast, database-free bookmarking service_ |
diff --git a/doc/_Sidebar.html b/doc/_Sidebar.html index 89c2cf8a..bb6dad93 100644 --- a/doc/_Sidebar.html +++ b/doc/_Sidebar.html | |||
@@ -15,13 +15,13 @@ | |||
15 | <div id="local-sidebar"> | 15 | <div id="local-sidebar"> |
16 | <ul> | 16 | <ul> |
17 | <li><a href="Home.html">Home</a></li> | 17 | <li><a href="Home.html">Home</a></li> |
18 | <li>Installation | 18 | <li>Setup |
19 | <ul> | 19 | <ul> |
20 | <li><a href="Download.html">Download</a></li> | 20 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
21 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
21 | <li><a href="Server-requirements.html">Server requirements</a></li> | 22 | <li><a href="Server-requirements.html">Server requirements</a></li> |
22 | <li><a href="Server-configuration.html">Server configuration</a></li> | 23 | <li><a href="Server-configuration.html">Server configuration</a></li> |
23 | <li><a href="Server-security.html">Server security</a></li> | 24 | <li><a href="Server-security.html">Server security</a></li> |
24 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 25 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
26 | <li><a href="Plugins.html">Plugins</a></li> | 26 | <li><a href="Plugins.html">Plugins</a></li> |
27 | </ul></li> | 27 | </ul></li> |
@@ -36,7 +36,6 @@ | |||
36 | <li>How To | 36 | <li>How To |
37 | <ul> | 37 | <ul> |
38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 38 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
39 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
40 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 39 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
41 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 40 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
42 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 41 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -60,20 +59,19 @@ | |||
60 | <ul> | 59 | <ul> |
61 | <li><a href="FAQ.html">FAQ</a></li> | 60 | <li><a href="FAQ.html">FAQ</a></li> |
62 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 61 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
63 | <li><a href="TODO.html">TODO</a></li> | ||
64 | </ul></li> | 62 | </ul></li> |
65 | </ul> | 63 | </ul> |
66 | </div> | 64 | </div> |
67 | <h1 id="sidebar">_Sidebar</h1> | 65 | <h1 id="sidebar">_Sidebar</h1> |
68 | <ul> | 66 | <ul> |
69 | <li><a href="Home.html">Home</a></li> | 67 | <li><a href="Home.html">Home</a></li> |
70 | <li>Installation | 68 | <li>Setup |
71 | <ul> | 69 | <ul> |
72 | <li><a href="Download.html">Download</a></li> | 70 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
71 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
73 | <li><a href="Server-requirements.html">Server requirements</a></li> | 72 | <li><a href="Server-requirements.html">Server requirements</a></li> |
74 | <li><a href="Server-configuration.html">Server configuration</a></li> | 73 | <li><a href="Server-configuration.html">Server configuration</a></li> |
75 | <li><a href="Server-security.html">Server security</a></li> | 74 | <li><a href="Server-security.html">Server security</a></li> |
76 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
77 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 75 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
78 | <li><a href="Plugins.html">Plugins</a></li> | 76 | <li><a href="Plugins.html">Plugins</a></li> |
79 | </ul></li> | 77 | </ul></li> |
@@ -88,7 +86,6 @@ | |||
88 | <li>How To | 86 | <li>How To |
89 | <ul> | 87 | <ul> |
90 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 88 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
91 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
92 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 89 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
93 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 90 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
94 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 91 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -112,7 +109,6 @@ | |||
112 | <ul> | 109 | <ul> |
113 | <li><a href="FAQ.html">FAQ</a></li> | 110 | <li><a href="FAQ.html">FAQ</a></li> |
114 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 111 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
115 | <li><a href="TODO.html">TODO</a></li> | ||
116 | </ul></li> | 112 | </ul></li> |
117 | </ul> | 113 | </ul> |
118 | </body> | 114 | </body> |
diff --git a/doc/_Sidebar.md b/doc/_Sidebar.md index 7c71f462..1778e3a3 100644 --- a/doc/_Sidebar.md +++ b/doc/_Sidebar.md | |||
@@ -1,11 +1,11 @@ | |||
1 | #_Sidebar | 1 | #_Sidebar |
2 | - [Home](Home.html) | 2 | - [Home](Home.html) |
3 | - Installation | 3 | - Setup |
4 | - [Download](Download.html) | 4 | - [Download and Installation](Download-and-Installation.html) |
5 | - [Upgrade and migration](Upgrade-and-migration.html) | ||
5 | - [Server requirements](Server-requirements.html) | 6 | - [Server requirements](Server-requirements.html) |
6 | - [Server configuration](Server-configuration.html) | 7 | - [Server configuration](Server-configuration.html) |
7 | - [Server security](Server-security.html) | 8 | - [Server security](Server-security.html) |
8 | - [Shaarli installation](Shaarli-installation.html) | ||
9 | - [Shaarli configuration](Shaarli-configuration.html) | 9 | - [Shaarli configuration](Shaarli-configuration.html) |
10 | - [Plugins](Plugins.html) | 10 | - [Plugins](Plugins.html) |
11 | - [Docker](Docker.html) | 11 | - [Docker](Docker.html) |
@@ -16,7 +16,6 @@ | |||
16 | - [RSS feeds](RSS-feeds.html) | 16 | - [RSS feeds](RSS-feeds.html) |
17 | - How To | 17 | - How To |
18 | - [Backup, restore, import and export](Backup,-restore,-import-and-export.html) | 18 | - [Backup, restore, import and export](Backup,-restore,-import-and-export.html) |
19 | - [Upgrade from original sebsauvage/Shaarli](Upgrade-from-original-sebsauvage/Shaarli.html) | ||
20 | - [Copy an existing installation over SSH and serve it locally](Copy-an-existing-installation-over-SSH-and-serve-it-locally.html) | 19 | - [Copy an existing installation over SSH and serve it locally](Copy-an-existing-installation-over-SSH-and-serve-it-locally.html) |
21 | - [Create and serve multiple Shaarlis (farm)](Create-and-serve-multiple-Shaarlis-(farm).html) | 20 | - [Create and serve multiple Shaarlis (farm)](Create-and-serve-multiple-Shaarlis-(farm).html) |
22 | - [Download CSS styles from an OPML list](Download-CSS-styles-from-an-OPML-list.html) | 21 | - [Download CSS styles from an OPML list](Download-CSS-styles-from-an-OPML-list.html) |
@@ -36,4 +35,3 @@ | |||
36 | - About | 35 | - About |
37 | - [FAQ](FAQ.html) | 36 | - [FAQ](FAQ.html) |
38 | - [Community & Related software](Community-&-Related-software.html) | 37 | - [Community & Related software](Community-&-Related-software.html) |
39 | - [TODO](TODO.html) | ||
diff --git a/doc/sidebar.html b/doc/sidebar.html index 62844581..4dad0161 100644 --- a/doc/sidebar.html +++ b/doc/sidebar.html | |||
@@ -1,13 +1,13 @@ | |||
1 | <div id="local-sidebar"> | 1 | <div id="local-sidebar"> |
2 | <ul> | 2 | <ul> |
3 | <li><a href="Home.html">Home</a></li> | 3 | <li><a href="Home.html">Home</a></li> |
4 | <li>Installation | 4 | <li>Setup |
5 | <ul> | 5 | <ul> |
6 | <li><a href="Download.html">Download</a></li> | 6 | <li><a href="Download-and-Installation.html">Download and Installation</a></li> |
7 | <li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li> | ||
7 | <li><a href="Server-requirements.html">Server requirements</a></li> | 8 | <li><a href="Server-requirements.html">Server requirements</a></li> |
8 | <li><a href="Server-configuration.html">Server configuration</a></li> | 9 | <li><a href="Server-configuration.html">Server configuration</a></li> |
9 | <li><a href="Server-security.html">Server security</a></li> | 10 | <li><a href="Server-security.html">Server security</a></li> |
10 | <li><a href="Shaarli-installation.html">Shaarli installation</a></li> | ||
11 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> | 11 | <li><a href="Shaarli-configuration.html">Shaarli configuration</a></li> |
12 | <li><a href="Plugins.html">Plugins</a></li> | 12 | <li><a href="Plugins.html">Plugins</a></li> |
13 | </ul></li> | 13 | </ul></li> |
@@ -22,7 +22,6 @@ | |||
22 | <li>How To | 22 | <li>How To |
23 | <ul> | 23 | <ul> |
24 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> | 24 | <li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li> |
25 | <li><a href="Upgrade-from-original-sebsauvage/Shaarli.html">Upgrade from original sebsauvage/Shaarli</a></li> | ||
26 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> | 25 | <li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li> |
27 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> | 26 | <li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li> |
28 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> | 27 | <li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li> |
@@ -46,7 +45,6 @@ | |||
46 | <ul> | 45 | <ul> |
47 | <li><a href="FAQ.html">FAQ</a></li> | 46 | <li><a href="FAQ.html">FAQ</a></li> |
48 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> | 47 | <li><a href="Community-&-Related-software.html">Community & Related software</a></li> |
49 | <li><a href="TODO.html">TODO</a></li> | ||
50 | </ul></li> | 48 | </ul></li> |
51 | </ul> | 49 | </ul> |
52 | </div> | 50 | </div> |
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 2ed59b89..d9ef8da7 100644 --- a/docker/development/Dockerfile +++ b/docker/development/Dockerfile | |||
@@ -1,26 +1,36 @@ | |||
1 | FROM debian:jessie | 1 | FROM debian:jessie |
2 | MAINTAINER Shaarli Community | 2 | MAINTAINER Shaarli Community |
3 | 3 | ||
4 | ENV TERM dumb | ||
4 | RUN apt-get update \ | 5 | RUN apt-get update \ |
5 | && apt-get install -y \ | 6 | && apt-get install --no-install-recommends -y \ |
6 | nginx-light php5-fpm php5-gd supervisor \ | 7 | ca-certificates \ |
7 | git nano | 8 | nginx-light \ |
9 | php5-curl \ | ||
10 | php5-fpm \ | ||
11 | php5-gd \ | ||
12 | php5-intl \ | ||
13 | supervisor \ | ||
14 | git \ | ||
15 | nano \ | ||
16 | && apt-get clean | ||
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 | ||
20 | COPY nginx.conf /etc/nginx/nginx.conf | ||
21 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf | ||
8 | 22 | ||
9 | ADD https://getcomposer.org/composer.phar /usr/local/bin/composer | 23 | ADD https://getcomposer.org/composer.phar /usr/local/bin/composer |
10 | RUN chmod 755 /usr/local/bin/composer | 24 | RUN chmod 755 /usr/local/bin/composer |
11 | 25 | ||
12 | COPY nginx.conf /etc/nginx/nginx.conf | ||
13 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf | ||
14 | RUN echo "<?php phpinfo(); ?>" > /var/www/index.php | ||
15 | |||
16 | WORKDIR /var/www | 26 | WORKDIR /var/www |
27 | RUN git clone https://github.com/shaarli/Shaarli.git shaarli \ | ||
28 | && cd shaarli \ | ||
29 | && composer --prefer-dist install | ||
17 | RUN rm -rf html \ | 30 | RUN rm -rf html \ |
18 | && git clone https://github.com/shaarli/Shaarli.git shaarli \ | 31 | && echo "<?php phpinfo(); ?>" > index.php \ |
19 | && chown -R www-data:www-data . | 32 | && chown -R www-data:www-data . |
20 | 33 | ||
21 | WORKDIR /var/www/shaarli | ||
22 | RUN composer install | ||
23 | |||
24 | VOLUME /var/www/shaarli/data | 34 | VOLUME /var/www/shaarli/data |
25 | 35 | ||
26 | EXPOSE 80 | 36 | EXPOSE 80 |
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 3db4eb56..d0509115 100644 --- a/docker/production/Dockerfile +++ b/docker/production/Dockerfile | |||
@@ -1,17 +1,34 @@ | |||
1 | FROM debian:jessie | 1 | FROM debian:jessie |
2 | MAINTAINER Shaarli Community | 2 | MAINTAINER Shaarli Community |
3 | 3 | ||
4 | ENV TERM dumb | ||
4 | RUN apt-get update \ | 5 | RUN apt-get update \ |
5 | && apt-get install -y curl nginx-light php5-fpm php5-gd supervisor | 6 | && apt-get install --no-install-recommends -y \ |
7 | ca-certificates \ | ||
8 | curl \ | ||
9 | nginx-light \ | ||
10 | php5-curl \ | ||
11 | php5-fpm \ | ||
12 | php5-gd \ | ||
13 | php5-intl \ | ||
14 | supervisor \ | ||
15 | && apt-get clean | ||
6 | 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 | ||
7 | COPY nginx.conf /etc/nginx/nginx.conf | 19 | COPY nginx.conf /etc/nginx/nginx.conf |
8 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf | 20 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf |
9 | 21 | ||
22 | ADD https://getcomposer.org/composer.phar /usr/local/bin/composer | ||
23 | RUN chmod 755 /usr/local/bin/composer | ||
24 | |||
10 | WORKDIR /var/www | 25 | WORKDIR /var/www |
11 | RUN rm -rf html \ | 26 | RUN curl -L https://github.com/shaarli/Shaarli/archive/master.tar.gz | tar xzf - \ |
12 | && curl -L https://github.com/shaarli/Shaarli/archive/master.tar.gz | tar xvzf - \ | ||
13 | && mv Shaarli-master shaarli \ | 27 | && mv Shaarli-master shaarli \ |
14 | && chown -R www-data:www-data shaarli | 28 | && cd shaarli \ |
29 | && composer --prefer-dist --no-dev install | ||
30 | RUN rm -rf html \ | ||
31 | && chown -R www-data:www-data . | ||
15 | 32 | ||
16 | VOLUME /var/www/shaarli/data | 33 | VOLUME /var/www/shaarli/data |
17 | 34 | ||
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 2bb3948c..fc9588b0 100644 --- a/docker/production/stable/Dockerfile +++ b/docker/production/stable/Dockerfile | |||
@@ -1,17 +1,34 @@ | |||
1 | FROM debian:jessie | 1 | FROM debian:jessie |
2 | MAINTAINER Shaarli Community | 2 | MAINTAINER Shaarli Community |
3 | 3 | ||
4 | ENV TERM dumb | ||
4 | RUN apt-get update \ | 5 | RUN apt-get update \ |
5 | && apt-get install -y curl nginx-light php5-fpm php5-gd supervisor | 6 | && apt-get install --no-install-recommends -y \ |
7 | ca-certificates \ | ||
8 | curl \ | ||
9 | nginx-light \ | ||
10 | php5-curl \ | ||
11 | php5-fpm \ | ||
12 | php5-gd \ | ||
13 | php5-intl \ | ||
14 | supervisor \ | ||
15 | && apt-get clean | ||
6 | 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 | ||
7 | COPY nginx.conf /etc/nginx/nginx.conf | 19 | COPY nginx.conf /etc/nginx/nginx.conf |
8 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf | 20 | COPY supervised.conf /etc/supervisor/conf.d/supervised.conf |
9 | 21 | ||
22 | ADD https://getcomposer.org/composer.phar /usr/local/bin/composer | ||
23 | RUN chmod 755 /usr/local/bin/composer | ||
24 | |||
10 | WORKDIR /var/www | 25 | WORKDIR /var/www |
11 | RUN rm -rf html \ | 26 | RUN curl -L https://github.com/shaarli/Shaarli/archive/stable.tar.gz | tar xzf - \ |
12 | && curl -L https://github.com/shaarli/Shaarli/archive/stable.tar.gz | tar xvzf - \ | ||
13 | && mv Shaarli-stable shaarli \ | 27 | && mv Shaarli-stable shaarli \ |
14 | && chown -R www-data:www-data shaarli | 28 | && cd shaarli \ |
29 | && composer --prefer-dist --no-dev install | ||
30 | RUN rm -rf html \ | ||
31 | && chown -R www-data:www-data . | ||
15 | 32 | ||
16 | VOLUME /var/www/shaarli/data | 33 | VOLUME /var/www/shaarli/data |
17 | 34 | ||
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 305afddc..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; |
@@ -1156,7 +1160,7 @@ ul.errors { | |||
1156 | } | 1160 | } |
1157 | 1161 | ||
1158 | #pluginsadmin .plugin_parameter { | 1162 | #pluginsadmin .plugin_parameter { |
1159 | padding: 5px 0; | 1163 | padding: 10px 0; |
1160 | border-width: 1px 0; | 1164 | border-width: 1px 0; |
1161 | border-style: solid; | 1165 | border-style: solid; |
1162 | border-color: #c0c0c0; | 1166 | border-color: #c0c0c0; |
@@ -1164,12 +1168,17 @@ ul.errors { | |||
1164 | 1168 | ||
1165 | #pluginsadmin .float_label { | 1169 | #pluginsadmin .float_label { |
1166 | float: left; | 1170 | float: left; |
1167 | width: 20%; | 1171 | width: 40%; |
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,8 +1,8 @@ | |||
1 | <?php | 1 | <?php |
2 | /** | 2 | /** |
3 | * Shaarli v0.7.0 - Shaare your links... | 3 | * Shaarli v0.8.4 - Shaare your links... |
4 | * | 4 | * |
5 | * The personal, minimalist, super-fast, no-database Delicious clone. | 5 | * The personal, minimalist, super-fast, database free, bookmarking service. |
6 | * | 6 | * |
7 | * Friendly fork by the Shaarli community: | 7 | * Friendly fork by the Shaarli community: |
8 | * - https://github.com/shaarli/Shaarli | 8 | * - https://github.com/shaarli/Shaarli |
@@ -22,114 +22,13 @@ if (date_default_timezone_get() == '') { | |||
22 | date_default_timezone_set('UTC'); | 22 | date_default_timezone_set('UTC'); |
23 | } | 23 | } |
24 | 24 | ||
25 | /* ----------------------------------------------------------------------------- | ||
26 | * Hardcoded parameters | ||
27 | * You should not touch any code below (or at your own risks!) | ||
28 | * (These parameters can be overwritten by editing the file /data/config.php) | ||
29 | * ----------------------------------------------------------------------------- | ||
30 | */ | ||
31 | |||
32 | /* | ||
33 | * Shaarli directories & configuration files | ||
34 | */ | ||
35 | // Data subdirectory | ||
36 | $GLOBALS['config']['DATADIR'] = 'data'; | ||
37 | |||
38 | // Main configuration file | ||
39 | $GLOBALS['config']['CONFIG_FILE'] = $GLOBALS['config']['DATADIR'].'/config.php'; | ||
40 | |||
41 | // Link datastore | ||
42 | $GLOBALS['config']['DATASTORE'] = $GLOBALS['config']['DATADIR'].'/datastore.php'; | ||
43 | |||
44 | // Banned IPs | ||
45 | $GLOBALS['config']['IPBANS_FILENAME'] = $GLOBALS['config']['DATADIR'].'/ipbans.php'; | ||
46 | |||
47 | // Processed updates file. | ||
48 | $GLOBALS['config']['UPDATES_FILE'] = $GLOBALS['config']['DATADIR'].'/updates.txt'; | ||
49 | |||
50 | // Access log | ||
51 | $GLOBALS['config']['LOG_FILE'] = $GLOBALS['config']['DATADIR'].'/log.txt'; | ||
52 | |||
53 | // For updates check of Shaarli | ||
54 | $GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt'; | ||
55 | |||
56 | // Set ENABLE_UPDATECHECK to disabled by default. | ||
57 | $GLOBALS['config']['ENABLE_UPDATECHECK'] = false; | ||
58 | |||
59 | // RainTPL cache directory (keep the trailing slash!) | ||
60 | $GLOBALS['config']['RAINTPL_TMP'] = 'tmp/'; | ||
61 | // Raintpl template directory (keep the trailing slash!) | ||
62 | $GLOBALS['config']['RAINTPL_TPL'] = 'tpl/'; | ||
63 | |||
64 | // Thumbnail cache directory | ||
65 | $GLOBALS['config']['CACHEDIR'] = 'cache'; | ||
66 | |||
67 | // Atom & RSS feed cache directory | ||
68 | $GLOBALS['config']['PAGECACHE'] = 'pagecache'; | ||
69 | |||
70 | /* | ||
71 | * Global configuration | ||
72 | */ | ||
73 | // Ban IP after this many failures | ||
74 | $GLOBALS['config']['BAN_AFTER'] = 4; | ||
75 | // Ban duration for IP address after login failures (in seconds) | ||
76 | $GLOBALS['config']['BAN_DURATION'] = 1800; | ||
77 | |||
78 | // Feed options | ||
79 | // Enable RSS permalinks by default. | ||
80 | // This corresponds to the default behavior of shaarli before this was added as an option. | ||
81 | $GLOBALS['config']['ENABLE_RSS_PERMALINKS'] = true; | ||
82 | // If true, an extra "ATOM feed" button will be displayed in the toolbar | ||
83 | $GLOBALS['config']['SHOW_ATOM'] = false; | ||
84 | |||
85 | // Link display options | ||
86 | $GLOBALS['config']['HIDE_PUBLIC_LINKS'] = false; | ||
87 | $GLOBALS['config']['HIDE_TIMESTAMPS'] = false; | ||
88 | $GLOBALS['config']['LINKS_PER_PAGE'] = 20; | ||
89 | |||
90 | // Open Shaarli (true): anyone can add/edit/delete links without having to login | ||
91 | $GLOBALS['config']['OPEN_SHAARLI'] = false; | ||
92 | |||
93 | // Thumbnails | ||
94 | // Display thumbnails in links | ||
95 | $GLOBALS['config']['ENABLE_THUMBNAILS'] = true; | ||
96 | // Store thumbnails in a local cache | ||
97 | $GLOBALS['config']['ENABLE_LOCALCACHE'] = true; | ||
98 | |||
99 | // Update check frequency for Shaarli. 86400 seconds=24 hours | ||
100 | $GLOBALS['config']['UPDATECHECK_BRANCH'] = 'stable'; | ||
101 | $GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400; | ||
102 | |||
103 | $GLOBALS['config']['REDIRECTOR_URLENCODE'] = true; | ||
104 | |||
105 | /* | ||
106 | * Plugin configuration | ||
107 | * | ||
108 | * Warning: order matters! | ||
109 | * | ||
110 | * These settings may be be overriden in: | ||
111 | * - data/config.php | ||
112 | * - each plugin's configuration file | ||
113 | */ | ||
114 | //$GLOBALS['config']['ENABLED_PLUGINS'] = array( | ||
115 | // 'qrcode', 'archiveorg', 'readityourself', 'demo_plugin', 'playvideos', | ||
116 | // 'wallabag', 'markdown', 'addlink_toolbar', | ||
117 | //); | ||
118 | $GLOBALS['config']['ENABLED_PLUGINS'] = array('qrcode'); | ||
119 | |||
120 | // Initialize plugin parameters array. | ||
121 | $GLOBALS['plugins'] = array(); | ||
122 | |||
123 | // PubSubHubbub support. Put an empty string to disable, or put your hub url here to enable. | ||
124 | $GLOBALS['config']['PUBSUBHUB_URL'] = ''; | ||
125 | |||
126 | /* | 25 | /* |
127 | * PHP configuration | 26 | * PHP configuration |
128 | */ | 27 | */ |
129 | define('shaarli_version', '0.7.0'); | 28 | define('shaarli_version', '0.8.2'); |
130 | 29 | ||
131 | // http://server.com/x/shaarli --> /shaarli/ | 30 | // http://server.com/x/shaarli --> /shaarli/ |
132 | 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))); |
133 | 32 | ||
134 | // High execution time in case of problematic imports/exports. | 33 | // High execution time in case of problematic imports/exports. |
135 | ini_set('max_input_time','60'); | 34 | ini_set('max_input_time','60'); |
@@ -144,20 +43,31 @@ error_reporting(E_ALL^E_WARNING); | |||
144 | // See all errors (for debugging only) | 43 | // See all errors (for debugging only) |
145 | //error_reporting(-1); | 44 | //error_reporting(-1); |
146 | 45 | ||
147 | /* | 46 | |
148 | * User configuration | 47 | // 3rd-party libraries |
149 | */ | 48 | if (! file_exists(__DIR__ . '/vendor/autoload.php')) { |
150 | if (is_file($GLOBALS['config']['CONFIG_FILE'])) { | 49 | header('Content-Type: text/plain; charset=utf-8'); |
151 | require_once $GLOBALS['config']['CONFIG_FILE']; | 50 | echo "Error: missing Composer configuration\n\n" |
51 | ."If you installed Shaarli through Git or using the development branch,\n" | ||
52 | ."please refer to the installation documentation to install PHP" | ||
53 | ." dependencies using Composer:\n" | ||
54 | ."- https://github.com/shaarli/Shaarli/wiki/Server-requirements\n" | ||
55 | ."- https://github.com/shaarli/Shaarli/wiki/Download-and-Installation"; | ||
56 | exit; | ||
152 | } | 57 | } |
58 | require_once 'inc/rain.tpl.class.php'; | ||
59 | require_once __DIR__ . '/vendor/autoload.php'; | ||
153 | 60 | ||
154 | // Shaarli library | 61 | // Shaarli library |
155 | require_once 'application/ApplicationUtils.php'; | 62 | require_once 'application/ApplicationUtils.php'; |
156 | require_once 'application/Cache.php'; | 63 | require_once 'application/Cache.php'; |
157 | require_once 'application/CachedPage.php'; | 64 | require_once 'application/CachedPage.php'; |
65 | require_once 'application/config/ConfigManager.php'; | ||
66 | require_once 'application/config/ConfigPlugin.php'; | ||
158 | require_once 'application/FeedBuilder.php'; | 67 | require_once 'application/FeedBuilder.php'; |
159 | require_once 'application/FileUtils.php'; | 68 | require_once 'application/FileUtils.php'; |
160 | require_once 'application/HttpUtils.php'; | 69 | require_once 'application/HttpUtils.php'; |
70 | require_once 'application/Languages.php'; | ||
161 | require_once 'application/LinkDB.php'; | 71 | require_once 'application/LinkDB.php'; |
162 | require_once 'application/LinkFilter.php'; | 72 | require_once 'application/LinkFilter.php'; |
163 | require_once 'application/LinkUtils.php'; | 73 | require_once 'application/LinkUtils.php'; |
@@ -166,7 +76,6 @@ require_once 'application/PageBuilder.php'; | |||
166 | require_once 'application/TimeZone.php'; | 76 | require_once 'application/TimeZone.php'; |
167 | require_once 'application/Url.php'; | 77 | require_once 'application/Url.php'; |
168 | require_once 'application/Utils.php'; | 78 | require_once 'application/Utils.php'; |
169 | require_once 'application/Config.php'; | ||
170 | require_once 'application/PluginManager.php'; | 79 | require_once 'application/PluginManager.php'; |
171 | require_once 'application/Router.php'; | 80 | require_once 'application/Router.php'; |
172 | require_once 'application/Updater.php'; | 81 | require_once 'application/Updater.php'; |
@@ -210,15 +119,18 @@ if (isset($_COOKIE['shaarli']) && !is_session_id_valid($_COOKIE['shaarli'])) { | |||
210 | $_COOKIE['shaarli'] = session_id(); | 119 | $_COOKIE['shaarli'] = session_id(); |
211 | } | 120 | } |
212 | 121 | ||
213 | include "inc/rain.tpl.class.php"; //include Rain TPL | 122 | $conf = new ConfigManager(); |
214 | raintpl::$tpl_dir = $GLOBALS['config']['RAINTPL_TPL']; // template directory | 123 | $conf->setEmpty('general.timezone', date_default_timezone_get()); |
215 | raintpl::$cache_dir = $GLOBALS['config']['RAINTPL_TMP']; // cache directory | 124 | $conf->setEmpty('general.title', 'Shared links on '. escape(index_url($_SERVER))); |
125 | RainTPL::$tpl_dir = $conf->get('resource.raintpl_tpl'); // template directory | ||
126 | RainTPL::$cache_dir = $conf->get('resource.raintpl_tmp'); // cache directory | ||
216 | 127 | ||
217 | $pluginManager = PluginManager::getInstance(); | 128 | $pluginManager = new PluginManager($conf); |
218 | $pluginManager->load($GLOBALS['config']['ENABLED_PLUGINS']); | 129 | $pluginManager->load($conf->get('general.enabled_plugins')); |
219 | 130 | ||
220 | ob_start(); // Output buffering for the page cache. | 131 | date_default_timezone_set($conf->get('general.timezone', 'UTC')); |
221 | 132 | ||
133 | ob_start(); // Output buffering for the page cache. | ||
222 | 134 | ||
223 | // In case stupid admin has left magic_quotes enabled in php.ini: | 135 | // In case stupid admin has left magic_quotes enabled in php.ini: |
224 | if (get_magic_quotes_gpc()) | 136 | if (get_magic_quotes_gpc()) |
@@ -235,18 +147,9 @@ header("Cache-Control: no-store, no-cache, must-revalidate"); | |||
235 | header("Cache-Control: post-check=0, pre-check=0", false); | 147 | header("Cache-Control: post-check=0, pre-check=0", false); |
236 | header("Pragma: no-cache"); | 148 | header("Pragma: no-cache"); |
237 | 149 | ||
238 | // Handling of old config file which do not have the new parameters. | 150 | if (! is_file($conf->getConfigFileExt())) { |
239 | if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.escape(index_url($_SERVER)); | ||
240 | if (empty($GLOBALS['timezone'])) $GLOBALS['timezone']=date_default_timezone_get(); | ||
241 | if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']=''; | ||
242 | if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false; | ||
243 | if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=false; | ||
244 | if (empty($GLOBALS['titleLink'])) $GLOBALS['titleLink']='?'; | ||
245 | // I really need to rewrite Shaarli with a proper configuation manager. | ||
246 | |||
247 | if (! is_file($GLOBALS['config']['CONFIG_FILE'])) { | ||
248 | // Ensure Shaarli has proper access to its resources | 151 | // Ensure Shaarli has proper access to its resources |
249 | $errors = ApplicationUtils::checkResourcePermissions($GLOBALS['config']); | 152 | $errors = ApplicationUtils::checkResourcePermissions($conf); |
250 | 153 | ||
251 | if ($errors != array()) { | 154 | if ($errors != array()) { |
252 | $message = '<p>Insufficient permissions:</p><ul>'; | 155 | $message = '<p>Insufficient permissions:</p><ul>'; |
@@ -262,15 +165,11 @@ if (! is_file($GLOBALS['config']['CONFIG_FILE'])) { | |||
262 | } | 165 | } |
263 | 166 | ||
264 | // Display the installation form if no existing config is found | 167 | // Display the installation form if no existing config is found |
265 | install(); | 168 | install($conf); |
266 | } | 169 | } |
267 | 170 | ||
268 | $GLOBALS['title'] = !empty($GLOBALS['title']) ? escape($GLOBALS['title']) : ''; | ||
269 | $GLOBALS['titleLink'] = !empty($GLOBALS['titleLink']) ? escape($GLOBALS['titleLink']) : ''; | ||
270 | $GLOBALS['redirector'] = !empty($GLOBALS['redirector']) ? escape($GLOBALS['redirector']) : ''; | ||
271 | |||
272 | // a token depending of deployment salt, user password, and the current ip | 171 | // a token depending of deployment salt, user password, and the current ip |
273 | define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GLOBALS['salt'])); | 172 | define('STAY_SIGNED_IN_TOKEN', sha1($conf->get('credentials.hash') . $_SERVER['REMOTE_ADDR'] . $conf->get('credentials.salt'))); |
274 | 173 | ||
275 | // Sniff browser language and set date format accordingly. | 174 | // Sniff browser language and set date format accordingly. |
276 | if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { | 175 | if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { |
@@ -278,17 +177,21 @@ if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { | |||
278 | } | 177 | } |
279 | header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling. | 178 | header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling. |
280 | 179 | ||
281 | //================================================================================================== | 180 | /** |
282 | // Checking session state (i.e. is the user still logged in) | 181 | * Checking session state (i.e. is the user still logged in) |
283 | //================================================================================================== | 182 | * |
284 | 183 | * @param ConfigManager $conf The configuration manager. | |
285 | function setup_login_state() { | 184 | * |
286 | if ($GLOBALS['config']['OPEN_SHAARLI']) { | 185 | * @return bool: true if the user is logged in, false otherwise. |
186 | */ | ||
187 | function setup_login_state($conf) | ||
188 | { | ||
189 | if ($conf->get('security.open_shaarli')) { | ||
287 | return true; | 190 | return true; |
288 | } | 191 | } |
289 | $userIsLoggedIn = false; // By default, we do not consider the user as logged in; | 192 | $userIsLoggedIn = false; // By default, we do not consider the user as logged in; |
290 | $loginFailure = false; // If set to true, every attempt to authenticate the user will fail. This indicates that an important condition isn't met. | 193 | $loginFailure = false; // If set to true, every attempt to authenticate the user will fail. This indicates that an important condition isn't met. |
291 | if (!isset($GLOBALS['login'])) { | 194 | if (! $conf->exists('credentials.login')) { |
292 | $userIsLoggedIn = false; // Shaarli is not configured yet. | 195 | $userIsLoggedIn = false; // Shaarli is not configured yet. |
293 | $loginFailure = true; | 196 | $loginFailure = true; |
294 | } | 197 | } |
@@ -296,13 +199,13 @@ function setup_login_state() { | |||
296 | $_COOKIE['shaarli_staySignedIn']===STAY_SIGNED_IN_TOKEN && | 199 | $_COOKIE['shaarli_staySignedIn']===STAY_SIGNED_IN_TOKEN && |
297 | !$loginFailure) | 200 | !$loginFailure) |
298 | { | 201 | { |
299 | fillSessionInfo(); | 202 | fillSessionInfo($conf); |
300 | $userIsLoggedIn = true; | 203 | $userIsLoggedIn = true; |
301 | } | 204 | } |
302 | // If session does not exist on server side, or IP address has changed, or session has expired, logout. | 205 | // If session does not exist on server side, or IP address has changed, or session has expired, logout. |
303 | if (empty($_SESSION['uid']) || | 206 | if (empty($_SESSION['uid']) |
304 | ($GLOBALS['disablesessionprotection']==false && $_SESSION['ip']!=allIPs()) || | 207 | || ($conf->get('security.session_protection_disabled') == false && $_SESSION['ip'] != allIPs()) |
305 | time() >= $_SESSION['expires_on']) | 208 | || time() >= $_SESSION['expires_on']) |
306 | { | 209 | { |
307 | logout(); | 210 | logout(); |
308 | $userIsLoggedIn = false; | 211 | $userIsLoggedIn = false; |
@@ -320,22 +223,26 @@ function setup_login_state() { | |||
320 | 223 | ||
321 | return $userIsLoggedIn; | 224 | return $userIsLoggedIn; |
322 | } | 225 | } |
323 | $userIsLoggedIn = setup_login_state(); | 226 | $userIsLoggedIn = setup_login_state($conf); |
324 | 227 | ||
325 | // ------------------------------------------------------------------------------------------ | 228 | /** |
326 | // PubSubHubbub protocol support (if enabled) [UNTESTED] | 229 | * PubSubHubbub protocol support (if enabled) [UNTESTED] |
327 | // (Source: http://aldarone.fr/les-flux-rss-shaarli-et-pubsubhubbub/ ) | 230 | * (Source: http://aldarone.fr/les-flux-rss-shaarli-et-pubsubhubbub/ ) |
328 | if (!empty($GLOBALS['config']['PUBSUBHUB_URL'])) include './publisher.php'; | 231 | * |
329 | function pubsubhub() | 232 | * @param ConfigManager $conf Configuration Manager instance. |
233 | */ | ||
234 | function pubsubhub($conf) | ||
330 | { | 235 | { |
331 | if (!empty($GLOBALS['config']['PUBSUBHUB_URL'])) | 236 | $pshUrl = $conf->get('config.PUBSUBHUB_URL'); |
237 | if (!empty($pshUrl)) | ||
332 | { | 238 | { |
333 | $p = new Publisher($GLOBALS['config']['PUBSUBHUB_URL']); | 239 | include_once './publisher.php'; |
334 | $topic_url = array ( | 240 | $p = new Publisher($pshUrl); |
335 | index_url($_SERVER).'?do=atom', | 241 | $topic_url = array ( |
336 | index_url($_SERVER).'?do=rss' | 242 | index_url($_SERVER).'?do=atom', |
337 | ); | 243 | index_url($_SERVER).'?do=rss' |
338 | $p->publish_update($topic_url); | 244 | ); |
245 | $p->publish_update($topic_url); | ||
339 | } | 246 | } |
340 | } | 247 | } |
341 | 248 | ||
@@ -345,32 +252,46 @@ function pubsubhub() | |||
345 | // Returns the IP address of the client (Used to prevent session cookie hijacking.) | 252 | // Returns the IP address of the client (Used to prevent session cookie hijacking.) |
346 | function allIPs() | 253 | function allIPs() |
347 | { | 254 | { |
348 | $ip = $_SERVER["REMOTE_ADDR"]; | 255 | $ip = $_SERVER['REMOTE_ADDR']; |
349 | // Then we use more HTTP headers to prevent session hijacking from users behind the same proxy. | 256 | // Then we use more HTTP headers to prevent session hijacking from users behind the same proxy. |
350 | if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip=$ip.'_'.$_SERVER['HTTP_X_FORWARDED_FOR']; } | 257 | if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip=$ip.'_'.$_SERVER['HTTP_X_FORWARDED_FOR']; } |
351 | if (isset($_SERVER['HTTP_CLIENT_IP'])) { $ip=$ip.'_'.$_SERVER['HTTP_CLIENT_IP']; } | 258 | if (isset($_SERVER['HTTP_CLIENT_IP'])) { $ip=$ip.'_'.$_SERVER['HTTP_CLIENT_IP']; } |
352 | return $ip; | 259 | return $ip; |
353 | } | 260 | } |
354 | 261 | ||
355 | function fillSessionInfo() { | 262 | /** |
263 | * Load user session. | ||
264 | * | ||
265 | * @param ConfigManager $conf Configuration Manager instance. | ||
266 | */ | ||
267 | function fillSessionInfo($conf) | ||
268 | { | ||
356 | $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid) | 269 | $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid) |
357 | $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked. | 270 | $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked. |
358 | $_SESSION['username']=$GLOBALS['login']; | 271 | $_SESSION['username']= $conf->get('credentials.login'); |
359 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration. | 272 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration. |
360 | } | 273 | } |
361 | 274 | ||
362 | // Check that user/password is correct. | 275 | /** |
363 | function check_auth($login,$password) | 276 | * Check that user/password is correct. |
277 | * | ||
278 | * @param string $login Username | ||
279 | * @param string $password User password | ||
280 | * @param ConfigManager $conf Configuration Manager instance. | ||
281 | * | ||
282 | * @return bool: authentication successful or not. | ||
283 | */ | ||
284 | function check_auth($login, $password, $conf) | ||
364 | { | 285 | { |
365 | $hash = sha1($password.$login.$GLOBALS['salt']); | 286 | $hash = sha1($password . $login . $conf->get('credentials.salt')); |
366 | if ($login==$GLOBALS['login'] && $hash==$GLOBALS['hash']) | 287 | if ($login == $conf->get('credentials.login') && $hash == $conf->get('credentials.hash')) |
367 | { // Login/password is correct. | 288 | { // Login/password is correct. |
368 | fillSessionInfo(); | 289 | fillSessionInfo($conf); |
369 | logm($GLOBALS['config']['LOG_FILE'], $_SERVER['REMOTE_ADDR'], 'Login successful'); | 290 | logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'Login successful'); |
370 | return True; | 291 | return true; |
371 | } | 292 | } |
372 | logm($GLOBALS['config']['LOG_FILE'], $_SERVER['REMOTE_ADDR'], 'Login failed for user '.$login); | 293 | logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'Login failed for user '.$login); |
373 | return False; | 294 | return false; |
374 | } | 295 | } |
375 | 296 | ||
376 | // Returns true if the user is logged in. | 297 | // Returns true if the user is logged in. |
@@ -395,34 +316,71 @@ function logout() { | |||
395 | // ------------------------------------------------------------------------------------------ | 316 | // ------------------------------------------------------------------------------------------ |
396 | // Brute force protection system | 317 | // Brute force protection system |
397 | // Several consecutive failed logins will ban the IP address for 30 minutes. | 318 | // Several consecutive failed logins will ban the IP address for 30 minutes. |
398 | if (!is_file($GLOBALS['config']['IPBANS_FILENAME'])) file_put_contents($GLOBALS['config']['IPBANS_FILENAME'], "<?php\n\$GLOBALS['IPBANS']=".var_export(array('FAILURES'=>array(),'BANS'=>array()),true).";\n?>"); | 319 | if (!is_file($conf->get('resource.ban_file', 'data/ipbans.php'))) { |
399 | include $GLOBALS['config']['IPBANS_FILENAME']; | 320 | // FIXME! globals |
400 | // Signal a failed login. Will ban the IP if too many failures: | 321 | file_put_contents( |
401 | function ban_loginFailed() | 322 | $conf->get('resource.ban_file', 'data/ipbans.php'), |
323 | "<?php\n\$GLOBALS['IPBANS']=".var_export(array('FAILURES'=>array(),'BANS'=>array()),true).";\n?>" | ||
324 | ); | ||
325 | } | ||
326 | include $conf->get('resource.ban_file', 'data/ipbans.php'); | ||
327 | /** | ||
328 | * Signal a failed login. Will ban the IP if too many failures: | ||
329 | * | ||
330 | * @param ConfigManager $conf Configuration Manager instance. | ||
331 | */ | ||
332 | function ban_loginFailed($conf) | ||
402 | { | 333 | { |
403 | $ip=$_SERVER["REMOTE_ADDR"]; $gb=$GLOBALS['IPBANS']; | 334 | $ip = $_SERVER['REMOTE_ADDR']; |
404 | if (!isset($gb['FAILURES'][$ip])) $gb['FAILURES'][$ip]=0; | 335 | $trusted = $conf->get('security.trusted_proxies', array()); |
336 | if (in_array($ip, $trusted)) { | ||
337 | $ip = getIpAddressFromProxy($_SERVER, $trusted); | ||
338 | if (!$ip) { | ||
339 | return; | ||
340 | } | ||
341 | } | ||
342 | $gb = $GLOBALS['IPBANS']; | ||
343 | if (! isset($gb['FAILURES'][$ip])) { | ||
344 | $gb['FAILURES'][$ip]=0; | ||
345 | } | ||
405 | $gb['FAILURES'][$ip]++; | 346 | $gb['FAILURES'][$ip]++; |
406 | if ($gb['FAILURES'][$ip]>($GLOBALS['config']['BAN_AFTER']-1)) | 347 | if ($gb['FAILURES'][$ip] > ($conf->get('security.ban_after') - 1)) |
407 | { | 348 | { |
408 | $gb['BANS'][$ip]=time()+$GLOBALS['config']['BAN_DURATION']; | 349 | $gb['BANS'][$ip] = time() + $conf->get('security.ban_after', 1800); |
409 | logm($GLOBALS['config']['LOG_FILE'], $_SERVER['REMOTE_ADDR'], 'IP address banned from login'); | 350 | logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'IP address banned from login'); |
410 | } | 351 | } |
411 | $GLOBALS['IPBANS'] = $gb; | 352 | $GLOBALS['IPBANS'] = $gb; |
412 | file_put_contents($GLOBALS['config']['IPBANS_FILENAME'], "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>"); | 353 | file_put_contents( |
354 | $conf->get('resource.ban_file', 'data/ipbans.php'), | ||
355 | "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>" | ||
356 | ); | ||
413 | } | 357 | } |
414 | 358 | ||
415 | // Signals a successful login. Resets failed login counter. | 359 | /** |
416 | function ban_loginOk() | 360 | * Signals a successful login. Resets failed login counter. |
361 | * | ||
362 | * @param ConfigManager $conf Configuration Manager instance. | ||
363 | */ | ||
364 | function ban_loginOk($conf) | ||
417 | { | 365 | { |
418 | $ip=$_SERVER["REMOTE_ADDR"]; $gb=$GLOBALS['IPBANS']; | 366 | $ip = $_SERVER['REMOTE_ADDR']; |
367 | $gb = $GLOBALS['IPBANS']; | ||
419 | unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]); | 368 | unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]); |
420 | $GLOBALS['IPBANS'] = $gb; | 369 | $GLOBALS['IPBANS'] = $gb; |
421 | file_put_contents($GLOBALS['config']['IPBANS_FILENAME'], "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>"); | 370 | file_put_contents( |
371 | $conf->get('resource.ban_file', 'data/ipbans.php'), | ||
372 | "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>" | ||
373 | ); | ||
422 | } | 374 | } |
423 | 375 | ||
424 | // Checks if the user CAN login. If 'true', the user can try to login. | 376 | /** |
425 | function ban_canLogin() | 377 | * Checks if the user CAN login. If 'true', the user can try to login. |
378 | * | ||
379 | * @param ConfigManager $conf Configuration Manager instance. | ||
380 | * | ||
381 | * @return bool: true if the user is allowed to login. | ||
382 | */ | ||
383 | function ban_canLogin($conf) | ||
426 | { | 384 | { |
427 | $ip=$_SERVER["REMOTE_ADDR"]; $gb=$GLOBALS['IPBANS']; | 385 | $ip=$_SERVER["REMOTE_ADDR"]; $gb=$GLOBALS['IPBANS']; |
428 | if (isset($gb['BANS'][$ip])) | 386 | if (isset($gb['BANS'][$ip])) |
@@ -430,9 +388,12 @@ function ban_canLogin() | |||
430 | // User is banned. Check if the ban has expired: | 388 | // User is banned. Check if the ban has expired: |
431 | if ($gb['BANS'][$ip]<=time()) | 389 | if ($gb['BANS'][$ip]<=time()) |
432 | { // Ban expired, user can try to login again. | 390 | { // Ban expired, user can try to login again. |
433 | logm($GLOBALS['config']['LOG_FILE'], $_SERVER['REMOTE_ADDR'], 'Ban lifted.'); | 391 | logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'Ban lifted.'); |
434 | unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]); | 392 | unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]); |
435 | file_put_contents($GLOBALS['config']['IPBANS_FILENAME'], "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>"); | 393 | file_put_contents( |
394 | $conf->get('resource.ban_file', 'data/ipbans.php'), | ||
395 | "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>" | ||
396 | ); | ||
436 | return true; // Ban has expired, user can login. | 397 | return true; // Ban has expired, user can login. |
437 | } | 398 | } |
438 | return false; // User is banned. | 399 | return false; // User is banned. |
@@ -444,10 +405,12 @@ function ban_canLogin() | |||
444 | // Process login form: Check if login/password is correct. | 405 | // Process login form: Check if login/password is correct. |
445 | if (isset($_POST['login'])) | 406 | if (isset($_POST['login'])) |
446 | { | 407 | { |
447 | if (!ban_canLogin()) die('I said: NO. You are banned for the moment. Go away.'); | 408 | if (!ban_canLogin($conf)) die('I said: NO. You are banned for the moment. Go away.'); |
448 | if (isset($_POST['password']) && tokenOk($_POST['token']) && (check_auth($_POST['login'], $_POST['password']))) | 409 | if (isset($_POST['password']) |
449 | { // Login/password is OK. | 410 | && tokenOk($_POST['token']) |
450 | ban_loginOk(); | 411 | && (check_auth($_POST['login'], $_POST['password'], $conf)) |
412 | ) { // Login/password is OK. | ||
413 | ban_loginOk($conf); | ||
451 | // If user wants to keep the session cookie even after the browser closes: | 414 | // If user wants to keep the session cookie even after the browser closes: |
452 | if (!empty($_POST['longlastingsession'])) | 415 | if (!empty($_POST['longlastingsession'])) |
453 | { | 416 | { |
@@ -495,7 +458,7 @@ if (isset($_POST['login'])) | |||
495 | } | 458 | } |
496 | else | 459 | else |
497 | { | 460 | { |
498 | ban_loginFailed(); | 461 | ban_loginFailed($conf); |
499 | $redir = '&username='. $_POST['login']; | 462 | $redir = '&username='. $_POST['login']; |
500 | if (isset($_GET['post'])) { | 463 | if (isset($_GET['post'])) { |
501 | $redir .= '&post=' . urlencode($_GET['post']); | 464 | $redir .= '&post=' . urlencode($_GET['post']); |
@@ -543,10 +506,16 @@ function getMaxFileSize() | |||
543 | // Token should be used in any form which acts on data (create,update,delete,import...). | 506 | // Token should be used in any form which acts on data (create,update,delete,import...). |
544 | if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array(); // Token are attached to the session. | 507 | if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array(); // Token are attached to the session. |
545 | 508 | ||
546 | // Returns a token. | 509 | /** |
547 | function getToken() | 510 | * Returns a token. |
511 | * | ||
512 | * @param ConfigManager $conf Configuration Manager instance. | ||
513 | * | ||
514 | * @return string token. | ||
515 | */ | ||
516 | function getToken($conf) | ||
548 | { | 517 | { |
549 | $rnd = sha1(uniqid('',true).'_'.mt_rand().$GLOBALS['salt']); // We generate a random string. | 518 | $rnd = sha1(uniqid('', true) .'_'. mt_rand() . $conf->get('credentials.salt')); // We generate a random string. |
550 | $_SESSION['tokens'][$rnd]=1; // Store it on the server side. | 519 | $_SESSION['tokens'][$rnd]=1; // Store it on the server side. |
551 | return $rnd; | 520 | return $rnd; |
552 | } | 521 | } |
@@ -563,15 +532,18 @@ function tokenOk($token) | |||
563 | return false; // Wrong token, or already used. | 532 | return false; // Wrong token, or already used. |
564 | } | 533 | } |
565 | 534 | ||
566 | // ------------------------------------------------------------------------------------------ | 535 | /** |
567 | // Daily RSS feed: 1 RSS entry per day giving all the links on that day. | 536 | * Daily RSS feed: 1 RSS entry per day giving all the links on that day. |
568 | // Gives the last 7 days (which have links). | 537 | * Gives the last 7 days (which have links). |
569 | // This RSS feed cannot be filtered. | 538 | * This RSS feed cannot be filtered. |
570 | function showDailyRSS() { | 539 | * |
540 | * @param ConfigManager $conf Configuration Manager instance. | ||
541 | */ | ||
542 | function showDailyRSS($conf) { | ||
571 | // Cache system | 543 | // Cache system |
572 | $query = $_SERVER['QUERY_STRING']; | 544 | $query = $_SERVER['QUERY_STRING']; |
573 | $cache = new CachedPage( | 545 | $cache = new CachedPage( |
574 | $GLOBALS['config']['PAGECACHE'], | 546 | $conf->get('config.PAGE_CACHE'), |
575 | page_url($_SERVER), | 547 | page_url($_SERVER), |
576 | startsWith($query,'do=dailyrss') && !isLoggedIn() | 548 | startsWith($query,'do=dailyrss') && !isLoggedIn() |
577 | ); | 549 | ); |
@@ -584,32 +556,27 @@ function showDailyRSS() { | |||
584 | // If cached was not found (or not usable), then read the database and build the response: | 556 | // If cached was not found (or not usable), then read the database and build the response: |
585 | // Read links from database (and filter private links if used it not logged in). | 557 | // Read links from database (and filter private links if used it not logged in). |
586 | $LINKSDB = new LinkDB( | 558 | $LINKSDB = new LinkDB( |
587 | $GLOBALS['config']['DATASTORE'], | 559 | $conf->get('resource.datastore'), |
588 | isLoggedIn(), | 560 | isLoggedIn(), |
589 | $GLOBALS['config']['HIDE_PUBLIC_LINKS'], | 561 | $conf->get('privacy.hide_public_links'), |
590 | $GLOBALS['redirector'], | 562 | $conf->get('redirector.url'), |
591 | $GLOBALS['config']['REDIRECTOR_URLENCODE'] | 563 | $conf->get('redirector.encode_url') |
592 | ); | 564 | ); |
593 | 565 | ||
594 | /* 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 |
595 | back in time (rsort()) until we have enough days ($nb_of_days). | 567 | back in time until we have enough days ($nb_of_days). |
596 | */ | 568 | */ |
597 | $linkdates = array(); | ||
598 | foreach ($LINKSDB as $linkdate => $value) { | ||
599 | $linkdates[] = $linkdate; | ||
600 | } | ||
601 | rsort($linkdates); | ||
602 | $nb_of_days = 7; // We take 7 days. | 569 | $nb_of_days = 7; // We take 7 days. |
603 | $today = Date('Ymd'); | 570 | $today = date('Ymd'); |
604 | $days = array(); | 571 | $days = array(); |
605 | 572 | ||
606 | foreach ($linkdates as $linkdate) { | 573 | foreach ($LINKSDB as $link) { |
607 | $day = substr($linkdate, 0, 8); // Extract day (without time) | 574 | $day = $link['created']->format('Ymd'); // Extract day (without time) |
608 | if (strcmp($day,$today) < 0) { | 575 | if (strcmp($day, $today) < 0) { |
609 | if (empty($days[$day])) { | 576 | if (empty($days[$day])) { |
610 | $days[$day] = array(); | 577 | $days[$day] = array(); |
611 | } | 578 | } |
612 | $days[$day][] = $linkdate; | 579 | $days[$day][] = $link; |
613 | } | 580 | } |
614 | 581 | ||
615 | if (count($days) > $nb_of_days) { | 582 | if (count($days) > $nb_of_days) { |
@@ -622,42 +589,35 @@ function showDailyRSS() { | |||
622 | $pageaddr = escape(index_url($_SERVER)); | 589 | $pageaddr = escape(index_url($_SERVER)); |
623 | echo '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">'; | 590 | echo '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">'; |
624 | echo '<channel>'; | 591 | echo '<channel>'; |
625 | echo '<title>Daily - '. $GLOBALS['title'] . '</title>'; | 592 | echo '<title>Daily - '. $conf->get('general.title') . '</title>'; |
626 | echo '<link>'. $pageaddr .'</link>'; | 593 | echo '<link>'. $pageaddr .'</link>'; |
627 | echo '<description>Daily shared links</description>'; | 594 | echo '<description>Daily shared links</description>'; |
628 | echo '<language>en-en</language>'; | 595 | echo '<language>en-en</language>'; |
629 | echo '<copyright>'. $pageaddr .'</copyright>'. PHP_EOL; | 596 | echo '<copyright>'. $pageaddr .'</copyright>'. PHP_EOL; |
630 | 597 | ||
631 | // For each day. | 598 | // For each day. |
632 | foreach ($days as $day => $linkdates) { | 599 | foreach ($days as $day => $links) { |
633 | $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); | 600 | $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); |
634 | $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. |
635 | 602 | ||
636 | // Build the HTML body of this RSS entry. | ||
637 | $html = ''; | ||
638 | $href = ''; | ||
639 | $links = array(); | ||
640 | |||
641 | // We pre-format some fields for proper output. | 603 | // We pre-format some fields for proper output. |
642 | foreach ($linkdates as $linkdate) { | 604 | foreach ($links as &$link) { |
643 | $l = $LINKSDB[$linkdate]; | 605 | $link['formatedDescription'] = format_description($link['description'], $conf->get('redirector.url')); |
644 | $l['formatedDescription'] = format_description($l['description'], $GLOBALS['redirector']); | 606 | $link['thumbnail'] = thumbnail($conf, $link['url']); |
645 | $l['thumbnail'] = thumbnail($l['url']); | 607 | $link['timestamp'] = $link['created']->getTimestamp(); |
646 | $l_date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $l['linkdate']); | 608 | if (startsWith($link['url'], '?')) { |
647 | $l['timestamp'] = $l_date->getTimestamp(); | 609 | $link['url'] = index_url($_SERVER) . $link['url']; // make permalink URL absolute |
648 | if (startsWith($l['url'], '?')) { | ||
649 | $l['url'] = index_url($_SERVER) . $l['url']; // make permalink URL absolute | ||
650 | } | 610 | } |
651 | $links[$linkdate] = $l; | ||
652 | } | 611 | } |
653 | 612 | ||
654 | // Then build the HTML for this day: | 613 | // Then build the HTML for this day: |
655 | $tpl = new RainTPL; | 614 | $tpl = new RainTPL; |
656 | $tpl->assign('title', $GLOBALS['title']); | 615 | $tpl->assign('title', $conf->get('general.title')); |
657 | $tpl->assign('daydate', $dayDate->getTimestamp()); | 616 | $tpl->assign('daydate', $dayDate->getTimestamp()); |
658 | $tpl->assign('absurl', $absurl); | 617 | $tpl->assign('absurl', $absurl); |
659 | $tpl->assign('links', $links); | 618 | $tpl->assign('links', $links); |
660 | $tpl->assign('rssdate', escape($dayDate->format(DateTime::RSS))); | 619 | $tpl->assign('rssdate', escape($dayDate->format(DateTime::RSS))); |
620 | $tpl->assign('hide_timestamps', $conf->get('privacy.hide_timestamps', false)); | ||
661 | $html = $tpl->draw('dailyrss', $return_string=true); | 621 | $html = $tpl->draw('dailyrss', $return_string=true); |
662 | 622 | ||
663 | echo $html . PHP_EOL; | 623 | echo $html . PHP_EOL; |
@@ -672,12 +632,14 @@ function showDailyRSS() { | |||
672 | /** | 632 | /** |
673 | * Show the 'Daily' page. | 633 | * Show the 'Daily' page. |
674 | * | 634 | * |
675 | * @param PageBuilder $pageBuilder Template engine wrapper. | 635 | * @param PageBuilder $pageBuilder Template engine wrapper. |
676 | * @param LinkDB $LINKSDB LinkDB instance. | 636 | * @param LinkDB $LINKSDB LinkDB instance. |
637 | * @param ConfigManager $conf Configuration Manager instance. | ||
638 | * @param PluginManager $pluginManager Plugin Manager instane. | ||
677 | */ | 639 | */ |
678 | function showDaily($pageBuilder, $LINKSDB) | 640 | function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) |
679 | { | 641 | { |
680 | $day=Date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD. | 642 | $day=date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD. |
681 | if (isset($_GET['day'])) $day=$_GET['day']; | 643 | if (isset($_GET['day'])) $day=$_GET['day']; |
682 | 644 | ||
683 | $days = $LINKSDB->days(); | 645 | $days = $LINKSDB->days(); |
@@ -705,10 +667,9 @@ function showDaily($pageBuilder, $LINKSDB) | |||
705 | $taglist = explode(' ',$link['tags']); | 667 | $taglist = explode(' ',$link['tags']); |
706 | uasort($taglist, 'strcasecmp'); | 668 | uasort($taglist, 'strcasecmp'); |
707 | $linksToDisplay[$key]['taglist']=$taglist; | 669 | $linksToDisplay[$key]['taglist']=$taglist; |
708 | $linksToDisplay[$key]['formatedDescription'] = format_description($link['description'], $GLOBALS['redirector']); | 670 | $linksToDisplay[$key]['formatedDescription'] = format_description($link['description'], $conf->get('redirector.url')); |
709 | $linksToDisplay[$key]['thumbnail'] = thumbnail($link['url']); | 671 | $linksToDisplay[$key]['thumbnail'] = thumbnail($conf, $link['url']); |
710 | $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); | 672 | $linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp(); |
711 | $linksToDisplay[$key]['timestamp'] = $date->getTimestamp(); | ||
712 | } | 673 | } |
713 | 674 | ||
714 | /* We need to spread the articles on 3 columns. | 675 | /* We need to spread the articles on 3 columns. |
@@ -741,7 +702,7 @@ function showDaily($pageBuilder, $LINKSDB) | |||
741 | 'previousday' => $previousday, | 702 | 'previousday' => $previousday, |
742 | 'nextday' => $nextday, | 703 | 'nextday' => $nextday, |
743 | ); | 704 | ); |
744 | $pluginManager = PluginManager::getInstance(); | 705 | |
745 | $pluginManager->executeHooks('render_daily', $data, array('loggedin' => isLoggedIn())); | 706 | $pluginManager->executeHooks('render_daily', $data, array('loggedin' => isLoggedIn())); |
746 | 707 | ||
747 | foreach ($data as $key => $value) { | 708 | foreach ($data as $key => $value) { |
@@ -752,36 +713,46 @@ function showDaily($pageBuilder, $LINKSDB) | |||
752 | exit; | 713 | exit; |
753 | } | 714 | } |
754 | 715 | ||
755 | // Renders the linklist | 716 | /** |
756 | function showLinkList($PAGE, $LINKSDB) { | 717 | * Renders the linklist |
757 | buildLinkList($PAGE,$LINKSDB); // Compute list of links to display | 718 | * |
719 | * @param pageBuilder $PAGE pageBuilder instance. | ||
720 | * @param LinkDB $LINKSDB LinkDB instance. | ||
721 | * @param ConfigManager $conf Configuration Manager instance. | ||
722 | * @param PluginManager $pluginManager Plugin Manager instance. | ||
723 | */ | ||
724 | function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager) { | ||
725 | buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager); // Compute list of links to display | ||
758 | $PAGE->renderPage('linklist'); | 726 | $PAGE->renderPage('linklist'); |
759 | } | 727 | } |
760 | 728 | ||
761 | 729 | /** | |
762 | // ------------------------------------------------------------------------------------------ | 730 | * Render HTML page (according to URL parameters and user rights) |
763 | // Render HTML page (according to URL parameters and user rights) | 731 | * |
764 | function renderPage() | 732 | * @param ConfigManager $conf Configuration Manager instance. |
733 | * @param PluginManager $pluginManager Plugin Manager instance, | ||
734 | */ | ||
735 | function renderPage($conf, $pluginManager) | ||
765 | { | 736 | { |
766 | $LINKSDB = new LinkDB( | 737 | $LINKSDB = new LinkDB( |
767 | $GLOBALS['config']['DATASTORE'], | 738 | $conf->get('resource.datastore'), |
768 | isLoggedIn(), | 739 | isLoggedIn(), |
769 | $GLOBALS['config']['HIDE_PUBLIC_LINKS'], | 740 | $conf->get('privacy.hide_public_links'), |
770 | $GLOBALS['redirector'], | 741 | $conf->get('redirector.url'), |
771 | $GLOBALS['config']['REDIRECTOR_URLENCODE'] | 742 | $conf->get('redirector.encode_url') |
772 | ); | 743 | ); |
773 | 744 | ||
774 | $updater = new Updater( | 745 | $updater = new Updater( |
775 | read_updates_file($GLOBALS['config']['UPDATES_FILE']), | 746 | read_updates_file($conf->get('resource.updates')), |
776 | $GLOBALS, | ||
777 | $LINKSDB, | 747 | $LINKSDB, |
748 | $conf, | ||
778 | isLoggedIn() | 749 | isLoggedIn() |
779 | ); | 750 | ); |
780 | try { | 751 | try { |
781 | $newUpdates = $updater->update(); | 752 | $newUpdates = $updater->update(); |
782 | if (! empty($newUpdates)) { | 753 | if (! empty($newUpdates)) { |
783 | write_updates_file( | 754 | write_updates_file( |
784 | $GLOBALS['config']['UPDATES_FILE'], | 755 | $conf->get('resource.updates'), |
785 | $updater->getDoneUpdates() | 756 | $updater->getDoneUpdates() |
786 | ); | 757 | ); |
787 | } | 758 | } |
@@ -790,9 +761,10 @@ function renderPage() | |||
790 | die($e->getMessage()); | 761 | die($e->getMessage()); |
791 | } | 762 | } |
792 | 763 | ||
793 | $PAGE = new PageBuilder(); | 764 | $PAGE = new PageBuilder($conf); |
794 | $PAGE->assign('linkcount', count($LINKSDB)); | 765 | $PAGE->assign('linkcount', count($LINKSDB)); |
795 | $PAGE->assign('privateLinkcount', count_private($LINKSDB)); | 766 | $PAGE->assign('privateLinkcount', count_private($LINKSDB)); |
767 | $PAGE->assign('plugin_errors', $pluginManager->getErrors()); | ||
796 | 768 | ||
797 | // Determine which page will be rendered. | 769 | // Determine which page will be rendered. |
798 | $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : ''; | 770 | $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : ''; |
@@ -805,7 +777,7 @@ function renderPage() | |||
805 | 'header', | 777 | 'header', |
806 | 'footer', | 778 | 'footer', |
807 | ); | 779 | ); |
808 | $pluginManager = PluginManager::getInstance(); | 780 | |
809 | foreach($common_hooks as $name) { | 781 | foreach($common_hooks as $name) { |
810 | $plugin_data = array(); | 782 | $plugin_data = array(); |
811 | $pluginManager->executeHooks('render_' . $name, $plugin_data, | 783 | $pluginManager->executeHooks('render_' . $name, $plugin_data, |
@@ -820,9 +792,7 @@ function renderPage() | |||
820 | // -------- Display login form. | 792 | // -------- Display login form. |
821 | if ($targetPage == Router::$PAGE_LOGIN) | 793 | if ($targetPage == Router::$PAGE_LOGIN) |
822 | { | 794 | { |
823 | if ($GLOBALS['config']['OPEN_SHAARLI']) { header('Location: ?'); exit; } // No need to login for open Shaarli | 795 | if ($conf->get('security.open_shaarli')) { header('Location: ?'); exit; } // No need to login for open Shaarli |
824 | $token=''; if (ban_canLogin()) $token=getToken(); // Do not waste token generation if not useful. | ||
825 | $PAGE->assign('token',$token); | ||
826 | if (isset($_GET['username'])) { | 796 | if (isset($_GET['username'])) { |
827 | $PAGE->assign('username', escape($_GET['username'])); | 797 | $PAGE->assign('username', escape($_GET['username'])); |
828 | } | 798 | } |
@@ -833,7 +803,7 @@ function renderPage() | |||
833 | // -------- User wants to logout. | 803 | // -------- User wants to logout. |
834 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout')) | 804 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout')) |
835 | { | 805 | { |
836 | invalidateCaches($GLOBALS['config']['PAGECACHE']); | 806 | invalidateCaches($conf->get('resource.page_cache')); |
837 | logout(); | 807 | logout(); |
838 | header('Location: ?'); | 808 | header('Location: ?'); |
839 | exit; | 809 | exit; |
@@ -849,8 +819,8 @@ function renderPage() | |||
849 | // Get only links which have a thumbnail. | 819 | // Get only links which have a thumbnail. |
850 | foreach($links as $link) | 820 | foreach($links as $link) |
851 | { | 821 | { |
852 | $permalink='?'.escape(smallhash($link['linkdate'])); | 822 | $permalink='?'.$link['shorturl']; |
853 | $thumb=lazyThumbnail($link['url'],$permalink); | 823 | $thumb=lazyThumbnail($conf, $link['url'],$permalink); |
854 | if ($thumb!='') // Only output links which have a thumbnail. | 824 | if ($thumb!='') // Only output links which have a thumbnail. |
855 | { | 825 | { |
856 | $link['thumbnail']=$thumb; // Thumbnail HTML code. | 826 | $link['thumbnail']=$thumb; // Thumbnail HTML code. |
@@ -883,7 +853,7 @@ function renderPage() | |||
883 | $maxcount = max($maxcount, $value); | 853 | $maxcount = max($maxcount, $value); |
884 | } | 854 | } |
885 | 855 | ||
886 | // Sort tags alphabetically: case insensitive, support locale if avalaible. | 856 | // Sort tags alphabetically: case insensitive, support locale if available. |
887 | uksort($tags, function($a, $b) { | 857 | uksort($tags, function($a, $b) { |
888 | // Collator is part of PHP intl. | 858 | // Collator is part of PHP intl. |
889 | if (class_exists('Collator')) { | 859 | if (class_exists('Collator')) { |
@@ -922,7 +892,7 @@ function renderPage() | |||
922 | 892 | ||
923 | // Daily page. | 893 | // Daily page. |
924 | if ($targetPage == Router::$PAGE_DAILY) { | 894 | if ($targetPage == Router::$PAGE_DAILY) { |
925 | showDaily($PAGE, $LINKSDB); | 895 | showDaily($PAGE, $LINKSDB, $conf, $pluginManager); |
926 | } | 896 | } |
927 | 897 | ||
928 | // ATOM and RSS feed. | 898 | // ATOM and RSS feed. |
@@ -933,7 +903,7 @@ function renderPage() | |||
933 | // Cache system | 903 | // Cache system |
934 | $query = $_SERVER['QUERY_STRING']; | 904 | $query = $_SERVER['QUERY_STRING']; |
935 | $cache = new CachedPage( | 905 | $cache = new CachedPage( |
936 | $GLOBALS['config']['PAGECACHE'], | 906 | $conf->get('resource.page_cache'), |
937 | page_url($_SERVER), | 907 | page_url($_SERVER), |
938 | startsWith($query,'do='. $targetPage) && !isLoggedIn() | 908 | startsWith($query,'do='. $targetPage) && !isLoggedIn() |
939 | ); | 909 | ); |
@@ -946,15 +916,15 @@ function renderPage() | |||
946 | // Generate data. | 916 | // Generate data. |
947 | $feedGenerator = new FeedBuilder($LINKSDB, $feedType, $_SERVER, $_GET, isLoggedIn()); | 917 | $feedGenerator = new FeedBuilder($LINKSDB, $feedType, $_SERVER, $_GET, isLoggedIn()); |
948 | $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0))); | 918 | $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0))); |
949 | $feedGenerator->setHideDates($GLOBALS['config']['HIDE_TIMESTAMPS'] && !isLoggedIn()); | 919 | $feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !isLoggedIn()); |
950 | $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$GLOBALS['config']['ENABLE_RSS_PERMALINKS']); | 920 | $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks')); |
951 | if (!empty($GLOBALS['config']['PUBSUBHUB_URL'])) { | 921 | $pshUrl = $conf->get('config.PUBSUBHUB_URL'); |
952 | $feedGenerator->setPubsubhubUrl($GLOBALS['config']['PUBSUBHUB_URL']); | 922 | if (!empty($pshUrl)) { |
923 | $feedGenerator->setPubsubhubUrl($pshUrl); | ||
953 | } | 924 | } |
954 | $data = $feedGenerator->buildData(); | 925 | $data = $feedGenerator->buildData(); |
955 | 926 | ||
956 | // Process plugin hook. | 927 | // Process plugin hook. |
957 | $pluginManager = PluginManager::getInstance(); | ||
958 | $pluginManager->executeHooks('render_feed', $data, array( | 928 | $pluginManager->executeHooks('render_feed', $data, array( |
959 | 'loggedin' => isLoggedIn(), | 929 | 'loggedin' => isLoggedIn(), |
960 | 'target' => $targetPage, | 930 | 'target' => $targetPage, |
@@ -1080,7 +1050,7 @@ function renderPage() | |||
1080 | exit; | 1050 | exit; |
1081 | } | 1051 | } |
1082 | 1052 | ||
1083 | showLinkList($PAGE, $LINKSDB); | 1053 | showLinkList($PAGE, $LINKSDB, $conf, $pluginManager); |
1084 | if (isset($_GET['edit_link'])) { | 1054 | if (isset($_GET['edit_link'])) { |
1085 | header('Location: ?do=login&edit_link='. escape($_GET['edit_link'])); | 1055 | header('Location: ?do=login&edit_link='. escape($_GET['edit_link'])); |
1086 | exit; | 1056 | exit; |
@@ -1096,6 +1066,7 @@ function renderPage() | |||
1096 | { | 1066 | { |
1097 | $data = array( | 1067 | $data = array( |
1098 | 'pageabsaddr' => index_url($_SERVER), | 1068 | 'pageabsaddr' => index_url($_SERVER), |
1069 | 'sslenabled' => !empty($_SERVER['HTTPS']) | ||
1099 | ); | 1070 | ); |
1100 | $pluginManager->executeHooks('render_tools', $data); | 1071 | $pluginManager->executeHooks('render_tools', $data); |
1101 | 1072 | ||
@@ -1110,19 +1081,23 @@ function renderPage() | |||
1110 | // -------- User wants to change his/her password. | 1081 | // -------- User wants to change his/her password. |
1111 | if ($targetPage == Router::$PAGE_CHANGEPASSWORD) | 1082 | if ($targetPage == Router::$PAGE_CHANGEPASSWORD) |
1112 | { | 1083 | { |
1113 | if ($GLOBALS['config']['OPEN_SHAARLI']) die('You are not supposed to change a password on an Open Shaarli.'); | 1084 | if ($conf->get('security.open_shaarli')) { |
1085 | die('You are not supposed to change a password on an Open Shaarli.'); | ||
1086 | } | ||
1087 | |||
1114 | if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) | 1088 | if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) |
1115 | { | 1089 | { |
1116 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! | 1090 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! |
1117 | 1091 | ||
1118 | // Make sure old password is correct. | 1092 | // Make sure old password is correct. |
1119 | $oldhash = sha1($_POST['oldpassword'].$GLOBALS['login'].$GLOBALS['salt']); | 1093 | $oldhash = sha1($_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt')); |
1120 | if ($oldhash!=$GLOBALS['hash']) { echo '<script>alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>'; exit; } | 1094 | if ($oldhash!= $conf->get('credentials.hash')) { echo '<script>alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>'; exit; } |
1121 | // Save new password | 1095 | // Save new password |
1122 | $GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless. | 1096 | // Salt renders rainbow-tables attacks useless. |
1123 | $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']); | 1097 | $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand())); |
1098 | $conf->set('credentials.hash', sha1($_POST['setpassword'] . $conf->get('credentials.login') . $conf->get('credentials.salt'))); | ||
1124 | try { | 1099 | try { |
1125 | writeConfig($GLOBALS, isLoggedIn()); | 1100 | $conf->write(isLoggedIn()); |
1126 | } | 1101 | } |
1127 | catch(Exception $e) { | 1102 | catch(Exception $e) { |
1128 | error_log( | 1103 | error_log( |
@@ -1139,7 +1114,6 @@ function renderPage() | |||
1139 | } | 1114 | } |
1140 | else // show the change password form. | 1115 | else // show the change password form. |
1141 | { | 1116 | { |
1142 | $PAGE->assign('token',getToken()); | ||
1143 | $PAGE->renderPage('changepassword'); | 1117 | $PAGE->renderPage('changepassword'); |
1144 | exit; | 1118 | exit; |
1145 | } | 1119 | } |
@@ -1159,17 +1133,17 @@ function renderPage() | |||
1159 | ) { | 1133 | ) { |
1160 | $tz = $_POST['continent'] . '/' . $_POST['city']; | 1134 | $tz = $_POST['continent'] . '/' . $_POST['city']; |
1161 | } | 1135 | } |
1162 | $GLOBALS['timezone'] = $tz; | 1136 | $conf->set('general.timezone', $tz); |
1163 | $GLOBALS['title']=$_POST['title']; | 1137 | $conf->set('general.title', escape($_POST['title'])); |
1164 | $GLOBALS['titleLink']=$_POST['titleLink']; | 1138 | $conf->set('general.header_link', escape($_POST['titleLink'])); |
1165 | $GLOBALS['redirector']=$_POST['redirector']; | 1139 | $conf->set('redirector.url', escape($_POST['redirector'])); |
1166 | $GLOBALS['disablesessionprotection']=!empty($_POST['disablesessionprotection']); | 1140 | $conf->set('security.session_protection_disabled', !empty($_POST['disablesessionprotection'])); |
1167 | $GLOBALS['privateLinkByDefault']=!empty($_POST['privateLinkByDefault']); | 1141 | $conf->set('privacy.default_private_links', !empty($_POST['privateLinkByDefault'])); |
1168 | $GLOBALS['config']['ENABLE_RSS_PERMALINKS']= !empty($_POST['enableRssPermalinks']); | 1142 | $conf->set('feed.rss_permalinks', !empty($_POST['enableRssPermalinks'])); |
1169 | $GLOBALS['config']['ENABLE_UPDATECHECK'] = !empty($_POST['updateCheck']); | 1143 | $conf->set('updates.check_updates', !empty($_POST['updateCheck'])); |
1170 | $GLOBALS['config']['HIDE_PUBLIC_LINKS'] = !empty($_POST['hidePublicLinks']); | 1144 | $conf->set('privacy.hide_public_links', !empty($_POST['hidePublicLinks'])); |
1171 | try { | 1145 | try { |
1172 | writeConfig($GLOBALS, isLoggedIn()); | 1146 | $conf->write(isLoggedIn()); |
1173 | } | 1147 | } |
1174 | catch(Exception $e) { | 1148 | catch(Exception $e) { |
1175 | error_log( | 1149 | error_log( |
@@ -1178,20 +1152,24 @@ function renderPage() | |||
1178 | ); | 1152 | ); |
1179 | 1153 | ||
1180 | // TODO: do not handle exceptions/errors in JS. | 1154 | // TODO: do not handle exceptions/errors in JS. |
1181 | echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=tools\';</script>'; | 1155 | echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=configure\';</script>'; |
1182 | exit; | 1156 | exit; |
1183 | } | 1157 | } |
1184 | echo '<script>alert("Configuration was saved.");document.location=\'?do=tools\';</script>'; | 1158 | echo '<script>alert("Configuration was saved.");document.location=\'?do=configure\';</script>'; |
1185 | exit; | 1159 | exit; |
1186 | } | 1160 | } |
1187 | else // Show the configuration form. | 1161 | else // Show the configuration form. |
1188 | { | 1162 | { |
1189 | $PAGE->assign('token',getToken()); | 1163 | $PAGE->assign('title', $conf->get('general.title')); |
1190 | $PAGE->assign('title', empty($GLOBALS['title']) ? '' : $GLOBALS['title'] ); | 1164 | $PAGE->assign('redirector', $conf->get('redirector.url')); |
1191 | $PAGE->assign('redirector', empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] ); | 1165 | list($timezone_form, $timezone_js) = generateTimeZoneForm($conf->get('general.timezone')); |
1192 | list($timezone_form, $timezone_js) = generateTimeZoneForm($GLOBALS['timezone']); | ||
1193 | $PAGE->assign('timezone_form', $timezone_form); | 1166 | $PAGE->assign('timezone_form', $timezone_form); |
1194 | $PAGE->assign('timezone_js',$timezone_js); | 1167 | $PAGE->assign('timezone_js',$timezone_js); |
1168 | $PAGE->assign('private_links_default', $conf->get('privacy.default_private_links', false)); | ||
1169 | $PAGE->assign('session_protection_disabled', $conf->get('security.session_protection_disabled', false)); | ||
1170 | $PAGE->assign('enable_rss_permalinks', $conf->get('feed.rss_permalinks', false)); | ||
1171 | $PAGE->assign('enable_update_check', $conf->get('updates.check_updates', true)); | ||
1172 | $PAGE->assign('hide_public_links', $conf->get('privacy.hide_public_links', false)); | ||
1195 | $PAGE->renderPage('configure'); | 1173 | $PAGE->renderPage('configure'); |
1196 | exit; | 1174 | exit; |
1197 | } | 1175 | } |
@@ -1201,7 +1179,6 @@ function renderPage() | |||
1201 | if ($targetPage == Router::$PAGE_CHANGETAG) | 1179 | if ($targetPage == Router::$PAGE_CHANGETAG) |
1202 | { | 1180 | { |
1203 | if (empty($_POST['fromtag']) || (empty($_POST['totag']) && isset($_POST['renametag']))) { | 1181 | if (empty($_POST['fromtag']) || (empty($_POST['totag']) && isset($_POST['renametag']))) { |
1204 | $PAGE->assign('token', getToken()); | ||
1205 | $PAGE->assign('tags', $LINKSDB->allTags()); | 1182 | $PAGE->assign('tags', $LINKSDB->allTags()); |
1206 | $PAGE->renderPage('changetag'); | 1183 | $PAGE->renderPage('changetag'); |
1207 | exit; | 1184 | exit; |
@@ -1223,7 +1200,7 @@ function renderPage() | |||
1223 | $value['tags']=trim(implode(' ',$tags)); | 1200 | $value['tags']=trim(implode(' ',$tags)); |
1224 | $LINKSDB[$key]=$value; | 1201 | $LINKSDB[$key]=$value; |
1225 | } | 1202 | } |
1226 | $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); | 1203 | $LINKSDB->save($conf->get('resource.page_cache')); |
1227 | echo '<script>alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>'; | 1204 | echo '<script>alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>'; |
1228 | exit; | 1205 | exit; |
1229 | } | 1206 | } |
@@ -1240,7 +1217,7 @@ function renderPage() | |||
1240 | $value['tags']=trim(implode(' ',$tags)); | 1217 | $value['tags']=trim(implode(' ',$tags)); |
1241 | $LINKSDB[$key]=$value; | 1218 | $LINKSDB[$key]=$value; |
1242 | } | 1219 | } |
1243 | $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk. | 1220 | $LINKSDB->save($conf->get('resource.page_cache')); // Save to disk. |
1244 | echo '<script>alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>'; | 1221 | echo '<script>alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>'; |
1245 | exit; | 1222 | exit; |
1246 | } | 1223 | } |
@@ -1260,13 +1237,33 @@ function renderPage() | |||
1260 | if (! tokenOk($_POST['token'])) { | 1237 | if (! tokenOk($_POST['token'])) { |
1261 | die('Wrong token.'); | 1238 | die('Wrong token.'); |
1262 | } | 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 | |||
1263 | // Remove multiple spaces. | 1260 | // Remove multiple spaces. |
1264 | $tags = trim(preg_replace('/\s\s+/', ' ', $_POST['lf_tags'])); | 1261 | $tags = trim(preg_replace('/\s\s+/', ' ', $_POST['lf_tags'])); |
1265 | // Remove first '-' char in tags. | 1262 | // Remove first '-' char in tags. |
1266 | $tags = preg_replace('/(^| )\-/', '$1', $tags); | 1263 | $tags = preg_replace('/(^| )\-/', '$1', $tags); |
1267 | // Remove duplicates. | 1264 | // Remove duplicates. |
1268 | $tags = implode(' ', array_unique(explode(' ', $tags))); | 1265 | $tags = implode(' ', array_unique(explode(' ', $tags))); |
1269 | $linkdate = $_POST['lf_linkdate']; | 1266 | |
1270 | $url = trim($_POST['lf_url']); | 1267 | $url = trim($_POST['lf_url']); |
1271 | if (! startsWith($url, 'http:') && ! startsWith($url, 'https:') | 1268 | if (! startsWith($url, 'http:') && ! startsWith($url, 'https:') |
1272 | && ! startsWith($url, 'ftp:') && ! startsWith($url, 'magnet:') | 1269 | && ! startsWith($url, 'ftp:') && ! startsWith($url, 'magnet:') |
@@ -1276,13 +1273,17 @@ function renderPage() | |||
1276 | } | 1273 | } |
1277 | 1274 | ||
1278 | $link = array( | 1275 | $link = array( |
1276 | 'id' => $id, | ||
1279 | 'title' => trim($_POST['lf_title']), | 1277 | 'title' => trim($_POST['lf_title']), |
1280 | 'url' => $url, | 1278 | 'url' => $url, |
1281 | 'description' => $_POST['lf_description'], | 1279 | 'description' => $_POST['lf_description'], |
1282 | 'private' => (isset($_POST['lf_private']) ? 1 : 0), | 1280 | 'private' => (isset($_POST['lf_private']) ? 1 : 0), |
1283 | 'linkdate' => $linkdate, | 1281 | 'created' => $created, |
1284 | 'tags' => str_replace(',', ' ', $tags) | 1282 | 'updated' => $updated, |
1283 | 'tags' => str_replace(',', ' ', $tags), | ||
1284 | 'shorturl' => $shortUrl, | ||
1285 | ); | 1285 | ); |
1286 | |||
1286 | // If title is empty, use the URL as title. | 1287 | // If title is empty, use the URL as title. |
1287 | if ($link['title'] == '') { | 1288 | if ($link['title'] == '') { |
1288 | $link['title'] = $link['url']; | 1289 | $link['title'] = $link['url']; |
@@ -1290,9 +1291,9 @@ function renderPage() | |||
1290 | 1291 | ||
1291 | $pluginManager->executeHooks('save_link', $link); | 1292 | $pluginManager->executeHooks('save_link', $link); |
1292 | 1293 | ||
1293 | $LINKSDB[$linkdate] = $link; | 1294 | $LINKSDB[$id] = $link; |
1294 | $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); | 1295 | $LINKSDB->save($conf->get('resource.page_cache')); |
1295 | pubsubhub(); | 1296 | pubsubhub($conf); |
1296 | 1297 | ||
1297 | // If we are called from the bookmarklet, we must close the popup: | 1298 | // If we are called from the bookmarklet, we must close the popup: |
1298 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { | 1299 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { |
@@ -1303,7 +1304,7 @@ function renderPage() | |||
1303 | $returnurl = !empty($_POST['returnurl']) ? $_POST['returnurl'] : '?'; | 1304 | $returnurl = !empty($_POST['returnurl']) ? $_POST['returnurl'] : '?'; |
1304 | $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); | 1305 | $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); |
1305 | // Scroll to the link which has been edited. | 1306 | // Scroll to the link which has been edited. |
1306 | $location .= '#' . smallHash($_POST['lf_linkdate']); | 1307 | $location .= '#' . $link['shorturl']; |
1307 | // 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. |
1308 | header('Location: '. $location); | 1309 | header('Location: '. $location); |
1309 | exit; | 1310 | exit; |
@@ -1314,8 +1315,10 @@ function renderPage() | |||
1314 | { | 1315 | { |
1315 | // 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: |
1316 | 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'])]; | ||
1317 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); | 1319 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); |
1318 | $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']; | ||
1319 | $returnurl = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); | 1322 | $returnurl = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); |
1320 | 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. |
1321 | exit; | 1324 | exit; |
@@ -1325,15 +1328,18 @@ function renderPage() | |||
1325 | if (isset($_POST['delete_link'])) | 1328 | if (isset($_POST['delete_link'])) |
1326 | { | 1329 | { |
1327 | if (!tokenOk($_POST['token'])) die('Wrong token.'); | 1330 | if (!tokenOk($_POST['token'])) die('Wrong token.'); |
1331 | |||
1328 | // We do not need to ask for confirmation: | 1332 | // We do not need to ask for confirmation: |
1329 | // - confirmation is handled by JavaScript | 1333 | // - confirmation is handled by JavaScript |
1330 | // - we are protected from XSRF by the token. | 1334 | // - we are protected from XSRF by the token. |
1331 | $linkdate=$_POST['lf_linkdate']; | ||
1332 | 1335 | ||
1333 | $pluginManager->executeHooks('delete_link', $LINKSDB[$linkdate]); | 1336 | // FIXME! We keep `lf_linkdate` for consistency before a proper API. To be removed. |
1337 | $id = isset($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : intval(escape($_POST['lf_linkdate'])); | ||
1338 | |||
1339 | $pluginManager->executeHooks('delete_link', $LINKSDB[$id]); | ||
1334 | 1340 | ||
1335 | unset($LINKSDB[$linkdate]); | 1341 | unset($LINKSDB[$id]); |
1336 | $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // save to disk | 1342 | $LINKSDB->save('resource.page_cache'); // save to disk |
1337 | 1343 | ||
1338 | // 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: |
1339 | 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; } |
@@ -1371,12 +1377,13 @@ function renderPage() | |||
1371 | // -------- 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. |
1372 | if (isset($_GET['edit_link'])) | 1378 | if (isset($_GET['edit_link'])) |
1373 | { | 1379 | { |
1374 | $link = $LINKSDB[$_GET['edit_link']]; // Read database | 1380 | $id = (int) escape($_GET['edit_link']); |
1381 | $link = $LINKSDB[$id]; // Read database | ||
1375 | 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); | ||
1376 | $data = array( | 1384 | $data = array( |
1377 | 'link' => $link, | 1385 | 'link' => $link, |
1378 | 'link_is_new' => false, | 1386 | 'link_is_new' => false, |
1379 | 'token' => getToken(), | ||
1380 | 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), | 1387 | 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), |
1381 | 'tags' => $LINKSDB->allTags(), | 1388 | 'tags' => $LINKSDB->allTags(), |
1382 | ); | 1389 | ); |
@@ -1397,10 +1404,10 @@ function renderPage() | |||
1397 | $link_is_new = false; | 1404 | $link_is_new = false; |
1398 | // 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) |
1399 | $link = $LINKSDB->getLinkFromUrl($url); | 1406 | $link = $LINKSDB->getLinkFromUrl($url); |
1400 | if (!$link) | 1407 | if (! $link) |
1401 | { | 1408 | { |
1402 | $link_is_new = true; | 1409 | $link_is_new = true; |
1403 | $linkdate = strval(date('Ymd_His')); | 1410 | $linkdate = strval(date(LinkDB::LINK_DATE_FORMAT)); |
1404 | // Get title if it was provided in URL (by the bookmarklet). | 1411 | // Get title if it was provided in URL (by the bookmarklet). |
1405 | $title = empty($_GET['title']) ? '' : escape($_GET['title']); | 1412 | $title = empty($_GET['title']) ? '' : escape($_GET['title']); |
1406 | // 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] |
@@ -1424,7 +1431,7 @@ function renderPage() | |||
1424 | } | 1431 | } |
1425 | 1432 | ||
1426 | if ($url == '') { | 1433 | if ($url == '') { |
1427 | $url = '?' . smallHash($linkdate); | 1434 | $url = '?' . smallHash($linkdate . $LINKSDB->getNextId()); |
1428 | $title = 'Note: '; | 1435 | $title = 'Note: '; |
1429 | } | 1436 | } |
1430 | $url = escape($url); | 1437 | $url = escape($url); |
@@ -1438,15 +1445,17 @@ function renderPage() | |||
1438 | 'tags' => $tags, | 1445 | 'tags' => $tags, |
1439 | 'private' => $private | 1446 | 'private' => $private |
1440 | ); | 1447 | ); |
1448 | } else { | ||
1449 | $link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT); | ||
1441 | } | 1450 | } |
1442 | 1451 | ||
1443 | $data = array( | 1452 | $data = array( |
1444 | 'link' => $link, | 1453 | 'link' => $link, |
1445 | 'link_is_new' => $link_is_new, | 1454 | 'link_is_new' => $link_is_new, |
1446 | 'token' => getToken(), // XSRF protection. | ||
1447 | 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), | 1455 | 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), |
1448 | 'source' => (isset($_GET['source']) ? $_GET['source'] : ''), | 1456 | 'source' => (isset($_GET['source']) ? $_GET['source'] : ''), |
1449 | 'tags' => $LINKSDB->allTags(), | 1457 | 'tags' => $LINKSDB->allTags(), |
1458 | 'default_private_links' => $conf->get('privacy.default_private_links', false), | ||
1450 | ); | 1459 | ); |
1451 | $pluginManager->executeHooks('render_editlink', $data); | 1460 | $pluginManager->executeHooks('render_editlink', $data); |
1452 | 1461 | ||
@@ -1502,27 +1511,37 @@ function renderPage() | |||
1502 | exit; | 1511 | exit; |
1503 | } | 1512 | } |
1504 | 1513 | ||
1505 | // -------- User is uploading a file for import | 1514 | if ($targetPage == Router::$PAGE_IMPORT) { |
1506 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=upload')) | 1515 | // Upload a Netscape bookmark dump to import its contents |
1507 | { | 1516 | |
1508 | // If file is too big, some form field may be missing. | 1517 | if (! isset($_POST['token']) || ! isset($_FILES['filetoupload'])) { |
1509 | if (!isset($_POST['token']) || (!isset($_FILES)) || (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size']==0)) | 1518 | // Show import dialog |
1510 | { | 1519 | $PAGE->assign('maxfilesize', getMaxFileSize()); |
1511 | $returnurl = ( empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER'] ); | 1520 | $PAGE->renderPage('import'); |
1512 | echo '<script>alert("The file you are trying to upload is probably bigger than what this webserver can accept ('.getMaxFileSize().' bytes). Please upload in smaller chunks.");document.location=\''.escape($returnurl).'\';</script>'; | ||
1513 | exit; | 1521 | exit; |
1514 | } | 1522 | } |
1515 | if (!tokenOk($_POST['token'])) die('Wrong token.'); | ||
1516 | importFile($LINKSDB); | ||
1517 | exit; | ||
1518 | } | ||
1519 | 1523 | ||
1520 | // -------- Show upload/import dialog: | 1524 | // Import bookmarks from an uploaded file |
1521 | if ($targetPage == Router::$PAGE_IMPORT) | 1525 | if (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) { |
1522 | { | 1526 | // The file is too big or some form field may be missing. |
1523 | $PAGE->assign('token',getToken()); | 1527 | echo '<script>alert("The file you are trying to upload is probably' |
1524 | $PAGE->assign('maxfilesize',getMaxFileSize()); | 1528 | .' bigger than what this webserver can accept (' |
1525 | $PAGE->renderPage('import'); | 1529 | .getMaxFileSize().' bytes).' |
1530 | .' Please upload in smaller chunks.");document.location=\'?do=' | ||
1531 | .Router::$PAGE_IMPORT .'\';</script>'; | ||
1532 | exit; | ||
1533 | } | ||
1534 | if (! tokenOk($_POST['token'])) { | ||
1535 | die('Wrong token.'); | ||
1536 | } | ||
1537 | $status = NetscapeBookmarkUtils::import( | ||
1538 | $_POST, | ||
1539 | $_FILES, | ||
1540 | $LINKSDB, | ||
1541 | $conf->get('resource.page_cache') | ||
1542 | ); | ||
1543 | echo '<script>alert("'.$status.'");document.location=\'?do=' | ||
1544 | .Router::$PAGE_IMPORT .'\';</script>'; | ||
1526 | exit; | 1545 | exit; |
1527 | } | 1546 | } |
1528 | 1547 | ||
@@ -1533,7 +1552,7 @@ function renderPage() | |||
1533 | // Split plugins into 2 arrays: ordered enabled plugins and disabled. | 1552 | // Split plugins into 2 arrays: ordered enabled plugins and disabled. |
1534 | $enabledPlugins = array_filter($pluginMeta, function($v) { return $v['order'] !== false; }); | 1553 | $enabledPlugins = array_filter($pluginMeta, function($v) { return $v['order'] !== false; }); |
1535 | // Load parameters. | 1554 | // Load parameters. |
1536 | $enabledPlugins = load_plugin_parameter_values($enabledPlugins, $GLOBALS['plugins']); | 1555 | $enabledPlugins = load_plugin_parameter_values($enabledPlugins, $conf->get('plugins', array())); |
1537 | uasort( | 1556 | uasort( |
1538 | $enabledPlugins, | 1557 | $enabledPlugins, |
1539 | function($a, $b) { return $a['order'] - $b['order']; } | 1558 | function($a, $b) { return $a['order'] - $b['order']; } |
@@ -1552,13 +1571,13 @@ function renderPage() | |||
1552 | if (isset($_POST['parameters_form'])) { | 1571 | if (isset($_POST['parameters_form'])) { |
1553 | unset($_POST['parameters_form']); | 1572 | unset($_POST['parameters_form']); |
1554 | foreach ($_POST as $param => $value) { | 1573 | foreach ($_POST as $param => $value) { |
1555 | $GLOBALS['plugins'][$param] = escape($value); | 1574 | $conf->set('plugins.'. $param, escape($value)); |
1556 | } | 1575 | } |
1557 | } | 1576 | } |
1558 | else { | 1577 | else { |
1559 | $GLOBALS['config']['ENABLED_PLUGINS'] = save_plugin_config($_POST); | 1578 | $conf->set('general.enabled_plugins', save_plugin_config($_POST)); |
1560 | } | 1579 | } |
1561 | writeConfig($GLOBALS, isLoggedIn()); | 1580 | $conf->write(isLoggedIn()); |
1562 | } | 1581 | } |
1563 | catch (Exception $e) { | 1582 | catch (Exception $e) { |
1564 | error_log( | 1583 | error_log( |
@@ -1575,103 +1594,20 @@ function renderPage() | |||
1575 | } | 1594 | } |
1576 | 1595 | ||
1577 | // -------- Otherwise, simply display search form and links: | 1596 | // -------- Otherwise, simply display search form and links: |
1578 | showLinkList($PAGE, $LINKSDB); | 1597 | showLinkList($PAGE, $LINKSDB, $conf, $pluginManager); |
1579 | exit; | 1598 | exit; |
1580 | } | 1599 | } |
1581 | 1600 | ||
1582 | // ----------------------------------------------------------------------------------------------- | ||
1583 | // Process the import file form. | ||
1584 | function importFile($LINKSDB) | ||
1585 | { | ||
1586 | if (!isLoggedIn()) { die('Not allowed.'); } | ||
1587 | |||
1588 | $filename=$_FILES['filetoupload']['name']; | ||
1589 | $filesize=$_FILES['filetoupload']['size']; | ||
1590 | $data=file_get_contents($_FILES['filetoupload']['tmp_name']); | ||
1591 | $private = (empty($_POST['private']) ? 0 : 1); // Should the links be imported as private? | ||
1592 | $overwrite = !empty($_POST['overwrite']) ; // Should the imported links overwrite existing ones? | ||
1593 | $import_count=0; | ||
1594 | |||
1595 | // Sniff file type: | ||
1596 | $type='unknown'; | ||
1597 | if (startsWith($data,'<!DOCTYPE NETSCAPE-Bookmark-file-1>')) $type='netscape'; // Netscape bookmark file (aka Firefox). | ||
1598 | |||
1599 | // Then import the bookmarks. | ||
1600 | if ($type=='netscape') | ||
1601 | { | ||
1602 | // This is a standard Netscape-style bookmark file. | ||
1603 | // This format is supported by all browsers (except IE, of course), also Delicious, Diigo and others. | ||
1604 | foreach(explode('<DT>',$data) as $html) // explode is very fast | ||
1605 | { | ||
1606 | $link = array('linkdate'=>'','title'=>'','url'=>'','description'=>'','tags'=>'','private'=>0); | ||
1607 | $d = explode('<DD>',$html); | ||
1608 | if (startsWith($d[0], '<A ')) | ||
1609 | { | ||
1610 | $link['description'] = (isset($d[1]) ? html_entity_decode(trim($d[1]),ENT_QUOTES,'UTF-8') : ''); // Get description (optional) | ||
1611 | preg_match('!<A .*?>(.*?)</A>!i',$d[0],$matches); $link['title'] = (isset($matches[1]) ? trim($matches[1]) : ''); // Get title | ||
1612 | $link['title'] = html_entity_decode($link['title'],ENT_QUOTES,'UTF-8'); | ||
1613 | preg_match_all('! ([A-Z_]+)=\"(.*?)"!i',$html,$matches,PREG_SET_ORDER); // Get all other attributes | ||
1614 | $raw_add_date=0; | ||
1615 | foreach($matches as $m) | ||
1616 | { | ||
1617 | $attr=$m[1]; $value=$m[2]; | ||
1618 | if ($attr=='HREF') $link['url']=html_entity_decode($value,ENT_QUOTES,'UTF-8'); | ||
1619 | elseif ($attr=='ADD_DATE') | ||
1620 | { | ||
1621 | $raw_add_date=intval($value); | ||
1622 | if ($raw_add_date>30000000000) $raw_add_date/=1000; //If larger than year 2920, then was likely stored in milliseconds instead of seconds | ||
1623 | } | ||
1624 | elseif ($attr=='PRIVATE') $link['private']=($value=='0'?0:1); | ||
1625 | elseif ($attr=='TAGS') $link['tags']=html_entity_decode(str_replace(',',' ',$value),ENT_QUOTES,'UTF-8'); | ||
1626 | } | ||
1627 | if ($link['url']!='') | ||
1628 | { | ||
1629 | if ($private==1) $link['private']=1; | ||
1630 | $dblink = $LINKSDB->getLinkFromUrl($link['url']); // See if the link is already in database. | ||
1631 | if ($dblink==false) | ||
1632 | { // Link not in database, let's import it... | ||
1633 | if (empty($raw_add_date)) $raw_add_date=time(); // In case of shitty bookmark file with no ADD_DATE | ||
1634 | |||
1635 | // Make sure date/time is not already used by another link. | ||
1636 | // (Some bookmark files have several different links with the same ADD_DATE) | ||
1637 | // We increment date by 1 second until we find a date which is not used in DB. | ||
1638 | // (so that links that have the same date/time are more or less kept grouped by date, but do not conflict.) | ||
1639 | while (!empty($LINKSDB[date('Ymd_His',$raw_add_date)])) { $raw_add_date++; }// Yes, I know it's ugly. | ||
1640 | $link['linkdate']=date('Ymd_His',$raw_add_date); | ||
1641 | $LINKSDB[$link['linkdate']] = $link; | ||
1642 | $import_count++; | ||
1643 | } | ||
1644 | else // Link already present in database. | ||
1645 | { | ||
1646 | if ($overwrite) | ||
1647 | { // If overwrite is required, we import link data, except date/time. | ||
1648 | $link['linkdate']=$dblink['linkdate']; | ||
1649 | $LINKSDB[$link['linkdate']] = $link; | ||
1650 | $import_count++; | ||
1651 | } | ||
1652 | } | ||
1653 | |||
1654 | } | ||
1655 | } | ||
1656 | } | ||
1657 | $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); | ||
1658 | |||
1659 | echo '<script>alert("File '.json_encode($filename).' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>'; | ||
1660 | } | ||
1661 | else | ||
1662 | { | ||
1663 | echo '<script>alert("File '.json_encode($filename).' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>'; | ||
1664 | } | ||
1665 | } | ||
1666 | |||
1667 | /** | 1601 | /** |
1668 | * Template for the list of links (<div id="linklist">) | 1602 | * Template for the list of links (<div id="linklist">) |
1669 | * This function fills all the necessary fields in the $PAGE for the template 'linklist.html' | 1603 | * This function fills all the necessary fields in the $PAGE for the template 'linklist.html' |
1670 | * | 1604 | * |
1671 | * @param pageBuilder $PAGE pageBuilder instance. | 1605 | * @param pageBuilder $PAGE pageBuilder instance. |
1672 | * @param LinkDB $LINKSDB LinkDB instance. | 1606 | * @param LinkDB $LINKSDB LinkDB instance. |
1607 | * @param ConfigManager $conf Configuration Manager instance. | ||
1608 | * @param PluginManager $pluginManager Plugin Manager instance. | ||
1673 | */ | 1609 | */ |
1674 | function buildLinkList($PAGE,$LINKSDB) | 1610 | function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) |
1675 | { | 1611 | { |
1676 | // Used in templates | 1612 | // Used in templates |
1677 | $searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : ''; | 1613 | $searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : ''; |
@@ -1698,10 +1634,7 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1698 | $keys[] = $key; | 1634 | $keys[] = $key; |
1699 | } | 1635 | } |
1700 | 1636 | ||
1701 | // If there is only a single link, we change on-the-fly the title of the page. | 1637 | |
1702 | if (count($linksToDisplay) == 1) { | ||
1703 | $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title']; | ||
1704 | } | ||
1705 | 1638 | ||
1706 | // Select articles according to paging. | 1639 | // Select articles according to paging. |
1707 | $pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']); | 1640 | $pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']); |
@@ -1716,15 +1649,18 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1716 | while ($i<$end && $i<count($keys)) | 1649 | while ($i<$end && $i<count($keys)) |
1717 | { | 1650 | { |
1718 | $link = $linksToDisplay[$keys[$i]]; | 1651 | $link = $linksToDisplay[$keys[$i]]; |
1719 | $link['description'] = format_description($link['description'], $GLOBALS['redirector']); | 1652 | $link['description'] = format_description($link['description'], $conf->get('redirector.url')); |
1720 | $classLi = ($i % 2) != 0 ? '' : 'publicLinkHightLight'; | 1653 | $classLi = ($i % 2) != 0 ? '' : 'publicLinkHightLight'; |
1721 | $link['class'] = $link['private'] == 0 ? $classLi : 'private'; | 1654 | $link['class'] = $link['private'] == 0 ? $classLi : 'private'; |
1722 | $date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']); | 1655 | $link['timestamp'] = $link['created']->getTimestamp(); |
1723 | $link['timestamp'] = $date->getTimestamp(); | 1656 | if (! empty($link['updated'])) { |
1657 | $link['updated_timestamp'] = $link['updated']->getTimestamp(); | ||
1658 | } else { | ||
1659 | $link['updated_timestamp'] = ''; | ||
1660 | } | ||
1724 | $taglist = explode(' ', $link['tags']); | 1661 | $taglist = explode(' ', $link['tags']); |
1725 | uasort($taglist, 'strcasecmp'); | 1662 | uasort($taglist, 'strcasecmp'); |
1726 | $link['taglist'] = $taglist; | 1663 | $link['taglist'] = $taglist; |
1727 | $link['shorturl'] = smallHash($link['linkdate']); | ||
1728 | // 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. |
1729 | if ($link['url'][0] === '?' && | 1665 | if ($link['url'][0] === '?' && |
1730 | strlen($link['url']) === 7) { | 1666 | strlen($link['url']) === 7) { |
@@ -1747,8 +1683,6 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1747 | $next_page_url = '?page=' . ($page-1) . $searchtermUrl . $searchtagsUrl; | 1683 | $next_page_url = '?page=' . ($page-1) . $searchtermUrl . $searchtagsUrl; |
1748 | } | 1684 | } |
1749 | 1685 | ||
1750 | $token = isLoggedIn() ? getToken() : ''; | ||
1751 | |||
1752 | // Fill all template fields. | 1686 | // Fill all template fields. |
1753 | $data = array( | 1687 | $data = array( |
1754 | 'previous_page_url' => $previous_page_url, | 1688 | 'previous_page_url' => $previous_page_url, |
@@ -1758,17 +1692,16 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1758 | 'result_count' => count($linksToDisplay), | 1692 | 'result_count' => count($linksToDisplay), |
1759 | 'search_term' => $searchterm, | 1693 | 'search_term' => $searchterm, |
1760 | 'search_tags' => $searchtags, | 1694 | 'search_tags' => $searchtags, |
1761 | 'redirector' => empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'], // Optional redirector URL. | 1695 | 'redirector' => $conf->get('redirector.url'), // Optional redirector URL. |
1762 | 'token' => $token, | ||
1763 | 'links' => $linkDisp, | 1696 | 'links' => $linkDisp, |
1764 | 'tags' => $LINKSDB->allTags(), | 1697 | 'tags' => $LINKSDB->allTags(), |
1765 | ); | 1698 | ); |
1766 | // FIXME! temporary fix - see #399. | 1699 | |
1767 | if (!empty($GLOBALS['pagetitle']) && count($linkDisp) == 1) { | 1700 | // If there is only a single link, we change on-the-fly the title of the page. |
1768 | $data['pagetitle'] = $GLOBALS['pagetitle']; | 1701 | if (count($linksToDisplay) == 1) { |
1702 | $data['pagetitle'] = $linksToDisplay[$keys[0]]['title'] .' - '. $conf->get('general.title'); | ||
1769 | } | 1703 | } |
1770 | 1704 | ||
1771 | $pluginManager = PluginManager::getInstance(); | ||
1772 | $pluginManager->executeHooks('render_linklist', $data, array('loggedin' => isLoggedIn())); | 1705 | $pluginManager->executeHooks('render_linklist', $data, array('loggedin' => isLoggedIn())); |
1773 | 1706 | ||
1774 | foreach ($data as $key => $value) { | 1707 | foreach ($data as $key => $value) { |
@@ -1778,18 +1711,26 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1778 | return; | 1711 | return; |
1779 | } | 1712 | } |
1780 | 1713 | ||
1781 | // Compute the thumbnail for a link. | 1714 | /** |
1782 | // | 1715 | * Compute the thumbnail for a link. |
1783 | // With a link to the original URL. | 1716 | * |
1784 | // Understands various services (youtube.com...) | 1717 | * With a link to the original URL. |
1785 | // Input: $url = URL for which the thumbnail must be found. | 1718 | * Understands various services (youtube.com...) |
1786 | // $href = if provided, this URL will be followed instead of $url | 1719 | * Input: $url = URL for which the thumbnail must be found. |
1787 | // Returns an associative array with thumbnail attributes (src,href,width,height,style,alt) | 1720 | * $href = if provided, this URL will be followed instead of $url |
1788 | // Some of them may be missing. | 1721 | * Returns an associative array with thumbnail attributes (src,href,width,height,style,alt) |
1789 | // Return an empty array if no thumbnail available. | 1722 | * Some of them may be missing. |
1790 | function computeThumbnail($url,$href=false) | 1723 | * Return an empty array if no thumbnail available. |
1724 | * | ||
1725 | * @param ConfigManager $conf Configuration Manager instance. | ||
1726 | * @param string $url | ||
1727 | * @param string|bool $href | ||
1728 | * | ||
1729 | * @return array | ||
1730 | */ | ||
1731 | function computeThumbnail($conf, $url, $href = false) | ||
1791 | { | 1732 | { |
1792 | if (!$GLOBALS['config']['ENABLE_THUMBNAILS']) return array(); | 1733 | if (!$conf->get('thumbnail.enable_thumbnails')) return array(); |
1793 | if ($href==false) $href=$url; | 1734 | if ($href==false) $href=$url; |
1794 | 1735 | ||
1795 | // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link. | 1736 | // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link. |
@@ -1857,7 +1798,7 @@ function computeThumbnail($url,$href=false) | |||
1857 | // So we deport the thumbnail generation in order not to slow down page generation | 1798 | // So we deport the thumbnail generation in order not to slow down page generation |
1858 | // (and we also cache the thumbnail) | 1799 | // (and we also cache the thumbnail) |
1859 | 1800 | ||
1860 | if (!$GLOBALS['config']['ENABLE_LOCALCACHE']) return array(); // If local cache is disabled, no thumbnails for services which require the use a local cache. | 1801 | if (! $conf->get('thumbnail.enable_localcache')) return array(); // If local cache is disabled, no thumbnails for services which require the use a local cache. |
1861 | 1802 | ||
1862 | if ($domain=='flickr.com' || endsWith($domain,'.flickr.com') | 1803 | if ($domain=='flickr.com' || endsWith($domain,'.flickr.com') |
1863 | || $domain=='vimeo.com' | 1804 | || $domain=='vimeo.com' |
@@ -1880,7 +1821,7 @@ function computeThumbnail($url,$href=false) | |||
1880 | $path = parse_url($url,PHP_URL_PATH); | 1821 | $path = parse_url($url,PHP_URL_PATH); |
1881 | if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. | 1822 | if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. |
1882 | } | 1823 | } |
1883 | $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation) | 1824 | $sign = hash_hmac('sha256', $url, $conf->get('credentials.salt')); // We use the salt to sign data (it's random, secret, and specific to each installation) |
1884 | return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url), | 1825 | return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url), |
1885 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); | 1826 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); |
1886 | } | 1827 | } |
@@ -1891,7 +1832,7 @@ function computeThumbnail($url,$href=false) | |||
1891 | $ext=strtolower(pathinfo($url,PATHINFO_EXTENSION)); | 1832 | $ext=strtolower(pathinfo($url,PATHINFO_EXTENSION)); |
1892 | if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') | 1833 | if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') |
1893 | { | 1834 | { |
1894 | $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation) | 1835 | $sign = hash_hmac('sha256', $url, $conf->get('credentials.salt')); // We use the salt to sign data (it's random, secret, and specific to each installation) |
1895 | return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url), | 1836 | return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url), |
1896 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); | 1837 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); |
1897 | } | 1838 | } |
@@ -1908,7 +1849,9 @@ function computeThumbnail($url,$href=false) | |||
1908 | // Returns '' if no thumbnail available. | 1849 | // Returns '' if no thumbnail available. |
1909 | function thumbnail($url,$href=false) | 1850 | function thumbnail($url,$href=false) |
1910 | { | 1851 | { |
1911 | $t = computeThumbnail($url,$href); | 1852 | // FIXME! |
1853 | global $conf; | ||
1854 | $t = computeThumbnail($conf, $url,$href); | ||
1912 | if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. | 1855 | if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. |
1913 | 1856 | ||
1914 | $html='<a href="'.escape($t['href']).'"><img src="'.escape($t['src']).'"'; | 1857 | $html='<a href="'.escape($t['href']).'"><img src="'.escape($t['src']).'"'; |
@@ -1926,9 +1869,11 @@ function thumbnail($url,$href=false) | |||
1926 | // Input: $url = URL for which the thumbnail must be found. | 1869 | // Input: $url = URL for which the thumbnail must be found. |
1927 | // $href = if provided, this URL will be followed instead of $url | 1870 | // $href = if provided, this URL will be followed instead of $url |
1928 | // Returns '' if no thumbnail available. | 1871 | // Returns '' if no thumbnail available. |
1929 | function lazyThumbnail($url,$href=false) | 1872 | function lazyThumbnail($conf, $url,$href=false) |
1930 | { | 1873 | { |
1931 | $t = computeThumbnail($url,$href); | 1874 | // FIXME! |
1875 | global $conf; | ||
1876 | $t = computeThumbnail($conf, $url,$href); | ||
1932 | if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. | 1877 | if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. |
1933 | 1878 | ||
1934 | $html='<a href="'.escape($t['href']).'">'; | 1879 | $html='<a href="'.escape($t['href']).'">'; |
@@ -1954,10 +1899,13 @@ function lazyThumbnail($url,$href=false) | |||
1954 | } | 1899 | } |
1955 | 1900 | ||
1956 | 1901 | ||
1957 | // ----------------------------------------------------------------------------------------------- | 1902 | /** |
1958 | // Installation | 1903 | * Installation |
1959 | // This function should NEVER be called if the file data/config.php exists. | 1904 | * This function should NEVER be called if the file data/config.php exists. |
1960 | function install() | 1905 | * |
1906 | * @param ConfigManager $conf Configuration Manager instance. | ||
1907 | */ | ||
1908 | function install($conf) | ||
1961 | { | 1909 | { |
1962 | // On free.fr host, make sure the /sessions directory exists, otherwise login will not work. | 1910 | // On free.fr host, make sure the /sessions directory exists, otherwise login will not work. |
1963 | if (endsWith($_SERVER['HTTP_HOST'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705); | 1911 | if (endsWith($_SERVER['HTTP_HOST'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705); |
@@ -1994,15 +1942,21 @@ function install() | |||
1994 | ) { | 1942 | ) { |
1995 | $tz = $_POST['continent'].'/'.$_POST['city']; | 1943 | $tz = $_POST['continent'].'/'.$_POST['city']; |
1996 | } | 1944 | } |
1997 | $GLOBALS['timezone'] = $tz; | 1945 | $conf->set('general.timezone', $tz); |
1998 | // Everything is ok, let's create config file. | 1946 | $login = $_POST['setlogin']; |
1999 | $GLOBALS['login'] = $_POST['setlogin']; | 1947 | $conf->set('credentials.login', $login); |
2000 | $GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless. | 1948 | $salt = sha1(uniqid('', true) .'_'. mt_rand()); |
2001 | $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']); | 1949 | $conf->set('credentials.salt', $salt); |
2002 | $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.escape(index_url($_SERVER)) : $_POST['title'] ); | 1950 | $conf->set('credentials.hash', sha1($_POST['setpassword'] . $login . $salt)); |
2003 | $GLOBALS['config']['ENABLE_UPDATECHECK'] = !empty($_POST['updateCheck']); | 1951 | if (!empty($_POST['title'])) { |
1952 | $conf->set('general.title', escape($_POST['title'])); | ||
1953 | } else { | ||
1954 | $conf->set('general.title', 'Shared links on '.escape(index_url($_SERVER))); | ||
1955 | } | ||
1956 | $conf->set('updates.check_updates', !empty($_POST['updateCheck'])); | ||
2004 | try { | 1957 | try { |
2005 | writeConfig($GLOBALS, isLoggedIn()); | 1958 | // Everything is ok, let's create config file. |
1959 | $conf->write(isLoggedIn()); | ||
2006 | } | 1960 | } |
2007 | catch(Exception $e) { | 1961 | catch(Exception $e) { |
2008 | error_log( | 1962 | error_log( |
@@ -2025,42 +1979,46 @@ function install() | |||
2025 | $timezone_html = '<tr><td><b>Timezone:</b></td><td>'.$timezone_form.'</td></tr>'; | 1979 | $timezone_html = '<tr><td><b>Timezone:</b></td><td>'.$timezone_form.'</td></tr>'; |
2026 | } | 1980 | } |
2027 | 1981 | ||
2028 | $PAGE = new PageBuilder(); | 1982 | $PAGE = new PageBuilder($conf); |
2029 | $PAGE->assign('timezone_html',$timezone_html); | 1983 | $PAGE->assign('timezone_html',$timezone_html); |
2030 | $PAGE->assign('timezone_js',$timezone_js); | 1984 | $PAGE->assign('timezone_js',$timezone_js); |
2031 | $PAGE->renderPage('install'); | 1985 | $PAGE->renderPage('install'); |
2032 | exit; | 1986 | exit; |
2033 | } | 1987 | } |
2034 | 1988 | ||
2035 | /* Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL, | 1989 | /** |
2036 | I have deported the thumbnail URL code generation here, otherwise this would slow down page generation. | 1990 | * Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL, |
2037 | The following function takes the URL a link (e.g. a flickr page) and return the proper thumbnail. | 1991 | * I have deported the thumbnail URL code generation here, otherwise this would slow down page generation. |
2038 | This function is called by passing the URL: | 1992 | * The following function takes the URL a link (e.g. a flickr page) and return the proper thumbnail. |
2039 | http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL] | 1993 | * This function is called by passing the URL: |
2040 | [URL] is the URL of the link (e.g. a flickr page) | 1994 | * http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL] |
2041 | [HMAC] is the signature for the [URL] (so that these URL cannot be forged). | 1995 | * [URL] is the URL of the link (e.g. a flickr page) |
2042 | The function below will fetch the image from the webservice and store it in the cache. | 1996 | * [HMAC] is the signature for the [URL] (so that these URL cannot be forged). |
2043 | */ | 1997 | * The function below will fetch the image from the webservice and store it in the cache. |
2044 | function genThumbnail() | 1998 | * |
1999 | * @param ConfigManager $conf Configuration Manager instance, | ||
2000 | */ | ||
2001 | function genThumbnail($conf) | ||
2045 | { | 2002 | { |
2046 | // Make sure the parameters in the URL were generated by us. | 2003 | // Make sure the parameters in the URL were generated by us. |
2047 | $sign = hash_hmac('sha256', $_GET['url'], $GLOBALS['salt']); | 2004 | $sign = hash_hmac('sha256', $_GET['url'], $conf->get('credentials.salt')); |
2048 | if ($sign!=$_GET['hmac']) die('Naughty boy!'); | 2005 | if ($sign!=$_GET['hmac']) die('Naughty boy!'); |
2049 | 2006 | ||
2007 | $cacheDir = $conf->get('resource.thumbnails_cache', 'cache'); | ||
2050 | // Let's see if we don't already have the image for this URL in the cache. | 2008 | // Let's see if we don't already have the image for this URL in the cache. |
2051 | $thumbname=hash('sha1',$_GET['url']).'.jpg'; | 2009 | $thumbname=hash('sha1',$_GET['url']).'.jpg'; |
2052 | if (is_file($GLOBALS['config']['CACHEDIR'].'/'.$thumbname)) | 2010 | if (is_file($cacheDir .'/'. $thumbname)) |
2053 | { // We have the thumbnail, just serve it: | 2011 | { // We have the thumbnail, just serve it: |
2054 | header('Content-Type: image/jpeg'); | 2012 | header('Content-Type: image/jpeg'); |
2055 | echo file_get_contents($GLOBALS['config']['CACHEDIR'].'/'.$thumbname); | 2013 | echo file_get_contents($cacheDir .'/'. $thumbname); |
2056 | return; | 2014 | return; |
2057 | } | 2015 | } |
2058 | // We may also serve a blank image (if service did not respond) | 2016 | // We may also serve a blank image (if service did not respond) |
2059 | $blankname=hash('sha1',$_GET['url']).'.gif'; | 2017 | $blankname=hash('sha1',$_GET['url']).'.gif'; |
2060 | if (is_file($GLOBALS['config']['CACHEDIR'].'/'.$blankname)) | 2018 | if (is_file($cacheDir .'/'. $blankname)) |
2061 | { | 2019 | { |
2062 | header('Content-Type: image/gif'); | 2020 | header('Content-Type: image/gif'); |
2063 | echo file_get_contents($GLOBALS['config']['CACHEDIR'].'/'.$blankname); | 2021 | echo file_get_contents($cacheDir .'/'. $blankname); |
2064 | return; | 2022 | return; |
2065 | } | 2023 | } |
2066 | 2024 | ||
@@ -2107,7 +2065,7 @@ function genThumbnail() | |||
2107 | list($headers, $content) = get_http_response($imageurl, 10); | 2065 | list($headers, $content) = get_http_response($imageurl, 10); |
2108 | if (strpos($headers[0], '200 OK') !== false) { | 2066 | if (strpos($headers[0], '200 OK') !== false) { |
2109 | // Save image to cache. | 2067 | // Save image to cache. |
2110 | file_put_contents($GLOBALS['config']['CACHEDIR'].'/' . $thumbname, $content); | 2068 | file_put_contents($cacheDir .'/'. $thumbname, $content); |
2111 | header('Content-Type: image/jpeg'); | 2069 | header('Content-Type: image/jpeg'); |
2112 | echo $content; | 2070 | echo $content; |
2113 | return; | 2071 | return; |
@@ -2128,7 +2086,7 @@ function genThumbnail() | |||
2128 | list($headers, $content) = get_http_response($imageurl, 10); | 2086 | list($headers, $content) = get_http_response($imageurl, 10); |
2129 | if (strpos($headers[0], '200 OK') !== false) { | 2087 | if (strpos($headers[0], '200 OK') !== false) { |
2130 | // Save image to cache. | 2088 | // Save image to cache. |
2131 | file_put_contents($GLOBALS['config']['CACHEDIR'] . '/' . $thumbname, $content); | 2089 | file_put_contents($cacheDir .'/'. $thumbname, $content); |
2132 | header('Content-Type: image/jpeg'); | 2090 | header('Content-Type: image/jpeg'); |
2133 | echo $content; | 2091 | echo $content; |
2134 | return; | 2092 | return; |
@@ -2151,7 +2109,7 @@ function genThumbnail() | |||
2151 | // No control on image size, so wait long enough | 2109 | // No control on image size, so wait long enough |
2152 | list($headers, $content) = get_http_response($imageurl, 20); | 2110 | list($headers, $content) = get_http_response($imageurl, 20); |
2153 | if (strpos($headers[0], '200 OK') !== false) { | 2111 | if (strpos($headers[0], '200 OK') !== false) { |
2154 | $filepath=$GLOBALS['config']['CACHEDIR'].'/'.$thumbname; | 2112 | $filepath = $cacheDir .'/'. $thumbname; |
2155 | file_put_contents($filepath, $content); // Save image to cache. | 2113 | file_put_contents($filepath, $content); // Save image to cache. |
2156 | if (resizeImage($filepath)) | 2114 | if (resizeImage($filepath)) |
2157 | { | 2115 | { |
@@ -2179,7 +2137,7 @@ function genThumbnail() | |||
2179 | // No control on image size, so wait long enough | 2137 | // No control on image size, so wait long enough |
2180 | list($headers, $content) = get_http_response($imageurl, 20); | 2138 | list($headers, $content) = get_http_response($imageurl, 20); |
2181 | if (strpos($headers[0], '200 OK') !== false) { | 2139 | if (strpos($headers[0], '200 OK') !== false) { |
2182 | $filepath=$GLOBALS['config']['CACHEDIR'].'/'.$thumbname; | 2140 | $filepath = $cacheDir.'/'.$thumbname; |
2183 | // Save image to cache. | 2141 | // Save image to cache. |
2184 | file_put_contents($filepath, $content); | 2142 | file_put_contents($filepath, $content); |
2185 | if (resizeImage($filepath)) | 2143 | if (resizeImage($filepath)) |
@@ -2199,7 +2157,7 @@ function genThumbnail() | |||
2199 | // We allow 30 seconds max to download (and downloads are limited to 4 Mb) | 2157 | // We allow 30 seconds max to download (and downloads are limited to 4 Mb) |
2200 | list($headers, $content) = get_http_response($url, 30); | 2158 | list($headers, $content) = get_http_response($url, 30); |
2201 | if (strpos($headers[0], '200 OK') !== false) { | 2159 | if (strpos($headers[0], '200 OK') !== false) { |
2202 | $filepath=$GLOBALS['config']['CACHEDIR'].'/'.$thumbname; | 2160 | $filepath = $cacheDir .'/'.$thumbname; |
2203 | // Save image to cache. | 2161 | // Save image to cache. |
2204 | file_put_contents($filepath, $content); | 2162 | file_put_contents($filepath, $content); |
2205 | if (resizeImage($filepath)) | 2163 | if (resizeImage($filepath)) |
@@ -2214,7 +2172,8 @@ function genThumbnail() | |||
2214 | 2172 | ||
2215 | // Otherwise, return an empty image (8x8 transparent gif) | 2173 | // Otherwise, return an empty image (8x8 transparent gif) |
2216 | $blankgif = base64_decode('R0lGODlhCAAIAIAAAP///////yH5BAEKAAEALAAAAAAIAAgAAAIHjI+py+1dAAA7'); | 2174 | $blankgif = base64_decode('R0lGODlhCAAIAIAAAP///////yH5BAEKAAEALAAAAAAIAAgAAAIHjI+py+1dAAA7'); |
2217 | file_put_contents($GLOBALS['config']['CACHEDIR'].'/'.$blankname,$blankgif); // Also put something in cache so that this URL is not requested twice. | 2175 | // Also put something in cache so that this URL is not requested twice. |
2176 | file_put_contents($cacheDir .'/'. $blankname, $blankgif); | ||
2218 | header('Content-Type: image/gif'); | 2177 | header('Content-Type: image/gif'); |
2219 | echo $blankgif; | 2178 | echo $blankgif; |
2220 | } | 2179 | } |
@@ -2252,8 +2211,9 @@ function resizeImage($filepath) | |||
2252 | return true; | 2211 | return true; |
2253 | } | 2212 | } |
2254 | 2213 | ||
2255 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=genthumbnail')) { genThumbnail(); exit; } // Thumbnail generation/cache does not need the link database. | 2214 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=genthumbnail')) { genThumbnail($conf); exit; } // Thumbnail generation/cache does not need the link database. |
2256 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) { showDailyRSS(); exit; } | 2215 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) { showDailyRSS($conf); exit; } |
2257 | if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=$GLOBALS['config']['LINKS_PER_PAGE']; | 2216 | if (!isset($_SESSION['LINKS_PER_PAGE'])) { |
2258 | renderPage(); | 2217 | $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); |
2259 | ?> | 2218 | } |
2219 | renderPage($conf, $pluginManager); | ||
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.html b/plugins/addlink_toolbar/addlink_toolbar.html deleted file mode 100644 index f38c41a0..00000000 --- a/plugins/addlink_toolbar/addlink_toolbar.html +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | <div id="addlink_toolbar"> | ||
2 | <form method="GET" action="" name="addform" class="addform"> | ||
3 | <input type="text" name="post" placeholder="URI"> | ||
4 | <input type="submit" value="Add link" class="bigbutton"> | ||
5 | </form> | ||
6 | </div> \ No newline at end of file | ||
diff --git a/plugins/addlink_toolbar/addlink_toolbar.php b/plugins/addlink_toolbar/addlink_toolbar.php index ba3849cf..bf8a198a 100644 --- a/plugins/addlink_toolbar/addlink_toolbar.php +++ b/plugins/addlink_toolbar/addlink_toolbar.php | |||
@@ -15,7 +15,27 @@ | |||
15 | function hook_addlink_toolbar_render_header($data) | 15 | 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 | $data['fields_toolbar'][] = file_get_contents(PluginManager::$PLUGINS_PATH . '/addlink_toolbar/addlink_toolbar.html'); | 18 | $form = array( |
19 | 'attr' => array( | ||
20 | 'method' => 'GET', | ||
21 | 'action' => '', | ||
22 | 'name' => 'addform', | ||
23 | 'class' => 'addform', | ||
24 | ), | ||
25 | 'inputs' => array( | ||
26 | array( | ||
27 | 'type' => 'text', | ||
28 | 'name' => 'post', | ||
29 | 'placeholder' => 'URI', | ||
30 | ), | ||
31 | array( | ||
32 | 'type' => 'submit', | ||
33 | 'value' => 'Add link', | ||
34 | 'class' => 'bigbutton', | ||
35 | ), | ||
36 | ), | ||
37 | ); | ||
38 | $data['fields_toolbar'][] = $form; | ||
19 | } | 39 | } |
20 | 40 | ||
21 | return $data; | 41 | return $data; |
@@ -35,4 +55,4 @@ function hook_addlink_toolbar_render_includes($data) | |||
35 | } | 55 | } |
36 | 56 | ||
37 | return $data; | 57 | return $data; |
38 | } \ No newline at end of file | 58 | } |
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/archiveorg/archiveorg.php b/plugins/archiveorg/archiveorg.php index 7d172584..03d13d0e 100644 --- a/plugins/archiveorg/archiveorg.php +++ b/plugins/archiveorg/archiveorg.php | |||
@@ -17,6 +17,9 @@ function hook_archiveorg_render_linklist($data) | |||
17 | $archive_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/archiveorg/archiveorg.html'); | 17 | $archive_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/archiveorg/archiveorg.html'); |
18 | 18 | ||
19 | foreach ($data['links'] as &$value) { | 19 | foreach ($data['links'] as &$value) { |
20 | if($value['private'] && preg_match('/^\?[a-zA-Z0-9-_@]{6}($|&|#)/', $value['real_url'])) { | ||
21 | continue; | ||
22 | } | ||
20 | $archive = sprintf($archive_html, $value['url']); | 23 | $archive = sprintf($archive_html, $value['url']); |
21 | $value['link_plugin'][] = $archive; | 24 | $value['link_plugin'][] = $archive; |
22 | } | 25 | } |
diff --git a/plugins/demo_plugin/custom_demo.css b/plugins/demo_plugin/custom_demo.css index af5e8bf9..95019e28 100644 --- a/plugins/demo_plugin/custom_demo.css +++ b/plugins/demo_plugin/custom_demo.css | |||
@@ -2,10 +2,6 @@ | |||
2 | color: red; | 2 | color: red; |
3 | } | 3 | } |
4 | 4 | ||
5 | .upper_plugin_demo { | ||
6 | float: left; | ||
7 | } | ||
8 | |||
9 | #demo_marquee { | 5 | #demo_marquee { |
10 | background: darkmagenta; | 6 | background: darkmagenta; |
11 | color: white; | 7 | color: white; |
diff --git a/plugins/demo_plugin/demo_plugin.php b/plugins/demo_plugin/demo_plugin.php index 18834e53..8fdbf663 100644 --- a/plugins/demo_plugin/demo_plugin.php +++ b/plugins/demo_plugin/demo_plugin.php | |||
@@ -15,6 +15,23 @@ | |||
15 | */ | 15 | */ |
16 | 16 | ||
17 | /** | 17 | /** |
18 | * Initialization function. | ||
19 | * It will be called when the plugin is loaded. | ||
20 | * This function can be used to return a list of initialization errors. | ||
21 | * | ||
22 | * @param $conf ConfigManager instance. | ||
23 | * | ||
24 | * @return array List of errors (optional). | ||
25 | */ | ||
26 | function demo_plugin_init($conf) | ||
27 | { | ||
28 | $conf->get('toto', 'nope'); | ||
29 | |||
30 | $errors[] = 'This a demo init error.'; | ||
31 | return $errors; | ||
32 | } | ||
33 | |||
34 | /** | ||
18 | * Hook render_header. | 35 | * Hook render_header. |
19 | * Executed on every page redering. | 36 | * Executed on every page redering. |
20 | * | 37 | * |
@@ -33,15 +50,68 @@ function hook_demo_plugin_render_header($data) | |||
33 | 50 | ||
34 | // If loggedin | 51 | // If loggedin |
35 | if ($data['_LOGGEDIN_'] === true) { | 52 | if ($data['_LOGGEDIN_'] === true) { |
36 | // Buttons in toolbar | 53 | /* |
37 | $data['buttons_toolbar'][] = '<li><a href="#">DEMO_buttons_toolbar</a></li>'; | 54 | * Links in toolbar: |
55 | * A link is an array of its attributes (key="value"), | ||
56 | * and a mandatory `html` key, which contains its value. | ||
57 | */ | ||
58 | $button = array( | ||
59 | 'attr' => array ( | ||
60 | 'href' => '#', | ||
61 | 'class' => 'mybutton', | ||
62 | 'title' => 'hover me', | ||
63 | ), | ||
64 | 'html' => 'DEMO buttons toolbar', | ||
65 | ); | ||
66 | $data['buttons_toolbar'][] = $button; | ||
38 | } | 67 | } |
39 | 68 | ||
40 | // Fields in toolbar | 69 | /* |
41 | $data['fields_toolbar'][] = 'DEMO_fields_toolbar'; | 70 | * Add additional input fields in the tools. |
71 | * A field is an array containing: | ||
72 | * [ | ||
73 | * 'form-attribute-1' => 'form attribute 1 value', | ||
74 | * 'form-attribute-2' => 'form attribute 2 value', | ||
75 | * 'inputs' => [ | ||
76 | * [ | ||
77 | * 'input-1-attribute-1 => 'input 1 attribute 1 value', | ||
78 | * 'input-1-attribute-2 => 'input 1 attribute 2 value', | ||
79 | * ], | ||
80 | * [ | ||
81 | * 'input-2-attribute-1 => 'input 2 attribute 1 value', | ||
82 | * ], | ||
83 | * ], | ||
84 | * ] | ||
85 | * This example renders as: | ||
86 | * <form form-attribute-1="form attribute 1 value" form-attribute-2="form attribute 2 value"> | ||
87 | * <input input-1-attribute-1="input 1 attribute 1 value" input-1-attribute-2="input 1 attribute 2 value"> | ||
88 | * <input input-2-attribute-1="input 2 attribute 1 value"> | ||
89 | * </form> | ||
90 | */ | ||
91 | $form = array( | ||
92 | 'attr' => array( | ||
93 | 'method' => 'GET', | ||
94 | 'action' => '?', | ||
95 | 'class' => 'addform', | ||
96 | ), | ||
97 | 'inputs' => array( | ||
98 | array( | ||
99 | 'type' => 'text', | ||
100 | 'name' => 'demo', | ||
101 | 'placeholder' => 'demo', | ||
102 | ) | ||
103 | ) | ||
104 | ); | ||
105 | $data['fields_toolbar'][] = $form; | ||
42 | } | 106 | } |
43 | // Another button always displayed | 107 | // Another button always displayed |
44 | $data['buttons_toolbar'][] = '<li><a href="#">DEMO</a></li>'; | 108 | $button = array( |
109 | 'attr' => array( | ||
110 | 'href' => '#', | ||
111 | ), | ||
112 | 'html' => 'Demo', | ||
113 | ); | ||
114 | $data['buttons_toolbar'][] = $button; | ||
45 | 115 | ||
46 | return $data; | 116 | return $data; |
47 | } | 117 | } |
@@ -126,8 +196,19 @@ function hook_demo_plugin_render_footer($data) | |||
126 | */ | 196 | */ |
127 | function hook_demo_plugin_render_linklist($data) | 197 | function hook_demo_plugin_render_linklist($data) |
128 | { | 198 | { |
129 | // action_plugin | 199 | /* |
130 | $data['action_plugin'][] = '<div class="upper_plugin_demo"><a href="?up" title="Uppercase!">←</a></div>'; | 200 | * Action links (action_plugin): |
201 | * A link is an array of its attributes (key="value"), | ||
202 | * and a mandatory `html` key, which contains its value. | ||
203 | * It's also recommended to add key 'on' or 'off' for theme rendering. | ||
204 | */ | ||
205 | $action = array( | ||
206 | 'attr' => array( | ||
207 | 'href' => '?up', | ||
208 | 'title' => 'Uppercase!', | ||
209 | ), | ||
210 | 'html' => '←', | ||
211 | ); | ||
131 | 212 | ||
132 | if (isset($_GET['up'])) { | 213 | if (isset($_GET['up'])) { |
133 | // Manipulate link data | 214 | // Manipulate link data |
@@ -135,7 +216,11 @@ function hook_demo_plugin_render_linklist($data) | |||
135 | $value['description'] = strtoupper($value['description']); | 216 | $value['description'] = strtoupper($value['description']); |
136 | $value['title'] = strtoupper($value['title']); | 217 | $value['title'] = strtoupper($value['title']); |
137 | } | 218 | } |
219 | $action['on'] = true; | ||
220 | } else { | ||
221 | $action['off'] = true; | ||
138 | } | 222 | } |
223 | $data['action_plugin'][] = $action; | ||
139 | 224 | ||
140 | // link_plugin (for each link) | 225 | // link_plugin (for each link) |
141 | foreach ($data['links'] as &$value) { | 226 | foreach ($data['links'] as &$value) { |
diff --git a/plugins/isso/isso.css b/plugins/isso/isso.css new file mode 100644 index 00000000..b89babc3 --- /dev/null +++ b/plugins/isso/isso.css | |||
@@ -0,0 +1,3 @@ | |||
1 | #isso-thread > h4 { | ||
2 | text-align: center; | ||
3 | } | ||
diff --git a/plugins/isso/isso.html b/plugins/isso/isso.html new file mode 100644 index 00000000..babab9b8 --- /dev/null +++ b/plugins/isso/isso.html | |||
@@ -0,0 +1,14 @@ | |||
1 | <script data-isso="//%s" | ||
2 | data-isso-css="true" | ||
3 | data-isso-lang="en" | ||
4 | data-isso-reply-to-self="true" | ||
5 | data-isso-max-comments-top="20" | ||
6 | data-isso-max-comments-nested="5" | ||
7 | data-isso-reveal-on-click="5" | ||
8 | data-isso-avatar="true" | ||
9 | data-isso-avatar-bg="#f0f0f0" | ||
10 | data-isso-avatar-fg="#9abf88 #5698c4 #e279a3 #9163b6 ..." | ||
11 | data-isso-vote="true" | ||
12 | src="//%s/js/embed.min.js"> | ||
13 | </script> | ||
14 | <section id="isso-thread" data-isso-id="%s" data-title="%s"></section> | ||
diff --git a/plugins/isso/isso.meta b/plugins/isso/isso.meta new file mode 100644 index 00000000..62473abc --- /dev/null +++ b/plugins/isso/isso.meta | |||
@@ -0,0 +1,3 @@ | |||
1 | description="Let visitor comment your shaares on permalinks with Isso." | ||
2 | parameters="ISSO_SERVER" | ||
3 | parameter.ISSO_SERVER="Isso server URL (without 'http://')" | ||
diff --git a/plugins/isso/isso.php b/plugins/isso/isso.php new file mode 100644 index 00000000..ce16645f --- /dev/null +++ b/plugins/isso/isso.php | |||
@@ -0,0 +1,54 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Plugin Isso. | ||
5 | */ | ||
6 | |||
7 | /** | ||
8 | * Display an error everywhere if the plugin is enabled without configuration. | ||
9 | * | ||
10 | * @param $data array List of links | ||
11 | * @param $conf ConfigManager instance | ||
12 | * | ||
13 | * @return mixed - linklist data with Isso plugin. | ||
14 | */ | ||
15 | function isso_init($conf) | ||
16 | { | ||
17 | $issoUrl = $conf->get('plugins.ISSO_SERVER'); | ||
18 | if (empty($issoUrl)) { | ||
19 | $error = 'Isso plugin error: '. | ||
20 | 'Please define the "ISSO_SERVER" setting in the plugin administration page.'; | ||
21 | return array($error); | ||
22 | } | ||
23 | } | ||
24 | |||
25 | /** | ||
26 | * Render linklist hook. | ||
27 | * Will only display Isso comments on permalinks. | ||
28 | * | ||
29 | * @param $data array List of links | ||
30 | * @param $conf ConfigManager instance | ||
31 | * | ||
32 | * @return mixed - linklist data with Isso plugin. | ||
33 | */ | ||
34 | function hook_isso_render_linklist($data, $conf) | ||
35 | { | ||
36 | $issoUrl = $conf->get('plugins.ISSO_SERVER'); | ||
37 | if (empty($issoUrl)) { | ||
38 | return $data; | ||
39 | } | ||
40 | |||
41 | // Only display comments for permalinks. | ||
42 | if (count($data['links']) == 1 && empty($data['search_tags']) && empty($data['search_term'])) { | ||
43 | $link = reset($data['links']); | ||
44 | $issoHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/isso/isso.html'); | ||
45 | |||
46 | $isso = sprintf($issoHtml, $issoUrl, $issoUrl, $link['id'], $link['id']); | ||
47 | $data['plugin_end_zone'][] = $isso; | ||
48 | |||
49 | // Hackish way to include this CSS file only when necessary. | ||
50 | $data['plugins_includes']['css_files'][] = PluginManager::$PLUGINS_PATH . '/isso/isso.css'; | ||
51 | } | ||
52 | |||
53 | return $data; | ||
54 | } | ||
diff --git a/plugins/markdown/Parsedown.php b/plugins/markdown/Parsedown.php deleted file mode 100644 index 91e05dcc..00000000 --- a/plugins/markdown/Parsedown.php +++ /dev/null | |||
@@ -1,1528 +0,0 @@ | |||
1 | <?php | ||
2 | |||
3 | # | ||
4 | # | ||
5 | # Parsedown | ||
6 | # http://parsedown.org | ||
7 | # | ||
8 | # (c) Emanuil Rusev | ||
9 | # http://erusev.com | ||
10 | # | ||
11 | # For the full license information, view the LICENSE file that was distributed | ||
12 | # with this source code. | ||
13 | # | ||
14 | # | ||
15 | |||
16 | class Parsedown | ||
17 | { | ||
18 | # ~ | ||
19 | |||
20 | const version = '1.6.0'; | ||
21 | |||
22 | # ~ | ||
23 | |||
24 | function text($text) | ||
25 | { | ||
26 | # make sure no definitions are set | ||
27 | $this->DefinitionData = array(); | ||
28 | |||
29 | # standardize line breaks | ||
30 | $text = str_replace(array("\r\n", "\r"), "\n", $text); | ||
31 | |||
32 | # remove surrounding line breaks | ||
33 | $text = trim($text, "\n"); | ||
34 | |||
35 | # split text into lines | ||
36 | $lines = explode("\n", $text); | ||
37 | |||
38 | # iterate through lines to identify blocks | ||
39 | $markup = $this->lines($lines); | ||
40 | |||
41 | # trim line breaks | ||
42 | $markup = trim($markup, "\n"); | ||
43 | |||
44 | return $markup; | ||
45 | } | ||
46 | |||
47 | # | ||
48 | # Setters | ||
49 | # | ||
50 | |||
51 | function setBreaksEnabled($breaksEnabled) | ||
52 | { | ||
53 | $this->breaksEnabled = $breaksEnabled; | ||
54 | |||
55 | return $this; | ||
56 | } | ||
57 | |||
58 | protected $breaksEnabled; | ||
59 | |||
60 | function setMarkupEscaped($markupEscaped) | ||
61 | { | ||
62 | $this->markupEscaped = $markupEscaped; | ||
63 | |||
64 | return $this; | ||
65 | } | ||
66 | |||
67 | protected $markupEscaped; | ||
68 | |||
69 | function setUrlsLinked($urlsLinked) | ||
70 | { | ||
71 | $this->urlsLinked = $urlsLinked; | ||
72 | |||
73 | return $this; | ||
74 | } | ||
75 | |||
76 | protected $urlsLinked = true; | ||
77 | |||
78 | # | ||
79 | # Lines | ||
80 | # | ||
81 | |||
82 | protected $BlockTypes = array( | ||
83 | '#' => array('Header'), | ||
84 | '*' => array('Rule', 'List'), | ||
85 | '+' => array('List'), | ||
86 | '-' => array('SetextHeader', 'Table', 'Rule', 'List'), | ||
87 | '0' => array('List'), | ||
88 | '1' => array('List'), | ||
89 | '2' => array('List'), | ||
90 | '3' => array('List'), | ||
91 | '4' => array('List'), | ||
92 | '5' => array('List'), | ||
93 | '6' => array('List'), | ||
94 | '7' => array('List'), | ||
95 | '8' => array('List'), | ||
96 | '9' => array('List'), | ||
97 | ':' => array('Table'), | ||
98 | '<' => array('Comment', 'Markup'), | ||
99 | '=' => array('SetextHeader'), | ||
100 | '>' => array('Quote'), | ||
101 | '[' => array('Reference'), | ||
102 | '_' => array('Rule'), | ||
103 | '`' => array('FencedCode'), | ||
104 | '|' => array('Table'), | ||
105 | '~' => array('FencedCode'), | ||
106 | ); | ||
107 | |||
108 | # ~ | ||
109 | |||
110 | protected $unmarkedBlockTypes = array( | ||
111 | 'Code', | ||
112 | ); | ||
113 | |||
114 | # | ||
115 | # Blocks | ||
116 | # | ||
117 | |||
118 | private function lines(array $lines) | ||
119 | { | ||
120 | $CurrentBlock = null; | ||
121 | |||
122 | foreach ($lines as $line) | ||
123 | { | ||
124 | if (chop($line) === '') | ||
125 | { | ||
126 | if (isset($CurrentBlock)) | ||
127 | { | ||
128 | $CurrentBlock['interrupted'] = true; | ||
129 | } | ||
130 | |||
131 | continue; | ||
132 | } | ||
133 | |||
134 | if (strpos($line, "\t") !== false) | ||
135 | { | ||
136 | $parts = explode("\t", $line); | ||
137 | |||
138 | $line = $parts[0]; | ||
139 | |||
140 | unset($parts[0]); | ||
141 | |||
142 | foreach ($parts as $part) | ||
143 | { | ||
144 | $shortage = 4 - mb_strlen($line, 'utf-8') % 4; | ||
145 | |||
146 | $line .= str_repeat(' ', $shortage); | ||
147 | $line .= $part; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | $indent = 0; | ||
152 | |||
153 | while (isset($line[$indent]) and $line[$indent] === ' ') | ||
154 | { | ||
155 | $indent ++; | ||
156 | } | ||
157 | |||
158 | $text = $indent > 0 ? substr($line, $indent) : $line; | ||
159 | |||
160 | # ~ | ||
161 | |||
162 | $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); | ||
163 | |||
164 | # ~ | ||
165 | |||
166 | if (isset($CurrentBlock['continuable'])) | ||
167 | { | ||
168 | $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); | ||
169 | |||
170 | if (isset($Block)) | ||
171 | { | ||
172 | $CurrentBlock = $Block; | ||
173 | |||
174 | continue; | ||
175 | } | ||
176 | else | ||
177 | { | ||
178 | if (method_exists($this, 'block'.$CurrentBlock['type'].'Complete')) | ||
179 | { | ||
180 | $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | |||
185 | # ~ | ||
186 | |||
187 | $marker = $text[0]; | ||
188 | |||
189 | # ~ | ||
190 | |||
191 | $blockTypes = $this->unmarkedBlockTypes; | ||
192 | |||
193 | if (isset($this->BlockTypes[$marker])) | ||
194 | { | ||
195 | foreach ($this->BlockTypes[$marker] as $blockType) | ||
196 | { | ||
197 | $blockTypes []= $blockType; | ||
198 | } | ||
199 | } | ||
200 | |||
201 | # | ||
202 | # ~ | ||
203 | |||
204 | foreach ($blockTypes as $blockType) | ||
205 | { | ||
206 | $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); | ||
207 | |||
208 | if (isset($Block)) | ||
209 | { | ||
210 | $Block['type'] = $blockType; | ||
211 | |||
212 | if ( ! isset($Block['identified'])) | ||
213 | { | ||
214 | $Blocks []= $CurrentBlock; | ||
215 | |||
216 | $Block['identified'] = true; | ||
217 | } | ||
218 | |||
219 | if (method_exists($this, 'block'.$blockType.'Continue')) | ||
220 | { | ||
221 | $Block['continuable'] = true; | ||
222 | } | ||
223 | |||
224 | $CurrentBlock = $Block; | ||
225 | |||
226 | continue 2; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | # ~ | ||
231 | |||
232 | if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted'])) | ||
233 | { | ||
234 | $CurrentBlock['element']['text'] .= "\n".$text; | ||
235 | } | ||
236 | else | ||
237 | { | ||
238 | $Blocks []= $CurrentBlock; | ||
239 | |||
240 | $CurrentBlock = $this->paragraph($Line); | ||
241 | |||
242 | $CurrentBlock['identified'] = true; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | # ~ | ||
247 | |||
248 | if (isset($CurrentBlock['continuable']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete')) | ||
249 | { | ||
250 | $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); | ||
251 | } | ||
252 | |||
253 | # ~ | ||
254 | |||
255 | $Blocks []= $CurrentBlock; | ||
256 | |||
257 | unset($Blocks[0]); | ||
258 | |||
259 | # ~ | ||
260 | |||
261 | $markup = ''; | ||
262 | |||
263 | foreach ($Blocks as $Block) | ||
264 | { | ||
265 | if (isset($Block['hidden'])) | ||
266 | { | ||
267 | continue; | ||
268 | } | ||
269 | |||
270 | $markup .= "\n"; | ||
271 | $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']); | ||
272 | } | ||
273 | |||
274 | $markup .= "\n"; | ||
275 | |||
276 | # ~ | ||
277 | |||
278 | return $markup; | ||
279 | } | ||
280 | |||
281 | # | ||
282 | # Code | ||
283 | |||
284 | protected function blockCode($Line, $Block = null) | ||
285 | { | ||
286 | if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted'])) | ||
287 | { | ||
288 | return; | ||
289 | } | ||
290 | |||
291 | if ($Line['indent'] >= 4) | ||
292 | { | ||
293 | $text = substr($Line['body'], 4); | ||
294 | |||
295 | $Block = array( | ||
296 | 'element' => array( | ||
297 | 'name' => 'pre', | ||
298 | 'handler' => 'element', | ||
299 | 'text' => array( | ||
300 | 'name' => 'code', | ||
301 | 'text' => $text, | ||
302 | ), | ||
303 | ), | ||
304 | ); | ||
305 | |||
306 | return $Block; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | protected function blockCodeContinue($Line, $Block) | ||
311 | { | ||
312 | if ($Line['indent'] >= 4) | ||
313 | { | ||
314 | if (isset($Block['interrupted'])) | ||
315 | { | ||
316 | $Block['element']['text']['text'] .= "\n"; | ||
317 | |||
318 | unset($Block['interrupted']); | ||
319 | } | ||
320 | |||
321 | $Block['element']['text']['text'] .= "\n"; | ||
322 | |||
323 | $text = substr($Line['body'], 4); | ||
324 | |||
325 | $Block['element']['text']['text'] .= $text; | ||
326 | |||
327 | return $Block; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | protected function blockCodeComplete($Block) | ||
332 | { | ||
333 | $text = $Block['element']['text']['text']; | ||
334 | |||
335 | $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); | ||
336 | |||
337 | $Block['element']['text']['text'] = $text; | ||
338 | |||
339 | return $Block; | ||
340 | } | ||
341 | |||
342 | # | ||
343 | # Comment | ||
344 | |||
345 | protected function blockComment($Line) | ||
346 | { | ||
347 | if ($this->markupEscaped) | ||
348 | { | ||
349 | return; | ||
350 | } | ||
351 | |||
352 | if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') | ||
353 | { | ||
354 | $Block = array( | ||
355 | 'markup' => $Line['body'], | ||
356 | ); | ||
357 | |||
358 | if (preg_match('/-->$/', $Line['text'])) | ||
359 | { | ||
360 | $Block['closed'] = true; | ||
361 | } | ||
362 | |||
363 | return $Block; | ||
364 | } | ||
365 | } | ||
366 | |||
367 | protected function blockCommentContinue($Line, array $Block) | ||
368 | { | ||
369 | if (isset($Block['closed'])) | ||
370 | { | ||
371 | return; | ||
372 | } | ||
373 | |||
374 | $Block['markup'] .= "\n" . $Line['body']; | ||
375 | |||
376 | if (preg_match('/-->$/', $Line['text'])) | ||
377 | { | ||
378 | $Block['closed'] = true; | ||
379 | } | ||
380 | |||
381 | return $Block; | ||
382 | } | ||
383 | |||
384 | # | ||
385 | # Fenced Code | ||
386 | |||
387 | protected function blockFencedCode($Line) | ||
388 | { | ||
389 | if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches)) | ||
390 | { | ||
391 | $Element = array( | ||
392 | 'name' => 'code', | ||
393 | 'text' => '', | ||
394 | ); | ||
395 | |||
396 | if (isset($matches[1])) | ||
397 | { | ||
398 | $class = 'language-'.$matches[1]; | ||
399 | |||
400 | $Element['attributes'] = array( | ||
401 | 'class' => $class, | ||
402 | ); | ||
403 | } | ||
404 | |||
405 | $Block = array( | ||
406 | 'char' => $Line['text'][0], | ||
407 | 'element' => array( | ||
408 | 'name' => 'pre', | ||
409 | 'handler' => 'element', | ||
410 | 'text' => $Element, | ||
411 | ), | ||
412 | ); | ||
413 | |||
414 | return $Block; | ||
415 | } | ||
416 | } | ||
417 | |||
418 | protected function blockFencedCodeContinue($Line, $Block) | ||
419 | { | ||
420 | if (isset($Block['complete'])) | ||
421 | { | ||
422 | return; | ||
423 | } | ||
424 | |||
425 | if (isset($Block['interrupted'])) | ||
426 | { | ||
427 | $Block['element']['text']['text'] .= "\n"; | ||
428 | |||
429 | unset($Block['interrupted']); | ||
430 | } | ||
431 | |||
432 | if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text'])) | ||
433 | { | ||
434 | $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1); | ||
435 | |||
436 | $Block['complete'] = true; | ||
437 | |||
438 | return $Block; | ||
439 | } | ||
440 | |||
441 | $Block['element']['text']['text'] .= "\n".$Line['body'];; | ||
442 | |||
443 | return $Block; | ||
444 | } | ||
445 | |||
446 | protected function blockFencedCodeComplete($Block) | ||
447 | { | ||
448 | $text = $Block['element']['text']['text']; | ||
449 | |||
450 | $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); | ||
451 | |||
452 | $Block['element']['text']['text'] = $text; | ||
453 | |||
454 | return $Block; | ||
455 | } | ||
456 | |||
457 | # | ||
458 | # Header | ||
459 | |||
460 | protected function blockHeader($Line) | ||
461 | { | ||
462 | if (isset($Line['text'][1])) | ||
463 | { | ||
464 | $level = 1; | ||
465 | |||
466 | while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') | ||
467 | { | ||
468 | $level ++; | ||
469 | } | ||
470 | |||
471 | if ($level > 6) | ||
472 | { | ||
473 | return; | ||
474 | } | ||
475 | |||
476 | $text = trim($Line['text'], '# '); | ||
477 | |||
478 | $Block = array( | ||
479 | 'element' => array( | ||
480 | 'name' => 'h' . min(6, $level), | ||
481 | 'text' => $text, | ||
482 | 'handler' => 'line', | ||
483 | ), | ||
484 | ); | ||
485 | |||
486 | return $Block; | ||
487 | } | ||
488 | } | ||
489 | |||
490 | # | ||
491 | # List | ||
492 | |||
493 | protected function blockList($Line) | ||
494 | { | ||
495 | list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]'); | ||
496 | |||
497 | if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches)) | ||
498 | { | ||
499 | $Block = array( | ||
500 | 'indent' => $Line['indent'], | ||
501 | 'pattern' => $pattern, | ||
502 | 'element' => array( | ||
503 | 'name' => $name, | ||
504 | 'handler' => 'elements', | ||
505 | ), | ||
506 | ); | ||
507 | |||
508 | $Block['li'] = array( | ||
509 | 'name' => 'li', | ||
510 | 'handler' => 'li', | ||
511 | 'text' => array( | ||
512 | $matches[2], | ||
513 | ), | ||
514 | ); | ||
515 | |||
516 | $Block['element']['text'] []= & $Block['li']; | ||
517 | |||
518 | return $Block; | ||
519 | } | ||
520 | } | ||
521 | |||
522 | protected function blockListContinue($Line, array $Block) | ||
523 | { | ||
524 | if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches)) | ||
525 | { | ||
526 | if (isset($Block['interrupted'])) | ||
527 | { | ||
528 | $Block['li']['text'] []= ''; | ||
529 | |||
530 | unset($Block['interrupted']); | ||
531 | } | ||
532 | |||
533 | unset($Block['li']); | ||
534 | |||
535 | $text = isset($matches[1]) ? $matches[1] : ''; | ||
536 | |||
537 | $Block['li'] = array( | ||
538 | 'name' => 'li', | ||
539 | 'handler' => 'li', | ||
540 | 'text' => array( | ||
541 | $text, | ||
542 | ), | ||
543 | ); | ||
544 | |||
545 | $Block['element']['text'] []= & $Block['li']; | ||
546 | |||
547 | return $Block; | ||
548 | } | ||
549 | |||
550 | if ($Line['text'][0] === '[' and $this->blockReference($Line)) | ||
551 | { | ||
552 | return $Block; | ||
553 | } | ||
554 | |||
555 | if ( ! isset($Block['interrupted'])) | ||
556 | { | ||
557 | $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); | ||
558 | |||
559 | $Block['li']['text'] []= $text; | ||
560 | |||
561 | return $Block; | ||
562 | } | ||
563 | |||
564 | if ($Line['indent'] > 0) | ||
565 | { | ||
566 | $Block['li']['text'] []= ''; | ||
567 | |||
568 | $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); | ||
569 | |||
570 | $Block['li']['text'] []= $text; | ||
571 | |||
572 | unset($Block['interrupted']); | ||
573 | |||
574 | return $Block; | ||
575 | } | ||
576 | } | ||
577 | |||
578 | # | ||
579 | # Quote | ||
580 | |||
581 | protected function blockQuote($Line) | ||
582 | { | ||
583 | if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) | ||
584 | { | ||
585 | $Block = array( | ||
586 | 'element' => array( | ||
587 | 'name' => 'blockquote', | ||
588 | 'handler' => 'lines', | ||
589 | 'text' => (array) $matches[1], | ||
590 | ), | ||
591 | ); | ||
592 | |||
593 | return $Block; | ||
594 | } | ||
595 | } | ||
596 | |||
597 | protected function blockQuoteContinue($Line, array $Block) | ||
598 | { | ||
599 | if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) | ||
600 | { | ||
601 | if (isset($Block['interrupted'])) | ||
602 | { | ||
603 | $Block['element']['text'] []= ''; | ||
604 | |||
605 | unset($Block['interrupted']); | ||
606 | } | ||
607 | |||
608 | $Block['element']['text'] []= $matches[1]; | ||
609 | |||
610 | return $Block; | ||
611 | } | ||
612 | |||
613 | if ( ! isset($Block['interrupted'])) | ||
614 | { | ||
615 | $Block['element']['text'] []= $Line['text']; | ||
616 | |||
617 | return $Block; | ||
618 | } | ||
619 | } | ||
620 | |||
621 | # | ||
622 | # Rule | ||
623 | |||
624 | protected function blockRule($Line) | ||
625 | { | ||
626 | if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text'])) | ||
627 | { | ||
628 | $Block = array( | ||
629 | 'element' => array( | ||
630 | 'name' => 'hr' | ||
631 | ), | ||
632 | ); | ||
633 | |||
634 | return $Block; | ||
635 | } | ||
636 | } | ||
637 | |||
638 | # | ||
639 | # Setext | ||
640 | |||
641 | protected function blockSetextHeader($Line, array $Block = null) | ||
642 | { | ||
643 | if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) | ||
644 | { | ||
645 | return; | ||
646 | } | ||
647 | |||
648 | if (chop($Line['text'], $Line['text'][0]) === '') | ||
649 | { | ||
650 | $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; | ||
651 | |||
652 | return $Block; | ||
653 | } | ||
654 | } | ||
655 | |||
656 | # | ||
657 | # Markup | ||
658 | |||
659 | protected function blockMarkup($Line) | ||
660 | { | ||
661 | if ($this->markupEscaped) | ||
662 | { | ||
663 | return; | ||
664 | } | ||
665 | |||
666 | if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) | ||
667 | { | ||
668 | $element = strtolower($matches[1]); | ||
669 | |||
670 | if (in_array($element, $this->textLevelElements)) | ||
671 | { | ||
672 | return; | ||
673 | } | ||
674 | |||
675 | $Block = array( | ||
676 | 'name' => $matches[1], | ||
677 | 'depth' => 0, | ||
678 | 'markup' => $Line['text'], | ||
679 | ); | ||
680 | |||
681 | $length = strlen($matches[0]); | ||
682 | |||
683 | $remainder = substr($Line['text'], $length); | ||
684 | |||
685 | if (trim($remainder) === '') | ||
686 | { | ||
687 | if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) | ||
688 | { | ||
689 | $Block['closed'] = true; | ||
690 | |||
691 | $Block['void'] = true; | ||
692 | } | ||
693 | } | ||
694 | else | ||
695 | { | ||
696 | if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) | ||
697 | { | ||
698 | return; | ||
699 | } | ||
700 | |||
701 | if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder)) | ||
702 | { | ||
703 | $Block['closed'] = true; | ||
704 | } | ||
705 | } | ||
706 | |||
707 | return $Block; | ||
708 | } | ||
709 | } | ||
710 | |||
711 | protected function blockMarkupContinue($Line, array $Block) | ||
712 | { | ||
713 | if (isset($Block['closed'])) | ||
714 | { | ||
715 | return; | ||
716 | } | ||
717 | |||
718 | if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open | ||
719 | { | ||
720 | $Block['depth'] ++; | ||
721 | } | ||
722 | |||
723 | if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close | ||
724 | { | ||
725 | if ($Block['depth'] > 0) | ||
726 | { | ||
727 | $Block['depth'] --; | ||
728 | } | ||
729 | else | ||
730 | { | ||
731 | $Block['closed'] = true; | ||
732 | } | ||
733 | } | ||
734 | |||
735 | if (isset($Block['interrupted'])) | ||
736 | { | ||
737 | $Block['markup'] .= "\n"; | ||
738 | |||
739 | unset($Block['interrupted']); | ||
740 | } | ||
741 | |||
742 | $Block['markup'] .= "\n".$Line['body']; | ||
743 | |||
744 | return $Block; | ||
745 | } | ||
746 | |||
747 | # | ||
748 | # Reference | ||
749 | |||
750 | protected function blockReference($Line) | ||
751 | { | ||
752 | if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) | ||
753 | { | ||
754 | $id = strtolower($matches[1]); | ||
755 | |||
756 | $Data = array( | ||
757 | 'url' => $matches[2], | ||
758 | 'title' => null, | ||
759 | ); | ||
760 | |||
761 | if (isset($matches[3])) | ||
762 | { | ||
763 | $Data['title'] = $matches[3]; | ||
764 | } | ||
765 | |||
766 | $this->DefinitionData['Reference'][$id] = $Data; | ||
767 | |||
768 | $Block = array( | ||
769 | 'hidden' => true, | ||
770 | ); | ||
771 | |||
772 | return $Block; | ||
773 | } | ||
774 | } | ||
775 | |||
776 | # | ||
777 | # Table | ||
778 | |||
779 | protected function blockTable($Line, array $Block = null) | ||
780 | { | ||
781 | if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) | ||
782 | { | ||
783 | return; | ||
784 | } | ||
785 | |||
786 | if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '') | ||
787 | { | ||
788 | $alignments = array(); | ||
789 | |||
790 | $divider = $Line['text']; | ||
791 | |||
792 | $divider = trim($divider); | ||
793 | $divider = trim($divider, '|'); | ||
794 | |||
795 | $dividerCells = explode('|', $divider); | ||
796 | |||
797 | foreach ($dividerCells as $dividerCell) | ||
798 | { | ||
799 | $dividerCell = trim($dividerCell); | ||
800 | |||
801 | if ($dividerCell === '') | ||
802 | { | ||
803 | continue; | ||
804 | } | ||
805 | |||
806 | $alignment = null; | ||
807 | |||
808 | if ($dividerCell[0] === ':') | ||
809 | { | ||
810 | $alignment = 'left'; | ||
811 | } | ||
812 | |||
813 | if (substr($dividerCell, - 1) === ':') | ||
814 | { | ||
815 | $alignment = $alignment === 'left' ? 'center' : 'right'; | ||
816 | } | ||
817 | |||
818 | $alignments []= $alignment; | ||
819 | } | ||
820 | |||
821 | # ~ | ||
822 | |||
823 | $HeaderElements = array(); | ||
824 | |||
825 | $header = $Block['element']['text']; | ||
826 | |||
827 | $header = trim($header); | ||
828 | $header = trim($header, '|'); | ||
829 | |||
830 | $headerCells = explode('|', $header); | ||
831 | |||
832 | foreach ($headerCells as $index => $headerCell) | ||
833 | { | ||
834 | $headerCell = trim($headerCell); | ||
835 | |||
836 | $HeaderElement = array( | ||
837 | 'name' => 'th', | ||
838 | 'text' => $headerCell, | ||
839 | 'handler' => 'line', | ||
840 | ); | ||
841 | |||
842 | if (isset($alignments[$index])) | ||
843 | { | ||
844 | $alignment = $alignments[$index]; | ||
845 | |||
846 | $HeaderElement['attributes'] = array( | ||
847 | 'style' => 'text-align: '.$alignment.';', | ||
848 | ); | ||
849 | } | ||
850 | |||
851 | $HeaderElements []= $HeaderElement; | ||
852 | } | ||
853 | |||
854 | # ~ | ||
855 | |||
856 | $Block = array( | ||
857 | 'alignments' => $alignments, | ||
858 | 'identified' => true, | ||
859 | 'element' => array( | ||
860 | 'name' => 'table', | ||
861 | 'handler' => 'elements', | ||
862 | ), | ||
863 | ); | ||
864 | |||
865 | $Block['element']['text'] []= array( | ||
866 | 'name' => 'thead', | ||
867 | 'handler' => 'elements', | ||
868 | ); | ||
869 | |||
870 | $Block['element']['text'] []= array( | ||
871 | 'name' => 'tbody', | ||
872 | 'handler' => 'elements', | ||
873 | 'text' => array(), | ||
874 | ); | ||
875 | |||
876 | $Block['element']['text'][0]['text'] []= array( | ||
877 | 'name' => 'tr', | ||
878 | 'handler' => 'elements', | ||
879 | 'text' => $HeaderElements, | ||
880 | ); | ||
881 | |||
882 | return $Block; | ||
883 | } | ||
884 | } | ||
885 | |||
886 | protected function blockTableContinue($Line, array $Block) | ||
887 | { | ||
888 | if (isset($Block['interrupted'])) | ||
889 | { | ||
890 | return; | ||
891 | } | ||
892 | |||
893 | if ($Line['text'][0] === '|' or strpos($Line['text'], '|')) | ||
894 | { | ||
895 | $Elements = array(); | ||
896 | |||
897 | $row = $Line['text']; | ||
898 | |||
899 | $row = trim($row); | ||
900 | $row = trim($row, '|'); | ||
901 | |||
902 | preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches); | ||
903 | |||
904 | foreach ($matches[0] as $index => $cell) | ||
905 | { | ||
906 | $cell = trim($cell); | ||
907 | |||
908 | $Element = array( | ||
909 | 'name' => 'td', | ||
910 | 'handler' => 'line', | ||
911 | 'text' => $cell, | ||
912 | ); | ||
913 | |||
914 | if (isset($Block['alignments'][$index])) | ||
915 | { | ||
916 | $Element['attributes'] = array( | ||
917 | 'style' => 'text-align: '.$Block['alignments'][$index].';', | ||
918 | ); | ||
919 | } | ||
920 | |||
921 | $Elements []= $Element; | ||
922 | } | ||
923 | |||
924 | $Element = array( | ||
925 | 'name' => 'tr', | ||
926 | 'handler' => 'elements', | ||
927 | 'text' => $Elements, | ||
928 | ); | ||
929 | |||
930 | $Block['element']['text'][1]['text'] []= $Element; | ||
931 | |||
932 | return $Block; | ||
933 | } | ||
934 | } | ||
935 | |||
936 | # | ||
937 | # ~ | ||
938 | # | ||
939 | |||
940 | protected function paragraph($Line) | ||
941 | { | ||
942 | $Block = array( | ||
943 | 'element' => array( | ||
944 | 'name' => 'p', | ||
945 | 'text' => $Line['text'], | ||
946 | 'handler' => 'line', | ||
947 | ), | ||
948 | ); | ||
949 | |||
950 | return $Block; | ||
951 | } | ||
952 | |||
953 | # | ||
954 | # Inline Elements | ||
955 | # | ||
956 | |||
957 | protected $InlineTypes = array( | ||
958 | '"' => array('SpecialCharacter'), | ||
959 | '!' => array('Image'), | ||
960 | '&' => array('SpecialCharacter'), | ||
961 | '*' => array('Emphasis'), | ||
962 | ':' => array('Url'), | ||
963 | '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'), | ||
964 | '>' => array('SpecialCharacter'), | ||
965 | '[' => array('Link'), | ||
966 | '_' => array('Emphasis'), | ||
967 | '`' => array('Code'), | ||
968 | '~' => array('Strikethrough'), | ||
969 | '\\' => array('EscapeSequence'), | ||
970 | ); | ||
971 | |||
972 | # ~ | ||
973 | |||
974 | protected $inlineMarkerList = '!"*_&[:<>`~\\'; | ||
975 | |||
976 | # | ||
977 | # ~ | ||
978 | # | ||
979 | |||
980 | public function line($text) | ||
981 | { | ||
982 | $markup = ''; | ||
983 | |||
984 | # $excerpt is based on the first occurrence of a marker | ||
985 | |||
986 | while ($excerpt = strpbrk($text, $this->inlineMarkerList)) | ||
987 | { | ||
988 | $marker = $excerpt[0]; | ||
989 | |||
990 | $markerPosition = strpos($text, $marker); | ||
991 | |||
992 | $Excerpt = array('text' => $excerpt, 'context' => $text); | ||
993 | |||
994 | foreach ($this->InlineTypes[$marker] as $inlineType) | ||
995 | { | ||
996 | $Inline = $this->{'inline'.$inlineType}($Excerpt); | ||
997 | |||
998 | if ( ! isset($Inline)) | ||
999 | { | ||
1000 | continue; | ||
1001 | } | ||
1002 | |||
1003 | # makes sure that the inline belongs to "our" marker | ||
1004 | |||
1005 | if (isset($Inline['position']) and $Inline['position'] > $markerPosition) | ||
1006 | { | ||
1007 | continue; | ||
1008 | } | ||
1009 | |||
1010 | # sets a default inline position | ||
1011 | |||
1012 | if ( ! isset($Inline['position'])) | ||
1013 | { | ||
1014 | $Inline['position'] = $markerPosition; | ||
1015 | } | ||
1016 | |||
1017 | # the text that comes before the inline | ||
1018 | $unmarkedText = substr($text, 0, $Inline['position']); | ||
1019 | |||
1020 | # compile the unmarked text | ||
1021 | $markup .= $this->unmarkedText($unmarkedText); | ||
1022 | |||
1023 | # compile the inline | ||
1024 | $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']); | ||
1025 | |||
1026 | # remove the examined text | ||
1027 | $text = substr($text, $Inline['position'] + $Inline['extent']); | ||
1028 | |||
1029 | continue 2; | ||
1030 | } | ||
1031 | |||
1032 | # the marker does not belong to an inline | ||
1033 | |||
1034 | $unmarkedText = substr($text, 0, $markerPosition + 1); | ||
1035 | |||
1036 | $markup .= $this->unmarkedText($unmarkedText); | ||
1037 | |||
1038 | $text = substr($text, $markerPosition + 1); | ||
1039 | } | ||
1040 | |||
1041 | $markup .= $this->unmarkedText($text); | ||
1042 | |||
1043 | return $markup; | ||
1044 | } | ||
1045 | |||
1046 | # | ||
1047 | # ~ | ||
1048 | # | ||
1049 | |||
1050 | protected function inlineCode($Excerpt) | ||
1051 | { | ||
1052 | $marker = $Excerpt['text'][0]; | ||
1053 | |||
1054 | if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches)) | ||
1055 | { | ||
1056 | $text = $matches[2]; | ||
1057 | $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); | ||
1058 | $text = preg_replace("/[ ]*\n/", ' ', $text); | ||
1059 | |||
1060 | return array( | ||
1061 | 'extent' => strlen($matches[0]), | ||
1062 | 'element' => array( | ||
1063 | 'name' => 'code', | ||
1064 | 'text' => $text, | ||
1065 | ), | ||
1066 | ); | ||
1067 | } | ||
1068 | } | ||
1069 | |||
1070 | protected function inlineEmailTag($Excerpt) | ||
1071 | { | ||
1072 | if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches)) | ||
1073 | { | ||
1074 | $url = $matches[1]; | ||
1075 | |||
1076 | if ( ! isset($matches[2])) | ||
1077 | { | ||
1078 | $url = 'mailto:' . $url; | ||
1079 | } | ||
1080 | |||
1081 | return array( | ||
1082 | 'extent' => strlen($matches[0]), | ||
1083 | 'element' => array( | ||
1084 | 'name' => 'a', | ||
1085 | 'text' => $matches[1], | ||
1086 | 'attributes' => array( | ||
1087 | 'href' => $url, | ||
1088 | ), | ||
1089 | ), | ||
1090 | ); | ||
1091 | } | ||
1092 | } | ||
1093 | |||
1094 | protected function inlineEmphasis($Excerpt) | ||
1095 | { | ||
1096 | if ( ! isset($Excerpt['text'][1])) | ||
1097 | { | ||
1098 | return; | ||
1099 | } | ||
1100 | |||
1101 | $marker = $Excerpt['text'][0]; | ||
1102 | |||
1103 | if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) | ||
1104 | { | ||
1105 | $emphasis = 'strong'; | ||
1106 | } | ||
1107 | elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) | ||
1108 | { | ||
1109 | $emphasis = 'em'; | ||
1110 | } | ||
1111 | else | ||
1112 | { | ||
1113 | return; | ||
1114 | } | ||
1115 | |||
1116 | return array( | ||
1117 | 'extent' => strlen($matches[0]), | ||
1118 | 'element' => array( | ||
1119 | 'name' => $emphasis, | ||
1120 | 'handler' => 'line', | ||
1121 | 'text' => $matches[1], | ||
1122 | ), | ||
1123 | ); | ||
1124 | } | ||
1125 | |||
1126 | protected function inlineEscapeSequence($Excerpt) | ||
1127 | { | ||
1128 | if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) | ||
1129 | { | ||
1130 | return array( | ||
1131 | 'markup' => $Excerpt['text'][1], | ||
1132 | 'extent' => 2, | ||
1133 | ); | ||
1134 | } | ||
1135 | } | ||
1136 | |||
1137 | protected function inlineImage($Excerpt) | ||
1138 | { | ||
1139 | if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') | ||
1140 | { | ||
1141 | return; | ||
1142 | } | ||
1143 | |||
1144 | $Excerpt['text']= substr($Excerpt['text'], 1); | ||
1145 | |||
1146 | $Link = $this->inlineLink($Excerpt); | ||
1147 | |||
1148 | if ($Link === null) | ||
1149 | { | ||
1150 | return; | ||
1151 | } | ||
1152 | |||
1153 | $Inline = array( | ||
1154 | 'extent' => $Link['extent'] + 1, | ||
1155 | 'element' => array( | ||
1156 | 'name' => 'img', | ||
1157 | 'attributes' => array( | ||
1158 | 'src' => $Link['element']['attributes']['href'], | ||
1159 | 'alt' => $Link['element']['text'], | ||
1160 | ), | ||
1161 | ), | ||
1162 | ); | ||
1163 | |||
1164 | $Inline['element']['attributes'] += $Link['element']['attributes']; | ||
1165 | |||
1166 | unset($Inline['element']['attributes']['href']); | ||
1167 | |||
1168 | return $Inline; | ||
1169 | } | ||
1170 | |||
1171 | protected function inlineLink($Excerpt) | ||
1172 | { | ||
1173 | $Element = array( | ||
1174 | 'name' => 'a', | ||
1175 | 'handler' => 'line', | ||
1176 | 'text' => null, | ||
1177 | 'attributes' => array( | ||
1178 | 'href' => null, | ||
1179 | 'title' => null, | ||
1180 | ), | ||
1181 | ); | ||
1182 | |||
1183 | $extent = 0; | ||
1184 | |||
1185 | $remainder = $Excerpt['text']; | ||
1186 | |||
1187 | if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches)) | ||
1188 | { | ||
1189 | $Element['text'] = $matches[1]; | ||
1190 | |||
1191 | $extent += strlen($matches[0]); | ||
1192 | |||
1193 | $remainder = substr($remainder, $extent); | ||
1194 | } | ||
1195 | else | ||
1196 | { | ||
1197 | return; | ||
1198 | } | ||
1199 | |||
1200 | if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches)) | ||
1201 | { | ||
1202 | $Element['attributes']['href'] = $matches[1]; | ||
1203 | |||
1204 | if (isset($matches[2])) | ||
1205 | { | ||
1206 | $Element['attributes']['title'] = substr($matches[2], 1, - 1); | ||
1207 | } | ||
1208 | |||
1209 | $extent += strlen($matches[0]); | ||
1210 | } | ||
1211 | else | ||
1212 | { | ||
1213 | if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) | ||
1214 | { | ||
1215 | $definition = strlen($matches[1]) ? $matches[1] : $Element['text']; | ||
1216 | $definition = strtolower($definition); | ||
1217 | |||
1218 | $extent += strlen($matches[0]); | ||
1219 | } | ||
1220 | else | ||
1221 | { | ||
1222 | $definition = strtolower($Element['text']); | ||
1223 | } | ||
1224 | |||
1225 | if ( ! isset($this->DefinitionData['Reference'][$definition])) | ||
1226 | { | ||
1227 | return; | ||
1228 | } | ||
1229 | |||
1230 | $Definition = $this->DefinitionData['Reference'][$definition]; | ||
1231 | |||
1232 | $Element['attributes']['href'] = $Definition['url']; | ||
1233 | $Element['attributes']['title'] = $Definition['title']; | ||
1234 | } | ||
1235 | |||
1236 | $Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']); | ||
1237 | |||
1238 | return array( | ||
1239 | 'extent' => $extent, | ||
1240 | 'element' => $Element, | ||
1241 | ); | ||
1242 | } | ||
1243 | |||
1244 | protected function inlineMarkup($Excerpt) | ||
1245 | { | ||
1246 | if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false) | ||
1247 | { | ||
1248 | return; | ||
1249 | } | ||
1250 | |||
1251 | if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches)) | ||
1252 | { | ||
1253 | return array( | ||
1254 | 'markup' => $matches[0], | ||
1255 | 'extent' => strlen($matches[0]), | ||
1256 | ); | ||
1257 | } | ||
1258 | |||
1259 | if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches)) | ||
1260 | { | ||
1261 | return array( | ||
1262 | 'markup' => $matches[0], | ||
1263 | 'extent' => strlen($matches[0]), | ||
1264 | ); | ||
1265 | } | ||
1266 | |||
1267 | if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) | ||
1268 | { | ||
1269 | return array( | ||
1270 | 'markup' => $matches[0], | ||
1271 | 'extent' => strlen($matches[0]), | ||
1272 | ); | ||
1273 | } | ||
1274 | } | ||
1275 | |||
1276 | protected function inlineSpecialCharacter($Excerpt) | ||
1277 | { | ||
1278 | if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text'])) | ||
1279 | { | ||
1280 | return array( | ||
1281 | 'markup' => '&', | ||
1282 | 'extent' => 1, | ||
1283 | ); | ||
1284 | } | ||
1285 | |||
1286 | $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot'); | ||
1287 | |||
1288 | if (isset($SpecialCharacter[$Excerpt['text'][0]])) | ||
1289 | { | ||
1290 | return array( | ||
1291 | 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';', | ||
1292 | 'extent' => 1, | ||
1293 | ); | ||
1294 | } | ||
1295 | } | ||
1296 | |||
1297 | protected function inlineStrikethrough($Excerpt) | ||
1298 | { | ||
1299 | if ( ! isset($Excerpt['text'][1])) | ||
1300 | { | ||
1301 | return; | ||
1302 | } | ||
1303 | |||
1304 | if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) | ||
1305 | { | ||
1306 | return array( | ||
1307 | 'extent' => strlen($matches[0]), | ||
1308 | 'element' => array( | ||
1309 | 'name' => 'del', | ||
1310 | 'text' => $matches[1], | ||
1311 | 'handler' => 'line', | ||
1312 | ), | ||
1313 | ); | ||
1314 | } | ||
1315 | } | ||
1316 | |||
1317 | protected function inlineUrl($Excerpt) | ||
1318 | { | ||
1319 | if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') | ||
1320 | { | ||
1321 | return; | ||
1322 | } | ||
1323 | |||
1324 | if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) | ||
1325 | { | ||
1326 | $Inline = array( | ||
1327 | 'extent' => strlen($matches[0][0]), | ||
1328 | 'position' => $matches[0][1], | ||
1329 | 'element' => array( | ||
1330 | 'name' => 'a', | ||
1331 | 'text' => $matches[0][0], | ||
1332 | 'attributes' => array( | ||
1333 | 'href' => $matches[0][0], | ||
1334 | ), | ||
1335 | ), | ||
1336 | ); | ||
1337 | |||
1338 | return $Inline; | ||
1339 | } | ||
1340 | } | ||
1341 | |||
1342 | protected function inlineUrlTag($Excerpt) | ||
1343 | { | ||
1344 | if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) | ||
1345 | { | ||
1346 | $url = str_replace(array('&', '<'), array('&', '<'), $matches[1]); | ||
1347 | |||
1348 | return array( | ||
1349 | 'extent' => strlen($matches[0]), | ||
1350 | 'element' => array( | ||
1351 | 'name' => 'a', | ||
1352 | 'text' => $url, | ||
1353 | 'attributes' => array( | ||
1354 | 'href' => $url, | ||
1355 | ), | ||
1356 | ), | ||
1357 | ); | ||
1358 | } | ||
1359 | } | ||
1360 | |||
1361 | # ~ | ||
1362 | |||
1363 | protected function unmarkedText($text) | ||
1364 | { | ||
1365 | if ($this->breaksEnabled) | ||
1366 | { | ||
1367 | $text = preg_replace('/[ ]*\n/', "<br />\n", $text); | ||
1368 | } | ||
1369 | else | ||
1370 | { | ||
1371 | $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text); | ||
1372 | $text = str_replace(" \n", "\n", $text); | ||
1373 | } | ||
1374 | |||
1375 | return $text; | ||
1376 | } | ||
1377 | |||
1378 | # | ||
1379 | # Handlers | ||
1380 | # | ||
1381 | |||
1382 | protected function element(array $Element) | ||
1383 | { | ||
1384 | $markup = '<'.$Element['name']; | ||
1385 | |||
1386 | if (isset($Element['attributes'])) | ||
1387 | { | ||
1388 | foreach ($Element['attributes'] as $name => $value) | ||
1389 | { | ||
1390 | if ($value === null) | ||
1391 | { | ||
1392 | continue; | ||
1393 | } | ||
1394 | |||
1395 | $markup .= ' '.$name.'="'.$value.'"'; | ||
1396 | } | ||
1397 | } | ||
1398 | |||
1399 | if (isset($Element['text'])) | ||
1400 | { | ||
1401 | $markup .= '>'; | ||
1402 | |||
1403 | if (isset($Element['handler'])) | ||
1404 | { | ||
1405 | $markup .= $this->{$Element['handler']}($Element['text']); | ||
1406 | } | ||
1407 | else | ||
1408 | { | ||
1409 | $markup .= $Element['text']; | ||
1410 | } | ||
1411 | |||
1412 | $markup .= '</'.$Element['name'].'>'; | ||
1413 | } | ||
1414 | else | ||
1415 | { | ||
1416 | $markup .= ' />'; | ||
1417 | } | ||
1418 | |||
1419 | return $markup; | ||
1420 | } | ||
1421 | |||
1422 | protected function elements(array $Elements) | ||
1423 | { | ||
1424 | $markup = ''; | ||
1425 | |||
1426 | foreach ($Elements as $Element) | ||
1427 | { | ||
1428 | $markup .= "\n" . $this->element($Element); | ||
1429 | } | ||
1430 | |||
1431 | $markup .= "\n"; | ||
1432 | |||
1433 | return $markup; | ||
1434 | } | ||
1435 | |||
1436 | # ~ | ||
1437 | |||
1438 | protected function li($lines) | ||
1439 | { | ||
1440 | $markup = $this->lines($lines); | ||
1441 | |||
1442 | $trimmedMarkup = trim($markup); | ||
1443 | |||
1444 | if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>') | ||
1445 | { | ||
1446 | $markup = $trimmedMarkup; | ||
1447 | $markup = substr($markup, 3); | ||
1448 | |||
1449 | $position = strpos($markup, "</p>"); | ||
1450 | |||
1451 | $markup = substr_replace($markup, '', $position, 4); | ||
1452 | } | ||
1453 | |||
1454 | return $markup; | ||
1455 | } | ||
1456 | |||
1457 | # | ||
1458 | # Deprecated Methods | ||
1459 | # | ||
1460 | |||
1461 | function parse($text) | ||
1462 | { | ||
1463 | $markup = $this->text($text); | ||
1464 | |||
1465 | return $markup; | ||
1466 | } | ||
1467 | |||
1468 | # | ||
1469 | # Static Methods | ||
1470 | # | ||
1471 | |||
1472 | static function instance($name = 'default') | ||
1473 | { | ||
1474 | if (isset(self::$instances[$name])) | ||
1475 | { | ||
1476 | return self::$instances[$name]; | ||
1477 | } | ||
1478 | |||
1479 | $instance = new static(); | ||
1480 | |||
1481 | self::$instances[$name] = $instance; | ||
1482 | |||
1483 | return $instance; | ||
1484 | } | ||
1485 | |||
1486 | private static $instances = array(); | ||
1487 | |||
1488 | # | ||
1489 | # Fields | ||
1490 | # | ||
1491 | |||
1492 | protected $DefinitionData; | ||
1493 | |||
1494 | # | ||
1495 | # Read-Only | ||
1496 | |||
1497 | protected $specialCharacters = array( | ||
1498 | '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', | ||
1499 | ); | ||
1500 | |||
1501 | protected $StrongRegex = array( | ||
1502 | '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', | ||
1503 | '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', | ||
1504 | ); | ||
1505 | |||
1506 | protected $EmRegex = array( | ||
1507 | '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', | ||
1508 | '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', | ||
1509 | ); | ||
1510 | |||
1511 | protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; | ||
1512 | |||
1513 | protected $voidElements = array( | ||
1514 | 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', | ||
1515 | ); | ||
1516 | |||
1517 | protected $textLevelElements = array( | ||
1518 | 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', | ||
1519 | 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', | ||
1520 | 'i', 'rp', 'del', 'code', 'strike', 'marquee', | ||
1521 | 'q', 'rt', 'ins', 'font', 'strong', | ||
1522 | 's', 'tt', 'sub', 'mark', | ||
1523 | 'u', 'xm', 'sup', 'nobr', | ||
1524 | 'var', 'ruby', | ||
1525 | 'wbr', 'span', | ||
1526 | 'time', | ||
1527 | ); | ||
1528 | } \ No newline at end of file | ||
diff --git a/plugins/markdown/README.md b/plugins/markdown/README.md index 4f021871..bc9427e2 100644 --- a/plugins/markdown/README.md +++ b/plugins/markdown/README.md | |||
@@ -20,26 +20,65 @@ 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 escape | ||
54 | |||
55 | By default, HTML tags are escaped. You can enable HTML tags rendering | ||
56 | by setting `security.markdwon_escape` to `false` in `data/config.json.php`: | ||
57 | |||
58 | ```json | ||
59 | { | ||
60 | "security": { | ||
61 | "markdown_escape": false | ||
62 | } | ||
63 | } | ||
64 | ``` | ||
65 | |||
66 | With this setting, Markdown support HTML tags. For example: | ||
67 | |||
68 | > <strong>strong</strong><strike>strike</strike> | ||
69 | |||
70 | Will render as: | ||
71 | |||
72 | > <strong>strong</strong><strike>strike</strike> | ||
73 | |||
74 | |||
75 | **Warning:** | ||
76 | |||
77 | * This setting might present **security risks** (XSS) on shared instances, even though tags | ||
78 | such as script, iframe, etc should be disabled. | ||
79 | * If you want to shaare HTML code, it is necessary to use inline code or code blocks. | ||
80 | * If your shaared descriptions contained HTML tags before enabling the markdown plugin, | ||
81 | enabling it might break your page. | ||
43 | 82 | ||
44 | ### Known issue | 83 | ### Known issue |
45 | 84 | ||
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 57fcce32..de7c823d 100644 --- a/plugins/markdown/markdown.php +++ b/plugins/markdown/markdown.php | |||
@@ -6,30 +6,28 @@ | |||
6 | * Shaare's descriptions are parsed with Markdown. | 6 | * Shaare's descriptions are parsed with Markdown. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | require_once 'Parsedown.php'; | ||
10 | |||
11 | /* | 9 | /* |
12 | * If this tag is used on a shaare, the description won't be processed by Parsedown. | 10 | * If this tag is used on a shaare, the description won't be processed by Parsedown. |
13 | * Using a private tag so it won't appear for visitors. | ||
14 | */ | 11 | */ |
15 | define('NO_MD_TAG', '.nomarkdown'); | 12 | define('NO_MD_TAG', 'nomarkdown'); |
16 | 13 | ||
17 | /** | 14 | /** |
18 | * Parse linklist descriptions. | 15 | * Parse linklist descriptions. |
19 | * | 16 | * |
20 | * @param array $data linklist data. | 17 | * @param array $data linklist data. |
18 | * @param ConfigManager $conf instance. | ||
21 | * | 19 | * |
22 | * @return mixed linklist data parsed in markdown (and converted to HTML). | 20 | * @return mixed linklist data parsed in markdown (and converted to HTML). |
23 | */ | 21 | */ |
24 | function hook_markdown_render_linklist($data) | 22 | function hook_markdown_render_linklist($data, $conf) |
25 | { | 23 | { |
26 | foreach ($data['links'] as &$value) { | 24 | foreach ($data['links'] as &$value) { |
27 | if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { | 25 | if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { |
26 | $value = stripNoMarkdownTag($value); | ||
28 | continue; | 27 | continue; |
29 | } | 28 | } |
30 | $value['description'] = process_markdown($value['description']); | 29 | $value['description'] = process_markdown($value['description'], $conf->get('security.markdown_escape', true)); |
31 | } | 30 | } |
32 | |||
33 | return $data; | 31 | return $data; |
34 | } | 32 | } |
35 | 33 | ||
@@ -37,16 +35,18 @@ function hook_markdown_render_linklist($data) | |||
37 | * Parse feed linklist descriptions. | 35 | * Parse feed linklist descriptions. |
38 | * | 36 | * |
39 | * @param array $data linklist data. | 37 | * @param array $data linklist data. |
38 | * @param ConfigManager $conf instance. | ||
40 | * | 39 | * |
41 | * @return mixed linklist data parsed in markdown (and converted to HTML). | 40 | * @return mixed linklist data parsed in markdown (and converted to HTML). |
42 | */ | 41 | */ |
43 | function hook_markdown_render_feed($data) | 42 | function hook_markdown_render_feed($data, $conf) |
44 | { | 43 | { |
45 | foreach ($data['links'] as &$value) { | 44 | foreach ($data['links'] as &$value) { |
46 | if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { | 45 | if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { |
46 | $value = stripNoMarkdownTag($value); | ||
47 | continue; | 47 | continue; |
48 | } | 48 | } |
49 | $value['description'] = process_markdown($value['description']); | 49 | $value['description'] = process_markdown($value['description'], $conf->get('security.markdown_escape', true)); |
50 | } | 50 | } |
51 | 51 | ||
52 | return $data; | 52 | return $data; |
@@ -55,19 +55,24 @@ function hook_markdown_render_feed($data) | |||
55 | /** | 55 | /** |
56 | * Parse daily descriptions. | 56 | * Parse daily descriptions. |
57 | * | 57 | * |
58 | * @param array $data daily data. | 58 | * @param array $data daily data. |
59 | * @param ConfigManager $conf instance. | ||
59 | * | 60 | * |
60 | * @return mixed daily data parsed in markdown (and converted to HTML). | 61 | * @return mixed daily data parsed in markdown (and converted to HTML). |
61 | */ | 62 | */ |
62 | function hook_markdown_render_daily($data) | 63 | function hook_markdown_render_daily($data, $conf) |
63 | { | 64 | { |
64 | // Manipulate columns data | 65 | // Manipulate columns data |
65 | foreach ($data['cols'] as &$value) { | 66 | foreach ($data['cols'] as &$value) { |
66 | foreach ($value as &$value2) { | 67 | foreach ($value as &$value2) { |
67 | if (!empty($value2['tags']) && noMarkdownTag($value2['tags'])) { | 68 | if (!empty($value2['tags']) && noMarkdownTag($value2['tags'])) { |
69 | $value2 = stripNoMarkdownTag($value2); | ||
68 | continue; | 70 | continue; |
69 | } | 71 | } |
70 | $value2['formatedDescription'] = process_markdown($value2['formatedDescription']); | 72 | $value2['formatedDescription'] = process_markdown( |
73 | $value2['formatedDescription'], | ||
74 | $conf->get('security.markdown_escape', true) | ||
75 | ); | ||
71 | } | 76 | } |
72 | } | 77 | } |
73 | 78 | ||
@@ -83,7 +88,30 @@ function hook_markdown_render_daily($data) | |||
83 | */ | 88 | */ |
84 | function noMarkdownTag($tags) | 89 | function noMarkdownTag($tags) |
85 | { | 90 | { |
86 | return strpos($tags, NO_MD_TAG) !== false; | 91 | return preg_match('/(^|\s)'. NO_MD_TAG .'(\s|$)/', $tags); |
92 | } | ||
93 | |||
94 | /** | ||
95 | * Remove the no-markdown meta tag so it won't be displayed. | ||
96 | * | ||
97 | * @param array $link Link data. | ||
98 | * | ||
99 | * @return array Updated link without no markdown tag. | ||
100 | */ | ||
101 | function stripNoMarkdownTag($link) | ||
102 | { | ||
103 | if (! empty($link['taglist'])) { | ||
104 | $offset = array_search(NO_MD_TAG, $link['taglist']); | ||
105 | if ($offset !== false) { | ||
106 | unset($link['taglist'][$offset]); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | if (!empty($link['tags'])) { | ||
111 | str_replace(NO_MD_TAG, '', $link['tags']); | ||
112 | } | ||
113 | |||
114 | return $link; | ||
87 | } | 115 | } |
88 | 116 | ||
89 | /** | 117 | /** |
@@ -138,7 +166,45 @@ function hook_markdown_render_editlink($data) | |||
138 | */ | 166 | */ |
139 | function reverse_text2clickable($description) | 167 | function reverse_text2clickable($description) |
140 | { | 168 | { |
141 | return preg_replace('!<a +href="([^ ]*)">[^ ]+</a>!m', '$1', $description); | 169 | $descriptionLines = explode(PHP_EOL, $description); |
170 | $descriptionOut = ''; | ||
171 | $codeBlockOn = false; | ||
172 | $lineCount = 0; | ||
173 | |||
174 | foreach ($descriptionLines as $descriptionLine) { | ||
175 | // Detect line of code: starting with 4 spaces, | ||
176 | // except lists which can start with +/*/- or `2.` after spaces. | ||
177 | $codeLineOn = preg_match('/^ +(?=[^\+\*\-])(?=(?!\d\.).)/', $descriptionLine) > 0; | ||
178 | // Detect and toggle block of code | ||
179 | if (!$codeBlockOn) { | ||
180 | $codeBlockOn = preg_match('/^```/', $descriptionLine) > 0; | ||
181 | } | ||
182 | elseif (preg_match('/^```/', $descriptionLine) > 0) { | ||
183 | $codeBlockOn = false; | ||
184 | } | ||
185 | |||
186 | $hashtagTitle = ' title="Hashtag [^"]+"'; | ||
187 | // Reverse `inline code` hashtags. | ||
188 | $descriptionLine = preg_replace( | ||
189 | '!(`[^`\n]*)<a href="[^ ]*"'. $hashtagTitle .'>([^<]+)</a>([^`\n]*`)!m', | ||
190 | '$1$2$3', | ||
191 | $descriptionLine | ||
192 | ); | ||
193 | |||
194 | // Reverse all links in code blocks, only non hashtag elsewhere. | ||
195 | $hashtagFilter = (!$codeBlockOn && !$codeLineOn) ? '(?!'. $hashtagTitle .')': '(?:'. $hashtagTitle .')?'; | ||
196 | $descriptionLine = preg_replace( | ||
197 | '#<a href="[^ ]*"'. $hashtagFilter .'>([^<]+)</a>#m', | ||
198 | '$1', | ||
199 | $descriptionLine | ||
200 | ); | ||
201 | |||
202 | $descriptionOut .= $descriptionLine; | ||
203 | if ($lineCount++ < count($descriptionLines) - 1) { | ||
204 | $descriptionOut .= PHP_EOL; | ||
205 | } | ||
206 | } | ||
207 | return $descriptionOut; | ||
142 | } | 208 | } |
143 | 209 | ||
144 | /** | 210 | /** |
@@ -190,7 +256,7 @@ function sanitize_html($description) | |||
190 | $description); | 256 | $description); |
191 | } | 257 | } |
192 | $description = preg_replace( | 258 | $description = preg_replace( |
193 | '#(<[^>]+)on[a-z]*="[^"]*"#is', | 259 | '#(<[^>]+)on[a-z]*="?[^ "]*"?#is', |
194 | '$1', | 260 | '$1', |
195 | $description); | 261 | $description); |
196 | return $description; | 262 | return $description; |
@@ -205,20 +271,21 @@ function sanitize_html($description) | |||
205 | * 5. Wrap description in 'markdown' CSS class. | 271 | * 5. Wrap description in 'markdown' CSS class. |
206 | * | 272 | * |
207 | * @param string $description input description text. | 273 | * @param string $description input description text. |
274 | * @param bool $escape escape HTML entities | ||
208 | * | 275 | * |
209 | * @return string HTML processed $description. | 276 | * @return string HTML processed $description. |
210 | */ | 277 | */ |
211 | function process_markdown($description) | 278 | function process_markdown($description, $escape = true) |
212 | { | 279 | { |
213 | $parsedown = new Parsedown(); | 280 | $parsedown = new Parsedown(); |
214 | 281 | ||
215 | $processedDescription = $description; | 282 | $processedDescription = $description; |
216 | $processedDescription = reverse_text2clickable($processedDescription); | ||
217 | $processedDescription = reverse_nl2br($processedDescription); | 283 | $processedDescription = reverse_nl2br($processedDescription); |
218 | $processedDescription = reverse_space2nbsp($processedDescription); | 284 | $processedDescription = reverse_space2nbsp($processedDescription); |
285 | $processedDescription = reverse_text2clickable($processedDescription); | ||
219 | $processedDescription = unescape($processedDescription); | 286 | $processedDescription = unescape($processedDescription); |
220 | $processedDescription = $parsedown | 287 | $processedDescription = $parsedown |
221 | ->setMarkupEscaped(false) | 288 | ->setMarkupEscaped($escape) |
222 | ->setBreaksEnabled(true) | 289 | ->setBreaksEnabled(true) |
223 | ->text($processedDescription); | 290 | ->text($processedDescription); |
224 | $processedDescription = sanitize_html($processedDescription); | 291 | $processedDescription = sanitize_html($processedDescription); |
diff --git a/plugins/piwik/piwik.meta b/plugins/piwik/piwik.meta new file mode 100644 index 00000000..20b9628e --- /dev/null +++ b/plugins/piwik/piwik.meta | |||
@@ -0,0 +1,4 @@ | |||
1 | description="A plugin that adds Piwik tracking code to Shaarli pages." | ||
2 | parameters="PIWIK_URL;PIWIK_SITEID" | ||
3 | parameter.PIWIK_URL="Piwik URL" | ||
4 | parameter.PIWIK_SITEID="Piwik site ID" | ||
diff --git a/plugins/piwik/piwik.php b/plugins/piwik/piwik.php new file mode 100644 index 00000000..7c44909c --- /dev/null +++ b/plugins/piwik/piwik.php | |||
@@ -0,0 +1,71 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * Piwik plugin. | ||
4 | * Adds tracking code on each page. | ||
5 | */ | ||
6 | |||
7 | /** | ||
8 | * Initialization function. | ||
9 | * It will be called when the plugin is loaded. | ||
10 | * This function can be used to return a list of initialization errors. | ||
11 | * | ||
12 | * @param $conf ConfigManager instance. | ||
13 | * | ||
14 | * @return array List of errors (optional). | ||
15 | */ | ||
16 | function piwik_init($conf) | ||
17 | { | ||
18 | $piwikUrl = $conf->get('plugins.PIWIK_URL'); | ||
19 | $piwikSiteid = $conf->get('plugins.PIWIK_SITEID'); | ||
20 | if (empty($piwikUrl) || empty($piwikSiteid)) { | ||
21 | $error = 'Piwik plugin error: ' . | ||
22 | 'Please define PIWIK_URL and PIWIK_SITEID in the plugin administration page.'; | ||
23 | return array($error); | ||
24 | } | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * Hook render_footer. | ||
29 | * Executed on every page redering. | ||
30 | * | ||
31 | * Template placeholders: | ||
32 | * - text | ||
33 | * - endofpage | ||
34 | * - js_files | ||
35 | * | ||
36 | * Data: | ||
37 | * - _PAGE_: current page | ||
38 | * - _LOGGEDIN_: true/false | ||
39 | * | ||
40 | * @param array $data data passed to plugin | ||
41 | * | ||
42 | * @return array altered $data. | ||
43 | */ | ||
44 | function hook_piwik_render_footer($data, $conf) | ||
45 | { | ||
46 | $piwikUrl = $conf->get('plugins.PIWIK_URL'); | ||
47 | $piwikSiteid = $conf->get('plugins.PIWIK_SITEID'); | ||
48 | if (empty($piwikUrl) || empty($piwikSiteid)) { | ||
49 | return $data; | ||
50 | } | ||
51 | |||
52 | // Free elements at the end of the page. | ||
53 | $data['endofpage'][] = '<!-- Piwik -->' . | ||
54 | '<script type="text/javascript">' . | ||
55 | ' var _paq = _paq || [];' . | ||
56 | ' _paq.push([\'trackPageView\']);' . | ||
57 | ' _paq.push([\'enableLinkTracking\']);' . | ||
58 | ' (function() {' . | ||
59 | ' var u="//' . $piwikUrl . '/";' . | ||
60 | ' _paq.push([\'setTrackerUrl\', u+\'piwik.php\']);' . | ||
61 | ' _paq.push([\'setSiteId\', \'' . $piwikSiteid . '\']);' . | ||
62 | ' var d=document, g=d.createElement(\'script\'), s=d.getElementsByTagName(\'script\')[0];' . | ||
63 | ' g.type=\'text/javascript\'; g.async=true; g.defer=true; g.src=u+\'piwik.js\'; s.parentNode.insertBefore(g,s);' . | ||
64 | ' })();' . | ||
65 | '</script>' . | ||
66 | '<noscript><p><img src="//' . $piwikUrl . '/piwik.php?idsite=' . $piwikSiteid . '" style="border:0;" alt="" /></p></noscript>' . | ||
67 | '<!-- End Piwik Code -->'; | ||
68 | |||
69 | return $data; | ||
70 | } | ||
71 | |||
diff --git a/plugins/playvideos/playvideos.html b/plugins/playvideos/playvideos.html deleted file mode 100644 index fda3d54f..00000000 --- a/plugins/playvideos/playvideos.html +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | <a href="#" id="playvideos">► Play Videos</a> \ No newline at end of file | ||
diff --git a/plugins/playvideos/playvideos.php b/plugins/playvideos/playvideos.php index 0a80aa58..64484504 100644 --- a/plugins/playvideos/playvideos.php +++ b/plugins/playvideos/playvideos.php | |||
@@ -16,7 +16,15 @@ | |||
16 | function hook_playvideos_render_header($data) | 16 | function hook_playvideos_render_header($data) |
17 | { | 17 | { |
18 | if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { | 18 | if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { |
19 | $data['buttons_toolbar'][] = file_get_contents(PluginManager::$PLUGINS_PATH . '/playvideos/playvideos.html'); | 19 | $playvideo = array( |
20 | 'attr' => array( | ||
21 | 'href' => '#', | ||
22 | 'title' => 'Video player', | ||
23 | 'id' => 'playvideos', | ||
24 | ), | ||
25 | 'html' => '► Play Videos' | ||
26 | ); | ||
27 | $data['buttons_toolbar'][] = $playvideo; | ||
20 | } | 28 | } |
21 | 29 | ||
22 | return $data; | 30 | return $data; |
@@ -37,4 +45,4 @@ function hook_playvideos_render_footer($data) | |||
37 | } | 45 | } |
38 | 46 | ||
39 | return $data; | 47 | return $data; |
40 | } \ No newline at end of file | 48 | } |
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/config.php.dist b/plugins/readityourself/config.php.dist deleted file mode 100644 index d6b5cb85..00000000 --- a/plugins/readityourself/config.php.dist +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | <?php | ||
2 | |||
3 | $GLOBALS['plugins']['READITYOUSELF_URL'] = 'http://someurl.com'; \ No newline at end of file | ||
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/readityourself/readityourself.php b/plugins/readityourself/readityourself.php index c8df4c4f..961c5bda 100644 --- a/plugins/readityourself/readityourself.php +++ b/plugins/readityourself/readityourself.php | |||
@@ -8,34 +8,42 @@ | |||
8 | // it seems kinda dead. | 8 | // it seems kinda dead. |
9 | // Not tested. | 9 | // Not tested. |
10 | 10 | ||
11 | // don't raise unnecessary warnings | 11 | /** |
12 | if (is_file(PluginManager::$PLUGINS_PATH . '/readityourself/config.php')) { | 12 | * Init function, return an error if the server is not set. |
13 | include PluginManager::$PLUGINS_PATH . '/readityourself/config.php'; | 13 | * |
14 | } | 14 | * @param $conf ConfigManager instance. |
15 | 15 | * | |
16 | if (empty($GLOBALS['plugins']['READITYOUSELF_URL'])) { | 16 | * @return array Eventual error. |
17 | $GLOBALS['plugin_errors'][] = 'Readityourself plugin error: '. | 17 | */ |
18 | 'Please define "$GLOBALS[\'plugins\'][\'READITYOUSELF_URL\']" '. | 18 | function readityourself_init($conf) |
19 | 'in "plugins/readityourself/config.php" or in your Shaarli config.php file.'; | 19 | { |
20 | $riyUrl = $conf->get('plugins.READITYOUSELF_URL'); | ||
21 | if (empty($riyUrl)) { | ||
22 | $error = 'Readityourself plugin error: '. | ||
23 | 'Please define the "READITYOUSELF_URL" setting in the plugin administration page.'; | ||
24 | return array($error); | ||
25 | } | ||
20 | } | 26 | } |
21 | 27 | ||
22 | /** | 28 | /** |
23 | * Add readityourself icon to link_plugin when rendering linklist. | 29 | * Add readityourself icon to link_plugin when rendering linklist. |
24 | * | 30 | * |
25 | * @param mixed $data - linklist data. | 31 | * @param mixed $data Linklist data. |
32 | * @param ConfigManager $conf Configuration Manager instance. | ||
26 | * | 33 | * |
27 | * @return mixed - linklist data with readityourself plugin. | 34 | * @return mixed - linklist data with readityourself plugin. |
28 | */ | 35 | */ |
29 | function hook_readityourself_render_linklist($data) | 36 | function hook_readityourself_render_linklist($data, $conf) |
30 | { | 37 | { |
31 | if (!isset($GLOBALS['plugins']['READITYOUSELF_URL'])) { | 38 | $riyUrl = $conf->get('plugins.READITYOUSELF_URL'); |
39 | if (empty($riyUrl)) { | ||
32 | return $data; | 40 | return $data; |
33 | } | 41 | } |
34 | 42 | ||
35 | $readityourself_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/readityourself/readityourself.html'); | 43 | $readityourself_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/readityourself/readityourself.html'); |
36 | 44 | ||
37 | foreach ($data['links'] as &$value) { | 45 | foreach ($data['links'] as &$value) { |
38 | $readityourself = sprintf($readityourself_html, $GLOBALS['plugins']['READITYOUSELF_URL'], $value['url'], PluginManager::$PLUGINS_PATH); | 46 | $readityourself = sprintf($readityourself_html, $riyUrl, $value['url'], PluginManager::$PLUGINS_PATH); |
39 | $value['link_plugin'][] = $readityourself; | 47 | $value['link_plugin'][] = $readityourself; |
40 | } | 48 | } |
41 | 49 | ||
diff --git a/plugins/wallabag/README.md b/plugins/wallabag/README.md index 5bc35be1..ea21a519 100644 --- a/plugins/wallabag/README.md +++ b/plugins/wallabag/README.md | |||
@@ -4,39 +4,34 @@ For each link in your Shaarli, adds a button to save the target page in your [wa | |||
4 | 4 | ||
5 | ### Installation | 5 | ### Installation |
6 | 6 | ||
7 | Clone this repository inside your `tpl/plugins/` directory, or download the archive and unpack it there. | 7 | Clone this repository inside your `tpl/plugins/` directory, or download the archive and unpack it there. |
8 | The directory structure should look like: | 8 | The directory structure should look like: |
9 | 9 | ||
10 | ``` | 10 | ```bash |
11 | └── tpl | 11 | └── tpl |
12 | └── plugins | 12 | └── plugins |
13 | └── wallabag | 13 | └── wallabag |
14 | ├── README.md | 14 | ├── README.md |
15 | ├── config.php.dist | 15 | ├── wallabag.html |
16 | ├── wallabag.html | 16 | ├── wallabag.meta |
17 | ├── wallabag.php | 17 | ├── wallabag.php |
18 | └── wallabag.png | 18 | ├── wallabag.php |
19 | └── WallabagInstance.php | ||
19 | ``` | 20 | ``` |
20 | 21 | ||
21 | To enable the plugin, add `'wallabag'` to your list of enabled plugins in `data/options.php` (`PLUGINS` array). | 22 | To enable the plugin, you can either: |
22 | This should look like: | ||
23 | 23 | ||
24 | ``` | 24 | * enable it in the plugins administration page (`?do=pluginadmin`). |
25 | $GLOBALS['config']['PLUGINS'] = array('qrcode', 'any_other_plugin', 'wallabag') | 25 | * add `wallabag` to your list of enabled plugins in `data/config.json.php` (`general.enabled_plugins` section). |
26 | ``` | ||
27 | 26 | ||
28 | ### Configuration | 27 | ### Configuration |
29 | 28 | ||
30 | Copy `config.php.dist` into `config.php` and setup your instance. | 29 | Go to the plugin administration page, and edit the following settings (with the plugin enabled). |
31 | 30 | ||
32 | *Wallabag instance URL* | 31 | **WALLABAG_URL**: *Wallabag instance URL* |
33 | ``` | 32 | Example value: `http://v2.wallabag.org` |
34 | $GLOBALS['config']['WALLABAG_URL'] = 'http://v2.wallabag.org' ; | ||
35 | ``` | ||
36 | 33 | ||
37 | *Wallabag version*: either `1` (for 1.x) or `2` (for 2.x) | 34 | **WALLABAG_VERSION**: *Wallabag version* |
38 | ``` | 35 | Value: either `1` (for 1.x) or `2` (for 2.x) |
39 | $GLOBALS['config']['WALLABAG_VERSION'] = 2; | ||
40 | ``` | ||
41 | 36 | ||
42 | > Note: these settings can also be set in `data/config.php`. \ No newline at end of file | 37 | > Note: these settings can also be set in `data/config.json.php`, in the plugins section. |
diff --git a/plugins/wallabag/config.php.dist b/plugins/wallabag/config.php.dist deleted file mode 100644 index a602708f..00000000 --- a/plugins/wallabag/config.php.dist +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | <?php | ||
2 | |||
3 | $GLOBALS['plugins']['WALLABAG_URL'] = 'https://demo.wallabag.org'; | ||
4 | $GLOBALS['plugins']['WALLABAG_VERSION'] = 1; \ No newline at end of file | ||
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/plugins/wallabag/wallabag.meta b/plugins/wallabag/wallabag.meta index 26e1ea63..9c93f81c 100644 --- a/plugins/wallabag/wallabag.meta +++ b/plugins/wallabag/wallabag.meta | |||
@@ -1,2 +1,4 @@ | |||
1 | description="For each link, add a Wallabag icon to save it in your instance." | 1 | description="For each link, add a Wallabag icon to save it in your instance." |
2 | parameters="WALLABAG_URL;WALLABAG_VERSION" \ No newline at end of file | 2 | parameters="WALLABAG_URL;WALLABAG_VERSION" |
3 | parameter.WALLABAG_URL="Wallabag API URL" | ||
4 | parameter.WALLABAG_VERSION="Wallabag API version (1 or 2)" \ No newline at end of file | ||
diff --git a/plugins/wallabag/wallabag.php b/plugins/wallabag/wallabag.php index 0d6fc66d..641e4cc2 100644 --- a/plugins/wallabag/wallabag.php +++ b/plugins/wallabag/wallabag.php | |||
@@ -6,34 +6,40 @@ | |||
6 | 6 | ||
7 | require_once 'WallabagInstance.php'; | 7 | require_once 'WallabagInstance.php'; |
8 | 8 | ||
9 | // don't raise unnecessary warnings | 9 | /** |
10 | if (is_file(PluginManager::$PLUGINS_PATH . '/wallabag/config.php')) { | 10 | * Init function, return an error if the server is not set. |
11 | include PluginManager::$PLUGINS_PATH . '/wallabag/config.php'; | 11 | * |
12 | } | 12 | * @param $conf ConfigManager instance. |
13 | 13 | * | |
14 | if (empty($GLOBALS['plugins']['WALLABAG_URL'])) { | 14 | * @return array Eventual error. |
15 | $GLOBALS['plugin_errors'][] = 'Wallabag plugin error: '. | 15 | */ |
16 | 'Please define "$GLOBALS[\'plugins\'][\'WALLABAG_URL\']" '. | 16 | function wallabag_init($conf) |
17 | 'in "plugins/wallabag/config.php" or in your Shaarli config.php file.'; | 17 | { |
18 | $wallabagUrl = $conf->get('plugins.WALLABAG_URL'); | ||
19 | if (empty($wallabagUrl)) { | ||
20 | $error = 'Wallabag plugin error: '. | ||
21 | 'Please define the "WALLABAG_URL" setting in the plugin administration page.'; | ||
22 | return array($error); | ||
23 | } | ||
18 | } | 24 | } |
19 | 25 | ||
20 | /** | 26 | /** |
21 | * Add wallabag icon to link_plugin when rendering linklist. | 27 | * Add wallabag icon to link_plugin when rendering linklist. |
22 | * | 28 | * |
23 | * @param mixed $data - linklist data. | 29 | * @param mixed $data Linklist data. |
30 | * @param ConfigManager $conf Configuration Manager instance. | ||
24 | * | 31 | * |
25 | * @return mixed - linklist data with wallabag plugin. | 32 | * @return mixed - linklist data with wallabag plugin. |
26 | */ | 33 | */ |
27 | function hook_wallabag_render_linklist($data) | 34 | function hook_wallabag_render_linklist($data, $conf) |
28 | { | 35 | { |
29 | if (!isset($GLOBALS['plugins']['WALLABAG_URL'])) { | 36 | $wallabagUrl = $conf->get('plugins.WALLABAG_URL'); |
37 | if (empty($wallabagUrl)) { | ||
30 | return $data; | 38 | return $data; |
31 | } | 39 | } |
32 | 40 | ||
33 | $version = isset($GLOBALS['plugins']['WALLABAG_VERSION']) | 41 | $version = $conf->get('plugins.WALLABAG_VERSION'); |
34 | ? $GLOBALS['plugins']['WALLABAG_VERSION'] | 42 | $wallabagInstance = new WallabagInstance($wallabagUrl, $version); |
35 | : ''; | ||
36 | $wallabagInstance = new WallabagInstance($GLOBALS['plugins']['WALLABAG_URL'], $version); | ||
37 | 43 | ||
38 | $wallabagHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/wallabag/wallabag.html'); | 44 | $wallabagHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/wallabag/wallabag.html'); |
39 | 45 | ||
diff --git a/shaarli_version.php b/shaarli_version.php index 184e5220..1c1c5d74 100644 --- a/shaarli_version.php +++ b/shaarli_version.php | |||
@@ -1 +1 @@ | |||
<?php /* 0.7.0 */ | <?php /* 0.8.4 */ ?> | ||
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/ApplicationUtilsTest.php b/tests/ApplicationUtilsTest.php index 6064357d..861b8d4e 100644 --- a/tests/ApplicationUtilsTest.php +++ b/tests/ApplicationUtilsTest.php | |||
@@ -3,6 +3,7 @@ | |||
3 | * ApplicationUtils' tests | 3 | * ApplicationUtils' tests |
4 | */ | 4 | */ |
5 | 5 | ||
6 | require_once 'application/config/ConfigManager.php'; | ||
6 | require_once 'application/ApplicationUtils.php'; | 7 | require_once 'application/ApplicationUtils.php'; |
7 | 8 | ||
8 | /** | 9 | /** |
@@ -59,7 +60,7 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase | |||
59 | $testTimeout | 60 | $testTimeout |
60 | ) | 61 | ) |
61 | ); | 62 | ); |
62 | $this->assertRegexp( | 63 | $this->assertRegExp( |
63 | self::$versionPattern, | 64 | self::$versionPattern, |
64 | ApplicationUtils::getLatestGitVersionCode( | 65 | ApplicationUtils::getLatestGitVersionCode( |
65 | 'https://raw.githubusercontent.com/shaarli/Shaarli/' | 66 | 'https://raw.githubusercontent.com/shaarli/Shaarli/' |
@@ -74,9 +75,12 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase | |||
74 | */ | 75 | */ |
75 | public function testGetLatestGitVersionCodeInvalidUrl() | 76 | public function testGetLatestGitVersionCodeInvalidUrl() |
76 | { | 77 | { |
78 | $oldlog = ini_get('error_log'); | ||
79 | ini_set('error_log', '/dev/null'); | ||
77 | $this->assertFalse( | 80 | $this->assertFalse( |
78 | ApplicationUtils::getLatestGitVersionCode('htttp://null.io', 1) | 81 | ApplicationUtils::getLatestGitVersionCode('htttp://null.io', 1) |
79 | ); | 82 | ); |
83 | ini_set('error_log', $oldlog); | ||
80 | } | 84 | } |
81 | 85 | ||
82 | /** | 86 | /** |
@@ -275,21 +279,21 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase | |||
275 | */ | 279 | */ |
276 | public function testCheckCurrentResourcePermissions() | 280 | public function testCheckCurrentResourcePermissions() |
277 | { | 281 | { |
278 | $config = array( | 282 | $conf = new ConfigManager(''); |
279 | 'CACHEDIR' => 'cache', | 283 | $conf->set('resource.thumbnails_cache', 'cache'); |
280 | 'CONFIG_FILE' => 'data/config.php', | 284 | $conf->set('resource.config', 'data/config.php'); |
281 | 'DATADIR' => 'data', | 285 | $conf->set('resource.data_dir', 'data'); |
282 | 'DATASTORE' => 'data/datastore.php', | 286 | $conf->set('resource.datastore', 'data/datastore.php'); |
283 | 'IPBANS_FILENAME' => 'data/ipbans.php', | 287 | $conf->set('resource.ban_file', 'data/ipbans.php'); |
284 | 'LOG_FILE' => 'data/log.txt', | 288 | $conf->set('resource.log', 'data/log.txt'); |
285 | 'PAGECACHE' => 'pagecache', | 289 | $conf->set('resource.page_cache', 'pagecache'); |
286 | 'RAINTPL_TMP' => 'tmp', | 290 | $conf->set('resource.raintpl_tmp', 'tmp'); |
287 | 'RAINTPL_TPL' => 'tpl', | 291 | $conf->set('resource.raintpl_tpl', 'tpl'); |
288 | 'UPDATECHECK_FILENAME' => 'data/lastupdatecheck.txt' | 292 | $conf->set('resource.update_check', 'data/lastupdatecheck.txt'); |
289 | ); | 293 | |
290 | $this->assertEquals( | 294 | $this->assertEquals( |
291 | array(), | 295 | array(), |
292 | ApplicationUtils::checkResourcePermissions($config) | 296 | ApplicationUtils::checkResourcePermissions($conf) |
293 | ); | 297 | ); |
294 | } | 298 | } |
295 | 299 | ||
@@ -298,18 +302,17 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase | |||
298 | */ | 302 | */ |
299 | public function testCheckCurrentResourcePermissionsErrors() | 303 | public function testCheckCurrentResourcePermissionsErrors() |
300 | { | 304 | { |
301 | $config = array( | 305 | $conf = new ConfigManager(''); |
302 | 'CACHEDIR' => 'null/cache', | 306 | $conf->set('resource.thumbnails_cache', 'null/cache'); |
303 | 'CONFIG_FILE' => 'null/data/config.php', | 307 | $conf->set('resource.config', 'null/data/config.php'); |
304 | 'DATADIR' => 'null/data', | 308 | $conf->set('resource.data_dir', 'null/data'); |
305 | 'DATASTORE' => 'null/data/store.php', | 309 | $conf->set('resource.datastore', 'null/data/store.php'); |
306 | 'IPBANS_FILENAME' => 'null/data/ipbans.php', | 310 | $conf->set('resource.ban_file', 'null/data/ipbans.php'); |
307 | 'LOG_FILE' => 'null/data/log.txt', | 311 | $conf->set('resource.log', 'null/data/log.txt'); |
308 | 'PAGECACHE' => 'null/pagecache', | 312 | $conf->set('resource.page_cache', 'null/pagecache'); |
309 | 'RAINTPL_TMP' => 'null/tmp', | 313 | $conf->set('resource.raintpl_tmp', 'null/tmp'); |
310 | 'RAINTPL_TPL' => 'null/tpl', | 314 | $conf->set('resource.raintpl_tpl', 'null/tpl'); |
311 | 'UPDATECHECK_FILENAME' => 'null/data/lastupdatecheck.txt' | 315 | $conf->set('resource.update_check', 'null/data/lastupdatecheck.txt'); |
312 | ); | ||
313 | $this->assertEquals( | 316 | $this->assertEquals( |
314 | array( | 317 | array( |
315 | '"null/tpl" directory is not readable', | 318 | '"null/tpl" directory is not readable', |
@@ -322,7 +325,7 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase | |||
322 | '"null/tmp" directory is not readable', | 325 | '"null/tmp" directory is not readable', |
323 | '"null/tmp" directory is not writable' | 326 | '"null/tmp" directory is not writable' |
324 | ), | 327 | ), |
325 | ApplicationUtils::checkResourcePermissions($config) | 328 | ApplicationUtils::checkResourcePermissions($conf) |
326 | ); | 329 | ); |
327 | } | 330 | } |
328 | } | 331 | } |
diff --git a/tests/CacheTest.php b/tests/CacheTest.php index 26c43225..992e26a5 100644 --- a/tests/CacheTest.php +++ b/tests/CacheTest.php | |||
@@ -64,10 +64,13 @@ class CacheTest extends PHPUnit_Framework_TestCase | |||
64 | */ | 64 | */ |
65 | public function testPurgeCachedPagesMissingDir() | 65 | public function testPurgeCachedPagesMissingDir() |
66 | { | 66 | { |
67 | $oldlog = ini_get('error_log'); | ||
68 | ini_set('error_log', '/dev/null'); | ||
67 | $this->assertEquals( | 69 | $this->assertEquals( |
68 | 'Cannot purge sandbox/dummycache_missing: no directory', | 70 | 'Cannot purge sandbox/dummycache_missing: no directory', |
69 | purgeCachedPages(self::$testCacheDir.'_missing') | 71 | purgeCachedPages(self::$testCacheDir.'_missing') |
70 | ); | 72 | ); |
73 | ini_set('error_log', $oldlog); | ||
71 | } | 74 | } |
72 | 75 | ||
73 | /** | 76 | /** |
diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php deleted file mode 100644 index 7200aae6..00000000 --- a/tests/ConfigTest.php +++ /dev/null | |||
@@ -1,244 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * Config' tests | ||
4 | */ | ||
5 | |||
6 | require_once 'application/Config.php'; | ||
7 | |||
8 | /** | ||
9 | * Unitary tests for Shaarli config related functions | ||
10 | */ | ||
11 | class ConfigTest extends PHPUnit_Framework_TestCase | ||
12 | { | ||
13 | // Configuration input set. | ||
14 | private static $configFields; | ||
15 | |||
16 | /** | ||
17 | * Executed before each test. | ||
18 | */ | ||
19 | public function setUp() | ||
20 | { | ||
21 | self::$configFields = array( | ||
22 | 'login' => 'login', | ||
23 | 'hash' => 'hash', | ||
24 | 'salt' => 'salt', | ||
25 | 'timezone' => 'Europe/Paris', | ||
26 | 'title' => 'title', | ||
27 | 'titleLink' => 'titleLink', | ||
28 | 'redirector' => '', | ||
29 | 'disablesessionprotection' => false, | ||
30 | 'privateLinkByDefault' => false, | ||
31 | 'config' => array( | ||
32 | 'CONFIG_FILE' => 'tests/config.php', | ||
33 | 'DATADIR' => 'tests', | ||
34 | 'config1' => 'config1data', | ||
35 | 'config2' => 'config2data', | ||
36 | ) | ||
37 | ); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * Executed after each test. | ||
42 | * | ||
43 | * @return void | ||
44 | */ | ||
45 | public function tearDown() | ||
46 | { | ||
47 | if (is_file(self::$configFields['config']['CONFIG_FILE'])) { | ||
48 | unlink(self::$configFields['config']['CONFIG_FILE']); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * Test writeConfig function, valid use case, while being logged in. | ||
54 | */ | ||
55 | public function testWriteConfig() | ||
56 | { | ||
57 | writeConfig(self::$configFields, true); | ||
58 | |||
59 | include self::$configFields['config']['CONFIG_FILE']; | ||
60 | $this->assertEquals(self::$configFields['login'], $GLOBALS['login']); | ||
61 | $this->assertEquals(self::$configFields['hash'], $GLOBALS['hash']); | ||
62 | $this->assertEquals(self::$configFields['salt'], $GLOBALS['salt']); | ||
63 | $this->assertEquals(self::$configFields['timezone'], $GLOBALS['timezone']); | ||
64 | $this->assertEquals(self::$configFields['title'], $GLOBALS['title']); | ||
65 | $this->assertEquals(self::$configFields['titleLink'], $GLOBALS['titleLink']); | ||
66 | $this->assertEquals(self::$configFields['redirector'], $GLOBALS['redirector']); | ||
67 | $this->assertEquals(self::$configFields['disablesessionprotection'], $GLOBALS['disablesessionprotection']); | ||
68 | $this->assertEquals(self::$configFields['privateLinkByDefault'], $GLOBALS['privateLinkByDefault']); | ||
69 | $this->assertEquals(self::$configFields['config']['config1'], $GLOBALS['config']['config1']); | ||
70 | $this->assertEquals(self::$configFields['config']['config2'], $GLOBALS['config']['config2']); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * Test writeConfig option while logged in: | ||
75 | * 1. init fields. | ||
76 | * 2. update fields, add new sub config, add new root config. | ||
77 | * 3. rewrite config. | ||
78 | * 4. check result. | ||
79 | */ | ||
80 | public function testWriteConfigFieldUpdate() | ||
81 | { | ||
82 | writeConfig(self::$configFields, true); | ||
83 | self::$configFields['title'] = 'ok'; | ||
84 | self::$configFields['config']['config1'] = 'ok'; | ||
85 | self::$configFields['config']['config_new'] = 'ok'; | ||
86 | self::$configFields['new'] = 'should not be saved'; | ||
87 | writeConfig(self::$configFields, true); | ||
88 | |||
89 | include self::$configFields['config']['CONFIG_FILE']; | ||
90 | $this->assertEquals('ok', $GLOBALS['title']); | ||
91 | $this->assertEquals('ok', $GLOBALS['config']['config1']); | ||
92 | $this->assertEquals('ok', $GLOBALS['config']['config_new']); | ||
93 | $this->assertFalse(isset($GLOBALS['new'])); | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * Test writeConfig function with an empty array. | ||
98 | * | ||
99 | * @expectedException MissingFieldConfigException | ||
100 | */ | ||
101 | public function testWriteConfigEmpty() | ||
102 | { | ||
103 | writeConfig(array(), true); | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * Test writeConfig function with a missing mandatory field. | ||
108 | * | ||
109 | * @expectedException MissingFieldConfigException | ||
110 | */ | ||
111 | public function testWriteConfigMissingField() | ||
112 | { | ||
113 | unset(self::$configFields['login']); | ||
114 | writeConfig(self::$configFields, true); | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * Test writeConfig function while being logged out, and there is no config file existing. | ||
119 | */ | ||
120 | public function testWriteConfigLoggedOutNoFile() | ||
121 | { | ||
122 | writeConfig(self::$configFields, false); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * Test writeConfig function while being logged out, and a config file already exists. | ||
127 | * | ||
128 | * @expectedException UnauthorizedConfigException | ||
129 | */ | ||
130 | public function testWriteConfigLoggedOutWithFile() | ||
131 | { | ||
132 | file_put_contents(self::$configFields['config']['CONFIG_FILE'], ''); | ||
133 | writeConfig(self::$configFields, false); | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * Test save_plugin_config with valid data. | ||
138 | * | ||
139 | * @throws PluginConfigOrderException | ||
140 | */ | ||
141 | public function testSavePluginConfigValid() | ||
142 | { | ||
143 | $data = array( | ||
144 | 'order_plugin1' => 2, // no plugin related | ||
145 | 'plugin2' => 0, // new - at the end | ||
146 | 'plugin3' => 0, // 2nd | ||
147 | 'order_plugin3' => 8, | ||
148 | 'plugin4' => 0, // 1st | ||
149 | 'order_plugin4' => 5, | ||
150 | ); | ||
151 | |||
152 | $expected = array( | ||
153 | 'plugin3', | ||
154 | 'plugin4', | ||
155 | 'plugin2', | ||
156 | ); | ||
157 | |||
158 | $out = save_plugin_config($data); | ||
159 | $this->assertEquals($expected, $out); | ||
160 | } | ||
161 | |||
162 | /** | ||
163 | * Test save_plugin_config with invalid data. | ||
164 | * | ||
165 | * @expectedException PluginConfigOrderException | ||
166 | */ | ||
167 | public function testSavePluginConfigInvalid() | ||
168 | { | ||
169 | $data = array( | ||
170 | 'plugin2' => 0, | ||
171 | 'plugin3' => 0, | ||
172 | 'order_plugin3' => 0, | ||
173 | 'plugin4' => 0, | ||
174 | 'order_plugin4' => 0, | ||
175 | ); | ||
176 | |||
177 | save_plugin_config($data); | ||
178 | } | ||
179 | |||
180 | /** | ||
181 | * Test save_plugin_config without data. | ||
182 | */ | ||
183 | public function testSavePluginConfigEmpty() | ||
184 | { | ||
185 | $this->assertEquals(array(), save_plugin_config(array())); | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * Test validate_plugin_order with valid data. | ||
190 | */ | ||
191 | public function testValidatePluginOrderValid() | ||
192 | { | ||
193 | $data = array( | ||
194 | 'order_plugin1' => 2, | ||
195 | 'plugin2' => 0, | ||
196 | 'plugin3' => 0, | ||
197 | 'order_plugin3' => 1, | ||
198 | 'plugin4' => 0, | ||
199 | 'order_plugin4' => 5, | ||
200 | ); | ||
201 | |||
202 | $this->assertTrue(validate_plugin_order($data)); | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * Test validate_plugin_order with invalid data. | ||
207 | */ | ||
208 | public function testValidatePluginOrderInvalid() | ||
209 | { | ||
210 | $data = array( | ||
211 | 'order_plugin1' => 2, | ||
212 | 'order_plugin3' => 1, | ||
213 | 'order_plugin4' => 1, | ||
214 | ); | ||
215 | |||
216 | $this->assertFalse(validate_plugin_order($data)); | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * Test load_plugin_parameter_values. | ||
221 | */ | ||
222 | public function testLoadPluginParameterValues() | ||
223 | { | ||
224 | $plugins = array( | ||
225 | 'plugin_name' => array( | ||
226 | 'parameters' => array( | ||
227 | 'param1' => true, | ||
228 | 'param2' => false, | ||
229 | 'param3' => '', | ||
230 | ) | ||
231 | ) | ||
232 | ); | ||
233 | |||
234 | $parameters = array( | ||
235 | 'param1' => 'value1', | ||
236 | 'param2' => 'value2', | ||
237 | ); | ||
238 | |||
239 | $result = load_plugin_parameter_values($plugins, $parameters); | ||
240 | $this->assertEquals('value1', $result['plugin_name']['parameters']['param1']); | ||
241 | $this->assertEquals('value2', $result['plugin_name']['parameters']['param2']); | ||
242 | $this->assertEquals('', $result['plugin_name']['parameters']['param3']); | ||
243 | } | ||
244 | } | ||
diff --git a/tests/FeedBuilderTest.php b/tests/FeedBuilderTest.php index 069b1581..06a44506 100644 --- a/tests/FeedBuilderTest.php +++ b/tests/FeedBuilderTest.php | |||
@@ -76,7 +76,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
76 | // Test headers (RSS) | 76 | // Test headers (RSS) |
77 | $this->assertEquals(self::$RSS_LANGUAGE, $data['language']); | 77 | $this->assertEquals(self::$RSS_LANGUAGE, $data['language']); |
78 | $this->assertEmpty($data['pubsubhub_url']); | 78 | $this->assertEmpty($data['pubsubhub_url']); |
79 | $this->assertEquals('Tue, 10 Mar 2015 11:46:51 +0100', $data['last_update']); | 79 | $this->assertRegExp('/Wed, 03 Aug 2016 09:30:33 \+\d{4}/', $data['last_update']); |
80 | $this->assertEquals(true, $data['show_dates']); | 80 | $this->assertEquals(true, $data['show_dates']); |
81 | $this->assertEquals('http://host.tld/index.php?do=feed', $data['self_link']); | 81 | $this->assertEquals('http://host.tld/index.php?do=feed', $data['self_link']); |
82 | $this->assertEquals('http://host.tld/', $data['index_url']); | 82 | $this->assertEquals('http://host.tld/', $data['index_url']); |
@@ -84,23 +84,30 @@ 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->assertEquals('Tue, 10 Mar 2015 11:46:51 +0100', $link['iso_date']); | 92 | $this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']); |
93 | $pub = DateTime::createFromFormat(DateTime::RSS, $link['pub_iso_date']); | ||
94 | $up = DateTime::createFromFormat(DateTime::ATOM, $link['up_iso_date']); | ||
95 | $this->assertEquals($pub, $up); | ||
92 | $this->assertContains('Stallman has a beard', $link['description']); | 96 | $this->assertContains('Stallman has a beard', $link['description']); |
93 | $this->assertContains('Permalink', $link['description']); | 97 | $this->assertContains('Permalink', $link['description']); |
94 | $this->assertContains('http://host.tld/?WDWyig', $link['description']); | 98 | $this->assertContains('http://host.tld/?WDWyig', $link['description']); |
95 | $this->assertEquals(1, count($link['taglist'])); | 99 | $this->assertEquals(1, count($link['taglist'])); |
96 | $this->assertEquals('stuff', $link['taglist'][0]); | 100 | $this->assertEquals('sTuff', $link['taglist'][0]); |
97 | 101 | ||
98 | // Test URL with external link. | 102 | // Test URL with external link. |
99 | $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']); |
100 | 104 | ||
101 | // Test multitags. | 105 | // Test multitags. |
102 | $this->assertEquals(5, count($data['links']['20141125_084734']['taglist'])); | 106 | $this->assertEquals(5, count($data['links'][6]['taglist'])); |
103 | $this->assertEquals('css', $data['links']['20141125_084734']['taglist'][0]); | 107 | $this->assertEquals('css', $data['links'][6]['taglist'][0]); |
108 | |||
109 | // Test update date | ||
110 | $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']); | ||
104 | } | 111 | } |
105 | 112 | ||
106 | /** | 113 | /** |
@@ -112,8 +119,10 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
112 | $feedBuilder->setLocale(self::$LOCALE); | 119 | $feedBuilder->setLocale(self::$LOCALE); |
113 | $data = $feedBuilder->buildData(); | 120 | $data = $feedBuilder->buildData(); |
114 | $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); | 121 | $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); |
115 | $link = array_shift($data['links']); | 122 | $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']); |
116 | $this->assertEquals('2015-03-10T11:46:51+01:00', $link['iso_date']); | 123 | $link = reset($data['links']); |
124 | $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']); | ||
125 | $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']); | ||
117 | } | 126 | } |
118 | 127 | ||
119 | /** | 128 | /** |
@@ -130,7 +139,8 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
130 | $data = $feedBuilder->buildData(); | 139 | $data = $feedBuilder->buildData(); |
131 | $this->assertEquals(1, count($data['links'])); | 140 | $this->assertEquals(1, count($data['links'])); |
132 | $link = array_shift($data['links']); | 141 | $link = array_shift($data['links']); |
133 | $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']); | ||
134 | } | 144 | } |
135 | 145 | ||
136 | /** | 146 | /** |
@@ -146,7 +156,8 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
146 | $data = $feedBuilder->buildData(); | 156 | $data = $feedBuilder->buildData(); |
147 | $this->assertEquals(1, count($data['links'])); | 157 | $this->assertEquals(1, count($data['links'])); |
148 | $link = array_shift($data['links']); | 158 | $link = array_shift($data['links']); |
149 | $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']); | ||
150 | } | 161 | } |
151 | 162 | ||
152 | /** | 163 | /** |
@@ -162,15 +173,17 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
162 | $this->assertTrue($data['usepermalinks']); | 173 | $this->assertTrue($data['usepermalinks']); |
163 | // First link is a permalink | 174 | // First link is a permalink |
164 | $link = array_shift($data['links']); | 175 | $link = array_shift($data['links']); |
165 | $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']); | ||
166 | $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); | 178 | $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); |
167 | $this->assertEquals('http://host.tld/?WDWyig', $link['url']); | 179 | $this->assertEquals('http://host.tld/?WDWyig', $link['url']); |
168 | $this->assertContains('Direct link', $link['description']); | 180 | $this->assertContains('Direct link', $link['description']); |
169 | $this->assertContains('http://host.tld/?WDWyig', $link['description']); | 181 | $this->assertContains('http://host.tld/?WDWyig', $link['description']); |
170 | // Second link is a direct link | 182 | // Second link is a direct link |
171 | $link = array_shift($data['links']); | 183 | $link = array_shift($data['links']); |
172 | $this->assertEquals('20150310_114633', $link['linkdate']); | 184 | $this->assertEquals(8, $link['id']); |
173 | $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']); | ||
174 | $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']); |
175 | $this->assertContains('Direct link', $link['description']); | 188 | $this->assertContains('Direct link', $link['description']); |
176 | $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']); |
@@ -209,4 +222,38 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase | |||
209 | $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); | 222 | $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); |
210 | $this->assertEquals('http://pubsubhub.io', $data['pubsubhub_url']); | 223 | $this->assertEquals('http://pubsubhub.io', $data['pubsubhub_url']); |
211 | } | 224 | } |
225 | |||
226 | /** | ||
227 | * Test buildData when Shaarli is served from a subdirectory | ||
228 | */ | ||
229 | public function testBuildDataServerSubdir() | ||
230 | { | ||
231 | $serverInfo = array( | ||
232 | 'HTTPS' => 'Off', | ||
233 | 'SERVER_NAME' => 'host.tld', | ||
234 | 'SERVER_PORT' => '8080', | ||
235 | 'SCRIPT_NAME' => '/~user/shaarli/index.php', | ||
236 | 'REQUEST_URI' => '/~user/shaarli/index.php?do=feed', | ||
237 | ); | ||
238 | $feedBuilder = new FeedBuilder( | ||
239 | self::$linkDB, | ||
240 | FeedBuilder::$FEED_ATOM, | ||
241 | $serverInfo, | ||
242 | null, | ||
243 | false | ||
244 | ); | ||
245 | $feedBuilder->setLocale(self::$LOCALE); | ||
246 | $data = $feedBuilder->buildData(); | ||
247 | |||
248 | $this->assertEquals( | ||
249 | 'http://host.tld:8080/~user/shaarli/index.php?do=feed', | ||
250 | $data['self_link'] | ||
251 | ); | ||
252 | |||
253 | // Test first link (note link) | ||
254 | $link = array_shift($data['links']); | ||
255 | $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['guid']); | ||
256 | $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['url']); | ||
257 | $this->assertContains('http://host.tld:8080/~user/shaarli/?addtag=hashtag', $link['description']); | ||
258 | } | ||
212 | } | 259 | } |
diff --git a/tests/HttpUtils/GetIpAdressFromProxyTest.php b/tests/HttpUtils/GetIpAdressFromProxyTest.php new file mode 100644 index 00000000..6a74a45a --- /dev/null +++ b/tests/HttpUtils/GetIpAdressFromProxyTest.php | |||
@@ -0,0 +1,58 @@ | |||
1 | <?php | ||
2 | |||
3 | require_once 'application/HttpUtils.php'; | ||
4 | |||
5 | /** | ||
6 | * Unitary tests for getIpAddressFromProxy() | ||
7 | */ | ||
8 | class GetIpAdressFromProxyTest extends PHPUnit_Framework_TestCase { | ||
9 | |||
10 | /** | ||
11 | * Test without proxy | ||
12 | */ | ||
13 | public function testWithoutProxy() | ||
14 | { | ||
15 | $this->assertFalse(getIpAddressFromProxy(array(), array())); | ||
16 | } | ||
17 | |||
18 | /** | ||
19 | * Test with a single IP in proxy header. | ||
20 | */ | ||
21 | public function testWithOneForwardedIp() | ||
22 | { | ||
23 | $ip = '1.1.1.1'; | ||
24 | $server = array('HTTP_X_FORWARDED_FOR' => $ip); | ||
25 | $this->assertEquals($ip, getIpAddressFromProxy($server, array())); | ||
26 | } | ||
27 | |||
28 | /** | ||
29 | * Test with a multiple IPs in proxy header. | ||
30 | */ | ||
31 | public function testWithMultipleForwardedIp() | ||
32 | { | ||
33 | $ip = '1.1.1.1'; | ||
34 | $ip2 = '2.2.2.2'; | ||
35 | |||
36 | $server = array('HTTP_X_FORWARDED_FOR' => $ip .','. $ip2); | ||
37 | $this->assertEquals($ip2, getIpAddressFromProxy($server, array())); | ||
38 | |||
39 | $server = array('HTTP_X_FORWARDED_FOR' => $ip .' , '. $ip2); | ||
40 | $this->assertEquals($ip2, getIpAddressFromProxy($server, array())); | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Test with a trusted IP address. | ||
45 | */ | ||
46 | public function testWithTrustedIp() | ||
47 | { | ||
48 | $ip = '1.1.1.1'; | ||
49 | $ip2 = '2.2.2.2'; | ||
50 | |||
51 | $server = array('HTTP_X_FORWARDED_FOR' => $ip); | ||
52 | $this->assertFalse(getIpAddressFromProxy($server, array($ip))); | ||
53 | |||
54 | $server = array('HTTP_X_FORWARDED_FOR' => $ip .','. $ip2); | ||
55 | $this->assertEquals($ip2, getIpAddressFromProxy($server, array($ip))); | ||
56 | $this->assertFalse(getIpAddressFromProxy($server, array($ip, $ip2))); | ||
57 | } | ||
58 | } | ||
diff --git a/tests/LanguagesTest.php b/tests/LanguagesTest.php new file mode 100644 index 00000000..79c136c8 --- /dev/null +++ b/tests/LanguagesTest.php | |||
@@ -0,0 +1,41 @@ | |||
1 | <?php | ||
2 | |||
3 | require_once 'application/Languages.php'; | ||
4 | |||
5 | /** | ||
6 | * Class LanguagesTest. | ||
7 | */ | ||
8 | class LanguagesTest extends PHPUnit_Framework_TestCase | ||
9 | { | ||
10 | /** | ||
11 | * Test t() with a simple non identified value. | ||
12 | */ | ||
13 | public function testTranslateSingleNotID() | ||
14 | { | ||
15 | $text = 'abcdé 564 fgK'; | ||
16 | $this->assertEquals($text, t($text)); | ||
17 | } | ||
18 | |||
19 | /** | ||
20 | * Test t() with a non identified plural form. | ||
21 | */ | ||
22 | public function testTranslatePluralNotID() | ||
23 | { | ||
24 | $text = '%s sandwich'; | ||
25 | $nText = '%s sandwiches'; | ||
26 | $this->assertEquals('0 sandwich', t($text, $nText)); | ||
27 | $this->assertEquals('1 sandwich', t($text, $nText, 1)); | ||
28 | $this->assertEquals('2 sandwiches', t($text, $nText, 2)); | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * Test t() with a non identified invalid plural form. | ||
33 | */ | ||
34 | public function testTranslatePluralNotIDInvalid() | ||
35 | { | ||
36 | $text = 'sandwich'; | ||
37 | $nText = 'sandwiches'; | ||
38 | $this->assertEquals('sandwich', t($text, $nText, 1)); | ||
39 | $this->assertEquals('sandwiches', t($text, $nText, 2)); | ||
40 | } | ||
41 | } | ||
diff --git a/tests/LinkDBTest.php b/tests/LinkDBTest.php index b055fe91..1f62a34a 100644 --- a/tests/LinkDBTest.php +++ b/tests/LinkDBTest.php | |||
@@ -101,7 +101,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
101 | * Attempt to instantiate a LinkDB whereas the datastore is not writable | 101 | * Attempt to instantiate a LinkDB whereas the datastore is not writable |
102 | * | 102 | * |
103 | * @expectedException IOException | 103 | * @expectedException IOException |
104 | * @expectedExceptionMessageRegExp /Error accessing null/ | 104 | * @expectedExceptionMessageRegExp /Error accessing\nnull/ |
105 | */ | 105 | */ |
106 | public function testConstructDatastoreNotWriteable() | 106 | public function testConstructDatastoreNotWriteable() |
107 | { | 107 | { |
@@ -117,7 +117,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
117 | unlink(self::$testDatastore); | 117 | unlink(self::$testDatastore); |
118 | $this->assertFileNotExists(self::$testDatastore); | 118 | $this->assertFileNotExists(self::$testDatastore); |
119 | 119 | ||
120 | $checkDB = self::getMethod('_checkDB'); | 120 | $checkDB = self::getMethod('check'); |
121 | $checkDB->invokeArgs($linkDB, array()); | 121 | $checkDB->invokeArgs($linkDB, array()); |
122 | $this->assertFileExists(self::$testDatastore); | 122 | $this->assertFileExists(self::$testDatastore); |
123 | 123 | ||
@@ -134,7 +134,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
134 | $datastoreSize = filesize(self::$testDatastore); | 134 | $datastoreSize = filesize(self::$testDatastore); |
135 | $this->assertGreaterThan(0, $datastoreSize); | 135 | $this->assertGreaterThan(0, $datastoreSize); |
136 | 136 | ||
137 | $checkDB = self::getMethod('_checkDB'); | 137 | $checkDB = self::getMethod('check'); |
138 | $checkDB->invokeArgs($linkDB, array()); | 138 | $checkDB->invokeArgs($linkDB, array()); |
139 | 139 | ||
140 | // ensure the datastore is left unmodified | 140 | // ensure the datastore is left unmodified |
@@ -180,21 +180,22 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
180 | /** | 180 | /** |
181 | * Save the links to the DB | 181 | * Save the links to the DB |
182 | */ | 182 | */ |
183 | public function testSaveDB() | 183 | public function testSave() |
184 | { | 184 | { |
185 | $testDB = new LinkDB(self::$testDatastore, true, false); | 185 | $testDB = new LinkDB(self::$testDatastore, true, false); |
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->savedb('tests'); | 198 | $testDB->save('tests'); |
198 | 199 | ||
199 | $testDB = new LinkDB(self::$testDatastore, true, false); | 200 | $testDB = new LinkDB(self::$testDatastore, true, false); |
200 | $this->assertEquals($dbSize + 1, sizeof($testDB)); | 201 | $this->assertEquals($dbSize + 1, sizeof($testDB)); |
@@ -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 | } |
@@ -256,7 +257,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
256 | $link = self::$publicLinkDB->getLinkFromUrl('http://mediagoblin.org/'); | 257 | $link = self::$publicLinkDB->getLinkFromUrl('http://mediagoblin.org/'); |
257 | 258 | ||
258 | $this->assertNotEquals(false, $link); | 259 | $this->assertNotEquals(false, $link); |
259 | $this->assertEquals( | 260 | $this->assertContains( |
260 | 'A free software media publishing platform', | 261 | 'A free software media publishing platform', |
261 | $link['description'] | 262 | $link['description'] |
262 | ); | 263 | ); |
@@ -290,7 +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, |
293 | 'stuff' => 2, | 294 | 'hashtag' => 2, |
295 | // The DB contains a link with `sTuff` and another one with `stuff` tag. | ||
296 | // They need to be grouped with the first case found - order by date DESC: `sTuff`. | ||
297 | 'sTuff' => 2, | ||
298 | 'ut' => 1, | ||
294 | ), | 299 | ), |
295 | self::$publicLinkDB->allTags() | 300 | self::$publicLinkDB->allTags() |
296 | ); | 301 | ); |
@@ -310,9 +315,15 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
310 | 'w3c' => 1, | 315 | 'w3c' => 1, |
311 | 'css' => 1, | 316 | 'css' => 1, |
312 | 'Mercurial' => 1, | 317 | 'Mercurial' => 1, |
313 | 'stuff' => 2, | 318 | 'sTuff' => 2, |
314 | '-exclude' => 1, | 319 | '-exclude' => 1, |
315 | '.hidden' => 1, | 320 | '.hidden' => 1, |
321 | 'hashtag' => 2, | ||
322 | 'tag1' => 1, | ||
323 | 'tag2' => 1, | ||
324 | 'tag3' => 1, | ||
325 | 'tag4' => 1, | ||
326 | 'ut' => 1, | ||
316 | ), | 327 | ), |
317 | self::$privateLinkDB->allTags() | 328 | self::$privateLinkDB->allTags() |
318 | ); | 329 | ); |
@@ -403,6 +414,11 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
403 | 1, | 414 | 1, |
404 | count(self::$publicLinkDB->filterHash($request)) | 415 | count(self::$publicLinkDB->filterHash($request)) |
405 | ); | 416 | ); |
417 | $request = smallHash('20150310_114633' . 8); | ||
418 | $this->assertEquals( | ||
419 | 1, | ||
420 | count(self::$publicLinkDB->filterHash($request)) | ||
421 | ); | ||
406 | } | 422 | } |
407 | 423 | ||
408 | /** | 424 | /** |
@@ -425,4 +441,23 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
425 | { | 441 | { |
426 | self::$publicLinkDB->filterHash(''); | 442 | self::$publicLinkDB->filterHash(''); |
427 | } | 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 | } | ||
428 | } | 463 | } |
diff --git a/tests/LinkFilterTest.php b/tests/LinkFilterTest.php index 1620bb78..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 | } |
@@ -387,4 +387,30 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase | |||
387 | )) | 387 | )) |
388 | ); | 388 | ); |
389 | } | 389 | } |
390 | |||
391 | /** | ||
392 | * Filter links by #hashtag. | ||
393 | */ | ||
394 | public function testFilterByHashtag() | ||
395 | { | ||
396 | $hashtag = 'hashtag'; | ||
397 | $this->assertEquals( | ||
398 | 3, | ||
399 | count(self::$linkFilter->filter( | ||
400 | LinkFilter::$FILTER_TAG, | ||
401 | $hashtag | ||
402 | )) | ||
403 | ); | ||
404 | |||
405 | $hashtag = 'private'; | ||
406 | $this->assertEquals( | ||
407 | 1, | ||
408 | count(self::$linkFilter->filter( | ||
409 | LinkFilter::$FILTER_TAG, | ||
410 | $hashtag, | ||
411 | false, | ||
412 | true | ||
413 | )) | ||
414 | ); | ||
415 | } | ||
390 | } | 416 | } |
diff --git a/tests/LinkUtilsTest.php b/tests/LinkUtilsTest.php index d1b022fd..7c0d4b0b 100644 --- a/tests/LinkUtilsTest.php +++ b/tests/LinkUtilsTest.php | |||
@@ -93,4 +93,92 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase | |||
93 | $refDB = new ReferenceLinkDB(); | 93 | $refDB = new ReferenceLinkDB(); |
94 | $this->assertEquals($refDB->countPrivateLinks(), count_private($refDB->getLinks())); | 94 | $this->assertEquals($refDB->countPrivateLinks(), count_private($refDB->getLinks())); |
95 | } | 95 | } |
96 | |||
97 | /** | ||
98 | * Test text2clickable without a redirector being set. | ||
99 | */ | ||
100 | public function testText2clickableWithoutRedirector() | ||
101 | { | ||
102 | $text = 'stuff http://hello.there/is=someone#here otherstuff'; | ||
103 | $expectedText = 'stuff <a href="http://hello.there/is=someone#here">http://hello.there/is=someone#here</a> otherstuff'; | ||
104 | $processedText = text2clickable($text, ''); | ||
105 | $this->assertEquals($expectedText, $processedText); | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * Test text2clickable a redirector set. | ||
110 | */ | ||
111 | public function testText2clickableWithRedirector() | ||
112 | { | ||
113 | $text = 'stuff http://hello.there/is=someone#here otherstuff'; | ||
114 | $redirector = 'http://redirector.to'; | ||
115 | $expectedText = 'stuff <a href="'. | ||
116 | $redirector . | ||
117 | urlencode('http://hello.there/is=someone#here') . | ||
118 | '">http://hello.there/is=someone#here</a> otherstuff'; | ||
119 | $processedText = text2clickable($text, $redirector); | ||
120 | $this->assertEquals($expectedText, $processedText); | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Test testSpace2nbsp. | ||
125 | */ | ||
126 | public function testSpace2nbsp() | ||
127 | { | ||
128 | $text = ' Are you thrilled by flags ?'. PHP_EOL .' Really?'; | ||
129 | $expectedText = ' Are you thrilled by flags ?'. PHP_EOL .' Really?'; | ||
130 | $processedText = space2nbsp($text); | ||
131 | $this->assertEquals($expectedText, $processedText); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Test hashtags auto-link. | ||
136 | */ | ||
137 | public function testHashtagAutolink() | ||
138 | { | ||
139 | $index = 'http://domain.tld/'; | ||
140 | $rawDescription = '#hashtag\n | ||
141 | # nothashtag\n | ||
142 | test#nothashtag #hashtag \#nothashtag\n | ||
143 | test #hashtag #hashtag test #hashtag.test\n | ||
144 | #hashtag #hashtag-nothashtag #hashtag_hashtag\n | ||
145 | What is #ашок anyway?\n | ||
146 | カタカナ #カタカナ」カタカナ\n'; | ||
147 | $autolinkedDescription = hashtag_autolink($rawDescription, $index); | ||
148 | |||
149 | $this->assertContains($this->getHashtagLink('hashtag', $index), $autolinkedDescription); | ||
150 | $this->assertNotContains(' #hashtag', $autolinkedDescription); | ||
151 | $this->assertNotContains('>#nothashtag', $autolinkedDescription); | ||
152 | $this->assertContains($this->getHashtagLink('ашок', $index), $autolinkedDescription); | ||
153 | $this->assertContains($this->getHashtagLink('カタカナ', $index), $autolinkedDescription); | ||
154 | $this->assertContains($this->getHashtagLink('hashtag_hashtag', $index), $autolinkedDescription); | ||
155 | $this->assertNotContains($this->getHashtagLink('hashtag-nothashtag', $index), $autolinkedDescription); | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * Test hashtags auto-link without index URL. | ||
160 | */ | ||
161 | public function testHashtagAutolinkNoIndex() | ||
162 | { | ||
163 | $rawDescription = 'blabla #hashtag x#nothashtag'; | ||
164 | $autolinkedDescription = hashtag_autolink($rawDescription); | ||
165 | |||
166 | $this->assertContains($this->getHashtagLink('hashtag'), $autolinkedDescription); | ||
167 | $this->assertNotContains(' #hashtag', $autolinkedDescription); | ||
168 | $this->assertNotContains('>#nothashtag', $autolinkedDescription); | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * Util function to build an hashtag link. | ||
173 | * | ||
174 | * @param string $hashtag Hashtag name. | ||
175 | * @param string $index Index URL. | ||
176 | * | ||
177 | * @return string HTML hashtag link. | ||
178 | */ | ||
179 | private function getHashtagLink($hashtag, $index = '') | ||
180 | { | ||
181 | $hashtagLink = '<a href="'. $index .'?addtag=$1" title="Hashtag $1">#$1</a>'; | ||
182 | return str_replace('$1', $hashtag, $hashtagLink); | ||
183 | } | ||
96 | } | 184 | } |
diff --git a/tests/NetscapeBookmarkUtilsTest.php b/tests/NetscapeBookmarkUtils/BookmarkExportTest.php index 41e6d84c..6a47bbb9 100644 --- a/tests/NetscapeBookmarkUtilsTest.php +++ b/tests/NetscapeBookmarkUtils/BookmarkExportTest.php | |||
@@ -3,9 +3,9 @@ | |||
3 | require_once 'application/NetscapeBookmarkUtils.php'; | 3 | require_once 'application/NetscapeBookmarkUtils.php'; |
4 | 4 | ||
5 | /** | 5 | /** |
6 | * Netscape bookmark import and export | 6 | * Netscape bookmark export |
7 | */ | 7 | */ |
8 | class NetscapeBookmarkUtilsTest extends PHPUnit_Framework_TestCase | 8 | class BookmarkExportTest extends PHPUnit_Framework_TestCase |
9 | { | 9 | { |
10 | /** | 10 | /** |
11 | * @var string datastore to test write operations | 11 | * @var string datastore to test write operations |
@@ -50,7 +50,7 @@ class NetscapeBookmarkUtilsTest 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 NetscapeBookmarkUtilsTest 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 NetscapeBookmarkUtilsTest 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 new file mode 100644 index 00000000..0ca07eac --- /dev/null +++ b/tests/NetscapeBookmarkUtils/BookmarkImportTest.php | |||
@@ -0,0 +1,590 @@ | |||
1 | <?php | ||
2 | |||
3 | require_once 'application/NetscapeBookmarkUtils.php'; | ||
4 | |||
5 | |||
6 | /** | ||
7 | * Utility function to load a file's metadata in a $_FILES-like array | ||
8 | * | ||
9 | * @param string $filename Basename of the file | ||
10 | * | ||
11 | * @return array A $_FILES-like array | ||
12 | */ | ||
13 | function file2array($filename) | ||
14 | { | ||
15 | return array( | ||
16 | 'filetoupload' => array( | ||
17 | 'name' => $filename, | ||
18 | 'tmp_name' => __DIR__ . '/input/' . $filename, | ||
19 | 'size' => filesize(__DIR__ . '/input/' . $filename) | ||
20 | ) | ||
21 | ); | ||
22 | } | ||
23 | |||
24 | |||
25 | /** | ||
26 | * Netscape bookmark import | ||
27 | */ | ||
28 | class BookmarkImportTest extends PHPUnit_Framework_TestCase | ||
29 | { | ||
30 | /** | ||
31 | * @var string datastore to test write operations | ||
32 | */ | ||
33 | protected static $testDatastore = 'sandbox/datastore.php'; | ||
34 | |||
35 | /** | ||
36 | * @var LinkDB private LinkDB instance | ||
37 | */ | ||
38 | protected $linkDb = null; | ||
39 | |||
40 | /** | ||
41 | * @var string Dummy page cache | ||
42 | */ | ||
43 | protected $pagecache = 'tests'; | ||
44 | |||
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 | /** | ||
58 | * Resets test data before each test | ||
59 | */ | ||
60 | protected function setUp() | ||
61 | { | ||
62 | if (file_exists(self::$testDatastore)) { | ||
63 | unlink(self::$testDatastore); | ||
64 | } | ||
65 | // start with an empty datastore | ||
66 | file_put_contents(self::$testDatastore, '<?php /* S7QysKquBQA= */ ?>'); | ||
67 | $this->linkDb = new LinkDB(self::$testDatastore, true, false); | ||
68 | } | ||
69 | |||
70 | public static function tearDownAfterClass() | ||
71 | { | ||
72 | date_default_timezone_set(self::$defaultTimeZone); | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * Attempt to import bookmarks from an empty file | ||
77 | */ | ||
78 | public function testImportEmptyData() | ||
79 | { | ||
80 | $files = file2array('empty.htm'); | ||
81 | $this->assertEquals( | ||
82 | 'File empty.htm (0 bytes) has an unknown file format.' | ||
83 | .' Nothing was imported.', | ||
84 | NetscapeBookmarkUtils::import(NULL, $files, NULL, NULL) | ||
85 | ); | ||
86 | $this->assertEquals(0, count($this->linkDb)); | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * Attempt to import bookmarks from a file with no Doctype | ||
91 | */ | ||
92 | public function testImportNoDoctype() | ||
93 | { | ||
94 | $files = file2array('no_doctype.htm'); | ||
95 | $this->assertEquals( | ||
96 | 'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.', | ||
97 | NetscapeBookmarkUtils::import(NULL, $files, NULL, NULL) | ||
98 | ); | ||
99 | $this->assertEquals(0, count($this->linkDb)); | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * Ensure IE dumps are supported | ||
104 | */ | ||
105 | public function testImportInternetExplorerEncoding() | ||
106 | { | ||
107 | $files = file2array('internet_explorer_encoding.htm'); | ||
108 | $this->assertEquals( | ||
109 | 'File internet_explorer_encoding.htm (356 bytes) was successfully processed:' | ||
110 | .' 1 links imported, 0 links overwritten, 0 links skipped.', | ||
111 | NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->pagecache) | ||
112 | ); | ||
113 | $this->assertEquals(1, count($this->linkDb)); | ||
114 | $this->assertEquals(0, count_private($this->linkDb)); | ||
115 | |||
116 | $this->assertEquals( | ||
117 | array( | ||
118 | 'id' => 0, | ||
119 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160618_203944'), | ||
120 | 'title' => 'Hg Init a Mercurial tutorial by Joel Spolsky', | ||
121 | 'url' => 'http://hginit.com/', | ||
122 | 'description' => '', | ||
123 | 'private' => 0, | ||
124 | 'tags' => '', | ||
125 | 'shorturl' => 'La37cg', | ||
126 | ), | ||
127 | $this->linkDb->getLinkFromUrl('http://hginit.com/') | ||
128 | ); | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * Import bookmarks nested in a folder hierarchy | ||
133 | */ | ||
134 | public function testImportNested() | ||
135 | { | ||
136 | $files = file2array('netscape_nested.htm'); | ||
137 | $this->assertEquals( | ||
138 | 'File netscape_nested.htm (1337 bytes) was successfully processed:' | ||
139 | .' 8 links imported, 0 links overwritten, 0 links skipped.', | ||
140 | NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->pagecache) | ||
141 | ); | ||
142 | $this->assertEquals(8, count($this->linkDb)); | ||
143 | $this->assertEquals(2, count_private($this->linkDb)); | ||
144 | |||
145 | $this->assertEquals( | ||
146 | array( | ||
147 | 'id' => 0, | ||
148 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235541'), | ||
149 | 'title' => 'Nested 1', | ||
150 | 'url' => 'http://nest.ed/1', | ||
151 | 'description' => '', | ||
152 | 'private' => 0, | ||
153 | 'tags' => 'tag1 tag2', | ||
154 | 'shorturl' => 'KyDNKA', | ||
155 | ), | ||
156 | $this->linkDb->getLinkFromUrl('http://nest.ed/1') | ||
157 | ); | ||
158 | $this->assertEquals( | ||
159 | array( | ||
160 | 'id' => 1, | ||
161 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235542'), | ||
162 | 'title' => 'Nested 1-1', | ||
163 | 'url' => 'http://nest.ed/1-1', | ||
164 | 'description' => '', | ||
165 | 'private' => 0, | ||
166 | 'tags' => 'folder1 tag1 tag2', | ||
167 | 'shorturl' => 'T2LnXg', | ||
168 | ), | ||
169 | $this->linkDb->getLinkFromUrl('http://nest.ed/1-1') | ||
170 | ); | ||
171 | $this->assertEquals( | ||
172 | array( | ||
173 | 'id' => 2, | ||
174 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235547'), | ||
175 | 'title' => 'Nested 1-2', | ||
176 | 'url' => 'http://nest.ed/1-2', | ||
177 | 'description' => '', | ||
178 | 'private' => 0, | ||
179 | 'tags' => 'folder1 tag3 tag4', | ||
180 | 'shorturl' => '46SZxA', | ||
181 | ), | ||
182 | $this->linkDb->getLinkFromUrl('http://nest.ed/1-2') | ||
183 | ); | ||
184 | $this->assertEquals( | ||
185 | array( | ||
186 | 'id' => 3, | ||
187 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'), | ||
188 | 'title' => 'Nested 2-1', | ||
189 | 'url' => 'http://nest.ed/2-1', | ||
190 | 'description' => 'First link of the second section', | ||
191 | 'private' => 1, | ||
192 | 'tags' => 'folder2', | ||
193 | 'shorturl' => '4UHOSw', | ||
194 | ), | ||
195 | $this->linkDb->getLinkFromUrl('http://nest.ed/2-1') | ||
196 | ); | ||
197 | $this->assertEquals( | ||
198 | array( | ||
199 | 'id' => 4, | ||
200 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'), | ||
201 | 'title' => 'Nested 2-2', | ||
202 | 'url' => 'http://nest.ed/2-2', | ||
203 | 'description' => 'Second link of the second section', | ||
204 | 'private' => 1, | ||
205 | 'tags' => 'folder2', | ||
206 | 'shorturl' => 'yfzwbw', | ||
207 | ), | ||
208 | $this->linkDb->getLinkFromUrl('http://nest.ed/2-2') | ||
209 | ); | ||
210 | $this->assertEquals( | ||
211 | array( | ||
212 | 'id' => 5, | ||
213 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'), | ||
214 | 'title' => 'Nested 3-1', | ||
215 | 'url' => 'http://nest.ed/3-1', | ||
216 | 'description' => '', | ||
217 | 'private' => 0, | ||
218 | 'tags' => 'folder3 folder3-1 tag3', | ||
219 | 'shorturl' => 'UwxIUQ', | ||
220 | ), | ||
221 | $this->linkDb->getLinkFromUrl('http://nest.ed/3-1') | ||
222 | ); | ||
223 | $this->assertEquals( | ||
224 | array( | ||
225 | 'id' => 6, | ||
226 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'), | ||
227 | 'title' => 'Nested 3-2', | ||
228 | 'url' => 'http://nest.ed/3-2', | ||
229 | 'description' => '', | ||
230 | 'private' => 0, | ||
231 | 'tags' => 'folder3 folder3-1', | ||
232 | 'shorturl' => 'p8dyZg', | ||
233 | ), | ||
234 | $this->linkDb->getLinkFromUrl('http://nest.ed/3-2') | ||
235 | ); | ||
236 | $this->assertEquals( | ||
237 | array( | ||
238 | 'id' => 7, | ||
239 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160229_111541'), | ||
240 | 'title' => 'Nested 2', | ||
241 | 'url' => 'http://nest.ed/2', | ||
242 | 'description' => '', | ||
243 | 'private' => 0, | ||
244 | 'tags' => 'tag4', | ||
245 | 'shorturl' => 'Gt3Uug', | ||
246 | ), | ||
247 | $this->linkDb->getLinkFromUrl('http://nest.ed/2') | ||
248 | ); | ||
249 | } | ||
250 | |||
251 | /** | ||
252 | * Import bookmarks with the default privacy setting (reuse from file) | ||
253 | * | ||
254 | * The $_POST array is not set. | ||
255 | */ | ||
256 | public function testImportDefaultPrivacyNoPost() | ||
257 | { | ||
258 | $files = file2array('netscape_basic.htm'); | ||
259 | $this->assertEquals( | ||
260 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
261 | .' 2 links imported, 0 links overwritten, 0 links skipped.', | ||
262 | NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->pagecache) | ||
263 | ); | ||
264 | |||
265 | $this->assertEquals(2, count($this->linkDb)); | ||
266 | $this->assertEquals(1, count_private($this->linkDb)); | ||
267 | |||
268 | $this->assertEquals( | ||
269 | array( | ||
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'), | ||
273 | 'title' => 'Secret stuff', | ||
274 | 'url' => 'https://private.tld', | ||
275 | 'description' => "Super-secret stuff you're not supposed to know about", | ||
276 | 'private' => 1, | ||
277 | 'tags' => 'private secret', | ||
278 | 'shorturl' => 'EokDtA', | ||
279 | ), | ||
280 | $this->linkDb->getLinkFromUrl('https://private.tld') | ||
281 | ); | ||
282 | $this->assertEquals( | ||
283 | array( | ||
284 | 'id' => 1, | ||
285 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'), | ||
286 | 'title' => 'Public stuff', | ||
287 | 'url' => 'http://public.tld', | ||
288 | 'description' => '', | ||
289 | 'private' => 0, | ||
290 | 'tags' => 'public hello world', | ||
291 | 'shorturl' => 'Er9ddA', | ||
292 | ), | ||
293 | $this->linkDb->getLinkFromUrl('http://public.tld') | ||
294 | ); | ||
295 | } | ||
296 | |||
297 | /** | ||
298 | * Import bookmarks with the default privacy setting (reuse from file) | ||
299 | */ | ||
300 | public function testImportKeepPrivacy() | ||
301 | { | ||
302 | $post = array('privacy' => 'default'); | ||
303 | $files = file2array('netscape_basic.htm'); | ||
304 | $this->assertEquals( | ||
305 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
306 | .' 2 links imported, 0 links overwritten, 0 links skipped.', | ||
307 | NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache) | ||
308 | ); | ||
309 | $this->assertEquals(2, count($this->linkDb)); | ||
310 | $this->assertEquals(1, count_private($this->linkDb)); | ||
311 | |||
312 | $this->assertEquals( | ||
313 | array( | ||
314 | 'id' => 0, | ||
315 | // Note that TZ in the import file is ignored. | ||
316 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20001010_135536'), | ||
317 | 'title' => 'Secret stuff', | ||
318 | 'url' => 'https://private.tld', | ||
319 | 'description' => "Super-secret stuff you're not supposed to know about", | ||
320 | 'private' => 1, | ||
321 | 'tags' => 'private secret', | ||
322 | 'shorturl' => 'EokDtA', | ||
323 | ), | ||
324 | $this->linkDb->getLinkFromUrl('https://private.tld') | ||
325 | ); | ||
326 | $this->assertEquals( | ||
327 | array( | ||
328 | 'id' => 1, | ||
329 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'), | ||
330 | 'title' => 'Public stuff', | ||
331 | 'url' => 'http://public.tld', | ||
332 | 'description' => '', | ||
333 | 'private' => 0, | ||
334 | 'tags' => 'public hello world', | ||
335 | 'shorturl' => 'Er9ddA', | ||
336 | ), | ||
337 | $this->linkDb->getLinkFromUrl('http://public.tld') | ||
338 | ); | ||
339 | } | ||
340 | |||
341 | /** | ||
342 | * Import links as public | ||
343 | */ | ||
344 | public function testImportAsPublic() | ||
345 | { | ||
346 | $post = array('privacy' => 'public'); | ||
347 | $files = file2array('netscape_basic.htm'); | ||
348 | $this->assertEquals( | ||
349 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
350 | .' 2 links imported, 0 links overwritten, 0 links skipped.', | ||
351 | NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache) | ||
352 | ); | ||
353 | $this->assertEquals(2, count($this->linkDb)); | ||
354 | $this->assertEquals(0, count_private($this->linkDb)); | ||
355 | $this->assertEquals( | ||
356 | 0, | ||
357 | $this->linkDb[0]['private'] | ||
358 | ); | ||
359 | $this->assertEquals( | ||
360 | 0, | ||
361 | $this->linkDb[1]['private'] | ||
362 | ); | ||
363 | } | ||
364 | |||
365 | /** | ||
366 | * Import links as private | ||
367 | */ | ||
368 | public function testImportAsPrivate() | ||
369 | { | ||
370 | $post = array('privacy' => 'private'); | ||
371 | $files = file2array('netscape_basic.htm'); | ||
372 | $this->assertEquals( | ||
373 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
374 | .' 2 links imported, 0 links overwritten, 0 links skipped.', | ||
375 | NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache) | ||
376 | ); | ||
377 | $this->assertEquals(2, count($this->linkDb)); | ||
378 | $this->assertEquals(2, count_private($this->linkDb)); | ||
379 | $this->assertEquals( | ||
380 | 1, | ||
381 | $this->linkDb['0']['private'] | ||
382 | ); | ||
383 | $this->assertEquals( | ||
384 | 1, | ||
385 | $this->linkDb['1']['private'] | ||
386 | ); | ||
387 | } | ||
388 | |||
389 | /** | ||
390 | * Overwrite private links so they become public | ||
391 | */ | ||
392 | public function testOverwriteAsPublic() | ||
393 | { | ||
394 | $files = file2array('netscape_basic.htm'); | ||
395 | |||
396 | // import links as private | ||
397 | $post = array('privacy' => 'private'); | ||
398 | $this->assertEquals( | ||
399 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
400 | .' 2 links imported, 0 links overwritten, 0 links skipped.', | ||
401 | NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache) | ||
402 | ); | ||
403 | $this->assertEquals(2, count($this->linkDb)); | ||
404 | $this->assertEquals(2, count_private($this->linkDb)); | ||
405 | $this->assertEquals( | ||
406 | 1, | ||
407 | $this->linkDb[0]['private'] | ||
408 | ); | ||
409 | $this->assertEquals( | ||
410 | 1, | ||
411 | $this->linkDb[1]['private'] | ||
412 | ); | ||
413 | // re-import as public, enable overwriting | ||
414 | $post = array( | ||
415 | 'privacy' => 'public', | ||
416 | 'overwrite' => 'true' | ||
417 | ); | ||
418 | $this->assertEquals( | ||
419 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
420 | .' 2 links imported, 2 links overwritten, 0 links skipped.', | ||
421 | NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache) | ||
422 | ); | ||
423 | $this->assertEquals(2, count($this->linkDb)); | ||
424 | $this->assertEquals(0, count_private($this->linkDb)); | ||
425 | $this->assertEquals( | ||
426 | 0, | ||
427 | $this->linkDb[0]['private'] | ||
428 | ); | ||
429 | $this->assertEquals( | ||
430 | 0, | ||
431 | $this->linkDb[1]['private'] | ||
432 | ); | ||
433 | } | ||
434 | |||
435 | /** | ||
436 | * Overwrite public links so they become private | ||
437 | */ | ||
438 | public function testOverwriteAsPrivate() | ||
439 | { | ||
440 | $files = file2array('netscape_basic.htm'); | ||
441 | |||
442 | // import links as public | ||
443 | $post = array('privacy' => 'public'); | ||
444 | $this->assertEquals( | ||
445 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
446 | .' 2 links imported, 0 links overwritten, 0 links skipped.', | ||
447 | NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache) | ||
448 | ); | ||
449 | $this->assertEquals(2, count($this->linkDb)); | ||
450 | $this->assertEquals(0, count_private($this->linkDb)); | ||
451 | $this->assertEquals( | ||
452 | 0, | ||
453 | $this->linkDb['0']['private'] | ||
454 | ); | ||
455 | $this->assertEquals( | ||
456 | 0, | ||
457 | $this->linkDb['1']['private'] | ||
458 | ); | ||
459 | |||
460 | // re-import as private, enable overwriting | ||
461 | $post = array( | ||
462 | 'privacy' => 'private', | ||
463 | 'overwrite' => 'true' | ||
464 | ); | ||
465 | $this->assertEquals( | ||
466 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
467 | .' 2 links imported, 2 links overwritten, 0 links skipped.', | ||
468 | NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache) | ||
469 | ); | ||
470 | $this->assertEquals(2, count($this->linkDb)); | ||
471 | $this->assertEquals(2, count_private($this->linkDb)); | ||
472 | $this->assertEquals( | ||
473 | 1, | ||
474 | $this->linkDb['0']['private'] | ||
475 | ); | ||
476 | $this->assertEquals( | ||
477 | 1, | ||
478 | $this->linkDb['1']['private'] | ||
479 | ); | ||
480 | } | ||
481 | |||
482 | /** | ||
483 | * Attept to import the same links twice without enabling overwriting | ||
484 | */ | ||
485 | public function testSkipOverwrite() | ||
486 | { | ||
487 | $post = array('privacy' => 'public'); | ||
488 | $files = file2array('netscape_basic.htm'); | ||
489 | $this->assertEquals( | ||
490 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
491 | .' 2 links imported, 0 links overwritten, 0 links skipped.', | ||
492 | NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache) | ||
493 | ); | ||
494 | $this->assertEquals(2, count($this->linkDb)); | ||
495 | $this->assertEquals(0, count_private($this->linkDb)); | ||
496 | |||
497 | // re-import as private, DO NOT enable overwriting | ||
498 | $post = array('privacy' => 'private'); | ||
499 | $this->assertEquals( | ||
500 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
501 | .' 0 links imported, 0 links overwritten, 2 links skipped.', | ||
502 | NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache) | ||
503 | ); | ||
504 | $this->assertEquals(2, count($this->linkDb)); | ||
505 | $this->assertEquals(0, count_private($this->linkDb)); | ||
506 | } | ||
507 | |||
508 | /** | ||
509 | * Add user-specified tags to all imported bookmarks | ||
510 | */ | ||
511 | public function testSetDefaultTags() | ||
512 | { | ||
513 | $post = array( | ||
514 | 'privacy' => 'public', | ||
515 | 'default_tags' => 'tag1,tag2 tag3' | ||
516 | ); | ||
517 | $files = file2array('netscape_basic.htm'); | ||
518 | $this->assertEquals( | ||
519 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
520 | .' 2 links imported, 0 links overwritten, 0 links skipped.', | ||
521 | NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache) | ||
522 | ); | ||
523 | $this->assertEquals(2, count($this->linkDb)); | ||
524 | $this->assertEquals(0, count_private($this->linkDb)); | ||
525 | $this->assertEquals( | ||
526 | 'tag1 tag2 tag3 private secret', | ||
527 | $this->linkDb['0']['tags'] | ||
528 | ); | ||
529 | $this->assertEquals( | ||
530 | 'tag1 tag2 tag3 public hello world', | ||
531 | $this->linkDb['1']['tags'] | ||
532 | ); | ||
533 | } | ||
534 | |||
535 | /** | ||
536 | * The user-specified tags contain characters to be escaped | ||
537 | */ | ||
538 | public function testSanitizeDefaultTags() | ||
539 | { | ||
540 | $post = array( | ||
541 | 'privacy' => 'public', | ||
542 | 'default_tags' => 'tag1&,tag2 "tag3"' | ||
543 | ); | ||
544 | $files = file2array('netscape_basic.htm'); | ||
545 | $this->assertEquals( | ||
546 | 'File netscape_basic.htm (482 bytes) was successfully processed:' | ||
547 | .' 2 links imported, 0 links overwritten, 0 links skipped.', | ||
548 | NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->pagecache) | ||
549 | ); | ||
550 | $this->assertEquals(2, count($this->linkDb)); | ||
551 | $this->assertEquals(0, count_private($this->linkDb)); | ||
552 | $this->assertEquals( | ||
553 | 'tag1& tag2 "tag3" private secret', | ||
554 | $this->linkDb['0']['tags'] | ||
555 | ); | ||
556 | $this->assertEquals( | ||
557 | 'tag1& tag2 "tag3" public hello world', | ||
558 | $this->linkDb['1']['tags'] | ||
559 | ); | ||
560 | } | ||
561 | |||
562 | /** | ||
563 | * Ensure each imported bookmark has a unique id | ||
564 | * | ||
565 | * See https://github.com/shaarli/Shaarli/issues/351 | ||
566 | */ | ||
567 | public function testImportSameDate() | ||
568 | { | ||
569 | $files = file2array('same_date.htm'); | ||
570 | $this->assertEquals( | ||
571 | 'File same_date.htm (453 bytes) was successfully processed:' | ||
572 | .' 3 links imported, 0 links overwritten, 0 links skipped.', | ||
573 | NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->pagecache) | ||
574 | ); | ||
575 | $this->assertEquals(3, count($this->linkDb)); | ||
576 | $this->assertEquals(0, count_private($this->linkDb)); | ||
577 | $this->assertEquals( | ||
578 | 0, | ||
579 | $this->linkDb[0]['id'] | ||
580 | ); | ||
581 | $this->assertEquals( | ||
582 | 1, | ||
583 | $this->linkDb[1]['id'] | ||
584 | ); | ||
585 | $this->assertEquals( | ||
586 | 2, | ||
587 | $this->linkDb[2]['id'] | ||
588 | ); | ||
589 | } | ||
590 | } | ||
diff --git a/tests/NetscapeBookmarkUtils/input/empty.htm b/tests/NetscapeBookmarkUtils/input/empty.htm new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/NetscapeBookmarkUtils/input/empty.htm | |||
diff --git a/tests/NetscapeBookmarkUtils/input/internet_explorer_encoding.htm b/tests/NetscapeBookmarkUtils/input/internet_explorer_encoding.htm new file mode 100644 index 00000000..18703cf6 --- /dev/null +++ b/tests/NetscapeBookmarkUtils/input/internet_explorer_encoding.htm | |||
@@ -0,0 +1,9 @@ | |||
1 | <!DOCTYPE NETSCAPE-Bookmark-file-1> | ||
2 | <!-- This is an automatically generated file. | ||
3 | It will be read and overwritten. | ||
4 | Do Not Edit! --> | ||
5 | <TITLE>Bookmarks</TITLE> | ||
6 | <H1>Bookmarks</H1> | ||
7 | <DL><p> | ||
8 | <DT><A HREF="http://hginit.com/" ADD_DATE="1466271584" LAST_VISIT="1466271584" LAST_MODIFIED="1466271584" >Hg Init a Mercurial tutorial by Joel Spolsky</A> | ||
9 | </DL><p> | ||
diff --git a/tests/NetscapeBookmarkUtils/input/netscape_basic.htm b/tests/NetscapeBookmarkUtils/input/netscape_basic.htm new file mode 100644 index 00000000..affe0cf8 --- /dev/null +++ b/tests/NetscapeBookmarkUtils/input/netscape_basic.htm | |||
@@ -0,0 +1,11 @@ | |||
1 | <!DOCTYPE NETSCAPE-Bookmark-file-1> | ||
2 | <!-- This is an automatically generated file. | ||
3 | It will be read and overwritten. | ||
4 | Do Not Edit! --> | ||
5 | <TITLE>Bookmarks</TITLE> | ||
6 | <H1>Bookmarks</H1> | ||
7 | <DL><p> | ||
8 | <DT><A HREF="https://private.tld" ADD_DATE="10/Oct/2000:13:55:36 +0300" PRIVATE="1" TAGS="private secret">Secret stuff</A> | ||
9 | <DD>Super-secret stuff you're not supposed to know about | ||
10 | <DT><A HREF="http://public.tld" ADD_DATE="1456433748" PRIVATE="0" TAGS="public hello world">Public stuff</A> | ||
11 | </DL><p> | ||
diff --git a/tests/NetscapeBookmarkUtils/input/netscape_nested.htm b/tests/NetscapeBookmarkUtils/input/netscape_nested.htm new file mode 100644 index 00000000..b486fe18 --- /dev/null +++ b/tests/NetscapeBookmarkUtils/input/netscape_nested.htm | |||
@@ -0,0 +1,31 @@ | |||
1 | <!DOCTYPE NETSCAPE-Bookmark-file-1> | ||
2 | <!-- This is an automatically generated file. | ||
3 | It will be read and overwritten. | ||
4 | Do Not Edit! --> | ||
5 | <TITLE>Bookmarks</TITLE> | ||
6 | <H1>Bookmarks</H1> | ||
7 | <DL><p> | ||
8 | <DT><A HREF="http://nest.ed/1" ADD_DATE="1456433741" PRIVATE="0" TAGS="tag1,tag2">Nested 1</A> | ||
9 | <DT><H3 ADD_DATE="1456433722" LAST_MODIFIED="1456433739">Folder1</H3> | ||
10 | <DL><p> | ||
11 | <DT><A HREF="http://nest.ed/1-1" ADD_DATE="1456433742" PRIVATE="0" TAGS="tag1,tag2">Nested 1-1</A> | ||
12 | <DT><A HREF="http://nest.ed/1-2" ADD_DATE="1456433747" PRIVATE="0" TAGS="tag3,tag4">Nested 1-2</A> | ||
13 | </DL><p> | ||
14 | <DT><H3 ADD_DATE="1456433722">Folder2</H3> | ||
15 | <DD>This second folder contains wonderful links! | ||
16 | <DL><p> | ||
17 | <DT><A HREF="http://nest.ed/2-1" ADD_DATE="1454433742" PRIVATE="1">Nested 2-1</A> | ||
18 | <DD>First link of the second section | ||
19 | <DT><A HREF="http://nest.ed/2-2" ADD_DATE="1453233747" PRIVATE="1">Nested 2-2</A> | ||
20 | <DD>Second link of the second section | ||
21 | </DL><p> | ||
22 | <DT><H3>Folder3</H3> | ||
23 | <DL><p> | ||
24 | <DT><H3>Folder3-1</H3> | ||
25 | <DL><p> | ||
26 | <DT><A HREF="http://nest.ed/3-1" ADD_DATE="1454433742" PRIVATE="0" TAGS="tag3">Nested 3-1</A> | ||
27 | <DT><A HREF="http://nest.ed/3-2" ADD_DATE="1453233747" PRIVATE="0">Nested 3-2</A> | ||
28 | </DL><p> | ||
29 | </DL><p> | ||
30 | <DT><A HREF="http://nest.ed/2" ADD_DATE="1456733741" PRIVATE="0" TAGS="tag4">Nested 2</A> | ||
31 | </DL><p> | ||
diff --git a/tests/NetscapeBookmarkUtils/input/no_doctype.htm b/tests/NetscapeBookmarkUtils/input/no_doctype.htm new file mode 100644 index 00000000..766d398b --- /dev/null +++ b/tests/NetscapeBookmarkUtils/input/no_doctype.htm | |||
@@ -0,0 +1,7 @@ | |||
1 | <TITLE>Bookmarks</TITLE> | ||
2 | <H1>Bookmarks</H1> | ||
3 | <DL><p> | ||
4 | <DT><A HREF="https://private.tld" ADD_DATE="10/Oct/2000:13:55:36 +0300" PRIVATE="1" TAGS="private secret">Secret stuff</A> | ||
5 | <DD>Super-secret stuff you're not supposed to know about | ||
6 | <DT><A HREF="http://public.tld" ADD_DATE="1456433748" PRIVATE="0" TAGS="public hello world">Public stuff</A> | ||
7 | </DL><p> | ||
diff --git a/tests/NetscapeBookmarkUtils/input/same_date.htm b/tests/NetscapeBookmarkUtils/input/same_date.htm new file mode 100644 index 00000000..9d58a582 --- /dev/null +++ b/tests/NetscapeBookmarkUtils/input/same_date.htm | |||
@@ -0,0 +1,11 @@ | |||
1 | <!DOCTYPE NETSCAPE-Bookmark-file-1> | ||
2 | <!-- This is an automatically generated file. | ||
3 | It will be read and overwritten. | ||
4 | Do Not Edit! --> | ||
5 | <TITLE>Bookmarks</TITLE> | ||
6 | <H1>Bookmarks</H1> | ||
7 | <DL><p> | ||
8 | <DT><A HREF="https://fir.st" ADD_DATE="1456433748" PRIVATE="0">Today's first link</A> | ||
9 | <DT><A HREF="https://seco.nd" ADD_DATE="1456433748" PRIVATE="0">Today's second link</A> | ||
10 | <DT><A HREF="https://thi.rd" ADD_DATE="1456433748" PRIVATE="0">Today's third link</A> | ||
11 | </DL><p> | ||
diff --git a/tests/PluginManagerTest.php b/tests/PluginManagerTest.php index 348082c7..ddf48185 100644 --- a/tests/PluginManagerTest.php +++ b/tests/PluginManagerTest.php | |||
@@ -24,29 +24,38 @@ class PluginManagerTest extends PHPUnit_Framework_TestCase | |||
24 | private static $pluginName = 'test'; | 24 | private static $pluginName = 'test'; |
25 | 25 | ||
26 | /** | 26 | /** |
27 | * @var PluginManager $pluginManager Plugin Mananger instance. | ||
28 | */ | ||
29 | protected $pluginManager; | ||
30 | |||
31 | public function setUp() | ||
32 | { | ||
33 | $conf = new ConfigManager(''); | ||
34 | $this->pluginManager = new PluginManager($conf); | ||
35 | } | ||
36 | |||
37 | /** | ||
27 | * Test plugin loading and hook execution. | 38 | * Test plugin loading and hook execution. |
28 | * | 39 | * |
29 | * @return void | 40 | * @return void |
30 | */ | 41 | */ |
31 | public function testPlugin() | 42 | public function testPlugin() |
32 | { | 43 | { |
33 | $pluginManager = PluginManager::getInstance(); | ||
34 | |||
35 | PluginManager::$PLUGINS_PATH = self::$pluginPath; | 44 | PluginManager::$PLUGINS_PATH = self::$pluginPath; |
36 | $pluginManager->load(array(self::$pluginName)); | 45 | $this->pluginManager->load(array(self::$pluginName)); |
37 | 46 | ||
38 | $this->assertTrue(function_exists('hook_test_random')); | 47 | $this->assertTrue(function_exists('hook_test_random')); |
39 | 48 | ||
40 | $data = array(0 => 'woot'); | 49 | $data = array(0 => 'woot'); |
41 | $pluginManager->executeHooks('random', $data); | 50 | $this->pluginManager->executeHooks('random', $data); |
42 | $this->assertEquals('woot', $data[1]); | 51 | $this->assertEquals('woot', $data[1]); |
43 | 52 | ||
44 | $data = array(0 => 'woot'); | 53 | $data = array(0 => 'woot'); |
45 | $pluginManager->executeHooks('random', $data, array('target' => 'test')); | 54 | $this->pluginManager->executeHooks('random', $data, array('target' => 'test')); |
46 | $this->assertEquals('page test', $data[1]); | 55 | $this->assertEquals('page test', $data[1]); |
47 | 56 | ||
48 | $data = array(0 => 'woot'); | 57 | $data = array(0 => 'woot'); |
49 | $pluginManager->executeHooks('random', $data, array('loggedin' => true)); | 58 | $this->pluginManager->executeHooks('random', $data, array('loggedin' => true)); |
50 | $this->assertEquals('loggedin', $data[1]); | 59 | $this->assertEquals('loggedin', $data[1]); |
51 | } | 60 | } |
52 | 61 | ||
@@ -57,11 +66,8 @@ class PluginManagerTest extends PHPUnit_Framework_TestCase | |||
57 | */ | 66 | */ |
58 | public function testPluginNotFound() | 67 | public function testPluginNotFound() |
59 | { | 68 | { |
60 | $pluginManager = PluginManager::getInstance(); | 69 | $this->pluginManager->load(array()); |
61 | 70 | $this->pluginManager->load(array('nope', 'renope')); | |
62 | $pluginManager->load(array()); | ||
63 | |||
64 | $pluginManager->load(array('nope', 'renope')); | ||
65 | } | 71 | } |
66 | 72 | ||
67 | /** | 73 | /** |
@@ -69,17 +75,21 @@ class PluginManagerTest extends PHPUnit_Framework_TestCase | |||
69 | */ | 75 | */ |
70 | public function testGetPluginsMeta() | 76 | public function testGetPluginsMeta() |
71 | { | 77 | { |
72 | $pluginManager = PluginManager::getInstance(); | ||
73 | |||
74 | PluginManager::$PLUGINS_PATH = self::$pluginPath; | 78 | PluginManager::$PLUGINS_PATH = self::$pluginPath; |
75 | $pluginManager->load(array(self::$pluginName)); | 79 | $this->pluginManager->load(array(self::$pluginName)); |
76 | 80 | ||
77 | $expectedParameters = array( | 81 | $expectedParameters = array( |
78 | 'pop' => '', | 82 | 'pop' => array( |
79 | 'hip' => '', | 83 | 'value' => '', |
84 | 'desc' => 'pop description', | ||
85 | ), | ||
86 | 'hip' => array( | ||
87 | 'value' => '', | ||
88 | 'desc' => '', | ||
89 | ), | ||
80 | ); | 90 | ); |
81 | $meta = $pluginManager->getPluginsMeta(); | 91 | $meta = $this->pluginManager->getPluginsMeta(); |
82 | $this->assertEquals('test plugin', $meta[self::$pluginName]['description']); | 92 | $this->assertEquals('test plugin', $meta[self::$pluginName]['description']); |
83 | $this->assertEquals($expectedParameters, $meta[self::$pluginName]['parameters']); | 93 | $this->assertEquals($expectedParameters, $meta[self::$pluginName]['parameters']); |
84 | } | 94 | } |
85 | } \ No newline at end of file | 95 | } |
diff --git a/tests/Updater/DummyUpdater.php b/tests/Updater/DummyUpdater.php index e9ef2aaa..a0be4413 100644 --- a/tests/Updater/DummyUpdater.php +++ b/tests/Updater/DummyUpdater.php | |||
@@ -11,14 +11,14 @@ class DummyUpdater extends Updater | |||
11 | /** | 11 | /** |
12 | * Object constructor. | 12 | * Object constructor. |
13 | * | 13 | * |
14 | * @param array $doneUpdates Updates which are already done. | 14 | * @param array $doneUpdates Updates which are already done. |
15 | * @param array $config Shaarli's configuration array. | 15 | * @param LinkDB $linkDB LinkDB instance. |
16 | * @param LinkDB $linkDB LinkDB instance. | 16 | * @param ConfigManager $conf Configuration Manager instance. |
17 | * @param boolean $isLoggedIn True if the user is logged in. | 17 | * @param boolean $isLoggedIn True if the user is logged in. |
18 | */ | 18 | */ |
19 | public function __construct($doneUpdates, $config, $linkDB, $isLoggedIn) | 19 | public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn) |
20 | { | 20 | { |
21 | parent::__construct($doneUpdates, $config, $linkDB, $isLoggedIn); | 21 | parent::__construct($doneUpdates, $linkDB, $conf, $isLoggedIn); |
22 | 22 | ||
23 | // Retrieve all update methods. | 23 | // Retrieve all update methods. |
24 | // For unit test, only retrieve final methods, | 24 | // For unit test, only retrieve final methods, |
diff --git a/tests/Updater/UpdaterTest.php b/tests/Updater/UpdaterTest.php index a29d9067..a3e8a4d2 100644 --- a/tests/Updater/UpdaterTest.php +++ b/tests/Updater/UpdaterTest.php | |||
@@ -1,5 +1,6 @@ | |||
1 | <?php | 1 | <?php |
2 | 2 | ||
3 | require_once 'application/config/ConfigManager.php'; | ||
3 | require_once 'tests/Updater/DummyUpdater.php'; | 4 | require_once 'tests/Updater/DummyUpdater.php'; |
4 | 5 | ||
5 | /** | 6 | /** |
@@ -9,58 +10,26 @@ require_once 'tests/Updater/DummyUpdater.php'; | |||
9 | class UpdaterTest extends PHPUnit_Framework_TestCase | 10 | class UpdaterTest extends PHPUnit_Framework_TestCase |
10 | { | 11 | { |
11 | /** | 12 | /** |
12 | * @var array Configuration input set. | 13 | * @var string Path to test datastore. |
13 | */ | 14 | */ |
14 | private static $configFields; | 15 | protected static $testDatastore = 'sandbox/datastore.php'; |
15 | 16 | ||
16 | /** | 17 | /** |
17 | * @var string Path to test datastore. | 18 | * @var string Config file path (without extension). |
18 | */ | 19 | */ |
19 | protected static $testDatastore = 'sandbox/datastore.php'; | 20 | protected static $configFile = 'tests/utils/config/configJson'; |
20 | 21 | ||
21 | /** | 22 | /** |
22 | * Executed before each test. | 23 | * @var ConfigManager |
23 | */ | 24 | */ |
24 | public function setUp() | 25 | protected $conf; |
25 | { | ||
26 | self::$configFields = array( | ||
27 | 'login' => 'login', | ||
28 | 'hash' => 'hash', | ||
29 | 'salt' => 'salt', | ||
30 | 'timezone' => 'Europe/Paris', | ||
31 | 'title' => 'title', | ||
32 | 'titleLink' => 'titleLink', | ||
33 | 'redirector' => '', | ||
34 | 'disablesessionprotection' => false, | ||
35 | 'privateLinkByDefault' => false, | ||
36 | 'config' => array( | ||
37 | 'CONFIG_FILE' => 'tests/Updater/config.php', | ||
38 | 'DATADIR' => 'tests/Updater', | ||
39 | 'PAGECACHE' => 'sandbox/pagecache', | ||
40 | 'config1' => 'config1data', | ||
41 | 'config2' => 'config2data', | ||
42 | ) | ||
43 | ); | ||
44 | } | ||
45 | 26 | ||
46 | /** | 27 | /** |
47 | * Executed after each test. | 28 | * Executed before each test. |
48 | * | ||
49 | * @return void | ||
50 | */ | 29 | */ |
51 | public function tearDown() | 30 | public function setUp() |
52 | { | 31 | { |
53 | if (is_file(self::$configFields['config']['CONFIG_FILE'])) { | 32 | $this->conf = new ConfigManager(self::$configFile); |
54 | unlink(self::$configFields['config']['CONFIG_FILE']); | ||
55 | } | ||
56 | |||
57 | if (is_file(self::$configFields['config']['DATADIR'] . '/options.php')) { | ||
58 | unlink(self::$configFields['config']['DATADIR'] . '/options.php'); | ||
59 | } | ||
60 | |||
61 | if (is_file(self::$configFields['config']['DATADIR'] . '/updates.json')) { | ||
62 | unlink(self::$configFields['config']['DATADIR'] . '/updates.json'); | ||
63 | } | ||
64 | } | 33 | } |
65 | 34 | ||
66 | /** | 35 | /** |
@@ -69,9 +38,10 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
69 | public function testReadEmptyUpdatesFile() | 38 | public function testReadEmptyUpdatesFile() |
70 | { | 39 | { |
71 | $this->assertEquals(array(), read_updates_file('')); | 40 | $this->assertEquals(array(), read_updates_file('')); |
72 | $updatesFile = self::$configFields['config']['DATADIR'] . '/updates.json'; | 41 | $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt'; |
73 | touch($updatesFile); | 42 | touch($updatesFile); |
74 | $this->assertEquals(array(), read_updates_file($updatesFile)); | 43 | $this->assertEquals(array(), read_updates_file($updatesFile)); |
44 | unlink($updatesFile); | ||
75 | } | 45 | } |
76 | 46 | ||
77 | /** | 47 | /** |
@@ -79,7 +49,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
79 | */ | 49 | */ |
80 | public function testReadWriteUpdatesFile() | 50 | public function testReadWriteUpdatesFile() |
81 | { | 51 | { |
82 | $updatesFile = self::$configFields['config']['DATADIR'] . '/updates.json'; | 52 | $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt'; |
83 | $updatesMethods = array('m1', 'm2', 'm3'); | 53 | $updatesMethods = array('m1', 'm2', 'm3'); |
84 | 54 | ||
85 | write_updates_file($updatesFile, $updatesMethods); | 55 | write_updates_file($updatesFile, $updatesMethods); |
@@ -91,6 +61,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
91 | write_updates_file($updatesFile, $updatesMethods); | 61 | write_updates_file($updatesFile, $updatesMethods); |
92 | $readMethods = read_updates_file($updatesFile); | 62 | $readMethods = read_updates_file($updatesFile); |
93 | $this->assertEquals($readMethods, $updatesMethods); | 63 | $this->assertEquals($readMethods, $updatesMethods); |
64 | unlink($updatesFile); | ||
94 | } | 65 | } |
95 | 66 | ||
96 | /** | 67 | /** |
@@ -112,10 +83,15 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
112 | */ | 83 | */ |
113 | public function testWriteUpdatesFileNotWritable() | 84 | public function testWriteUpdatesFileNotWritable() |
114 | { | 85 | { |
115 | $updatesFile = self::$configFields['config']['DATADIR'] . '/updates.json'; | 86 | $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt'; |
116 | touch($updatesFile); | 87 | touch($updatesFile); |
117 | chmod($updatesFile, 0444); | 88 | chmod($updatesFile, 0444); |
118 | @write_updates_file($updatesFile, array('test')); | 89 | try { |
90 | @write_updates_file($updatesFile, array('test')); | ||
91 | } catch (Exception $e) { | ||
92 | unlink($updatesFile); | ||
93 | throw $e; | ||
94 | } | ||
119 | } | 95 | } |
120 | 96 | ||
121 | /** | 97 | /** |
@@ -131,10 +107,10 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
131 | 'updateMethodDummy3', | 107 | 'updateMethodDummy3', |
132 | 'updateMethodException', | 108 | 'updateMethodException', |
133 | ); | 109 | ); |
134 | $updater = new DummyUpdater($updates, array(), array(), true); | 110 | $updater = new DummyUpdater($updates, array(), $this->conf, true); |
135 | $this->assertEquals(array(), $updater->update()); | 111 | $this->assertEquals(array(), $updater->update()); |
136 | 112 | ||
137 | $updater = new DummyUpdater(array(), array(), array(), false); | 113 | $updater = new DummyUpdater(array(), array(), $this->conf, false); |
138 | $this->assertEquals(array(), $updater->update()); | 114 | $this->assertEquals(array(), $updater->update()); |
139 | } | 115 | } |
140 | 116 | ||
@@ -149,7 +125,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
149 | 'updateMethodDummy2', | 125 | 'updateMethodDummy2', |
150 | 'updateMethodDummy3', | 126 | 'updateMethodDummy3', |
151 | ); | 127 | ); |
152 | $updater = new DummyUpdater($updates, array(), array(), true); | 128 | $updater = new DummyUpdater($updates, array(), $this->conf, true); |
153 | $this->assertEquals($expectedUpdates, $updater->update()); | 129 | $this->assertEquals($expectedUpdates, $updater->update()); |
154 | } | 130 | } |
155 | 131 | ||
@@ -165,7 +141,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
165 | ); | 141 | ); |
166 | $expectedUpdate = array('updateMethodDummy2'); | 142 | $expectedUpdate = array('updateMethodDummy2'); |
167 | 143 | ||
168 | $updater = new DummyUpdater($updates, array(), array(), true); | 144 | $updater = new DummyUpdater($updates, array(), $this->conf, true); |
169 | $this->assertEquals($expectedUpdate, $updater->update()); | 145 | $this->assertEquals($expectedUpdate, $updater->update()); |
170 | } | 146 | } |
171 | 147 | ||
@@ -182,7 +158,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
182 | 'updateMethodDummy3', | 158 | 'updateMethodDummy3', |
183 | ); | 159 | ); |
184 | 160 | ||
185 | $updater = new DummyUpdater($updates, array(), array(), true); | 161 | $updater = new DummyUpdater($updates, array(), $this->conf, true); |
186 | $updater->update(); | 162 | $updater->update(); |
187 | } | 163 | } |
188 | 164 | ||
@@ -195,26 +171,28 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
195 | */ | 171 | */ |
196 | public function testUpdateMergeDeprecatedConfig() | 172 | public function testUpdateMergeDeprecatedConfig() |
197 | { | 173 | { |
198 | // init | 174 | $this->conf->setConfigFile('tests/utils/config/configPhp'); |
199 | writeConfig(self::$configFields, true); | 175 | $this->conf->reset(); |
200 | $configCopy = self::$configFields; | ||
201 | $invert = !$configCopy['privateLinkByDefault']; | ||
202 | $configCopy['privateLinkByDefault'] = $invert; | ||
203 | 176 | ||
204 | // Use writeConfig to create a options.php | 177 | $optionsFile = 'tests/Updater/options.php'; |
205 | $configCopy['config']['CONFIG_FILE'] = 'tests/Updater/options.php'; | 178 | $options = '<?php |
206 | writeConfig($configCopy, true); | 179 | $GLOBALS[\'privateLinkByDefault\'] = true;'; |
180 | file_put_contents($optionsFile, $options); | ||
207 | 181 | ||
208 | $this->assertTrue(is_file($configCopy['config']['CONFIG_FILE'])); | 182 | // tmp config file. |
183 | $this->conf->setConfigFile('tests/Updater/config'); | ||
209 | 184 | ||
210 | // merge configs | 185 | // merge configs |
211 | $updater = new Updater(array(), self::$configFields, array(), true); | 186 | $updater = new Updater(array(), array(), $this->conf, true); |
187 | // This writes a new config file in tests/Updater/config.php | ||
212 | $updater->updateMethodMergeDeprecatedConfigFile(); | 188 | $updater->updateMethodMergeDeprecatedConfigFile(); |
213 | 189 | ||
214 | // make sure updated field is changed | 190 | // make sure updated field is changed |
215 | include self::$configFields['config']['CONFIG_FILE']; | 191 | $this->conf->reload(); |
216 | $this->assertEquals($invert, $GLOBALS['privateLinkByDefault']); | 192 | $this->assertTrue($this->conf->get('privacy.default_private_links')); |
217 | $this->assertFalse(is_file($configCopy['config']['CONFIG_FILE'])); | 193 | $this->assertFalse(is_file($optionsFile)); |
194 | // Delete the generated file. | ||
195 | unlink($this->conf->getConfigFileExt()); | ||
218 | } | 196 | } |
219 | 197 | ||
220 | /** | 198 | /** |
@@ -222,23 +200,254 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
222 | */ | 200 | */ |
223 | public function testMergeDeprecatedConfigNoFile() | 201 | public function testMergeDeprecatedConfigNoFile() |
224 | { | 202 | { |
225 | writeConfig(self::$configFields, true); | 203 | $updater = new Updater(array(), array(), $this->conf, true); |
226 | |||
227 | $updater = new Updater(array(), self::$configFields, array(), true); | ||
228 | $updater->updateMethodMergeDeprecatedConfigFile(); | 204 | $updater->updateMethodMergeDeprecatedConfigFile(); |
229 | 205 | ||
230 | include self::$configFields['config']['CONFIG_FILE']; | 206 | $this->assertEquals('root', $this->conf->get('credentials.login')); |
231 | $this->assertEquals(self::$configFields['login'], $GLOBALS['login']); | ||
232 | } | 207 | } |
233 | 208 | ||
209 | /** | ||
210 | * Test renameDashTags update method. | ||
211 | */ | ||
234 | public function testRenameDashTags() | 212 | public function testRenameDashTags() |
235 | { | 213 | { |
236 | $refDB = new ReferenceLinkDB(); | 214 | $refDB = new ReferenceLinkDB(); |
237 | $refDB->write(self::$testDatastore); | 215 | $refDB->write(self::$testDatastore); |
238 | $linkDB = new LinkDB(self::$testDatastore, true, false); | 216 | $linkDB = new LinkDB(self::$testDatastore, true, false); |
217 | |||
239 | $this->assertEmpty($linkDB->filterSearch(array('searchtags' => 'exclude'))); | 218 | $this->assertEmpty($linkDB->filterSearch(array('searchtags' => 'exclude'))); |
240 | $updater = new Updater(array(), self::$configFields, $linkDB, true); | 219 | $updater = new Updater(array(), $linkDB, $this->conf, true); |
241 | $updater->updateMethodRenameDashTags(); | 220 | $updater->updateMethodRenameDashTags(); |
242 | $this->assertNotEmpty($linkDB->filterSearch(array('searchtags' => 'exclude'))); | 221 | $this->assertNotEmpty($linkDB->filterSearch(array('searchtags' => 'exclude'))); |
243 | } | 222 | } |
223 | |||
224 | /** | ||
225 | * Convert old PHP config file to JSON config. | ||
226 | */ | ||
227 | public function testConfigToJson() | ||
228 | { | ||
229 | $configFile = 'tests/utils/config/configPhp'; | ||
230 | $this->conf->setConfigFile($configFile); | ||
231 | $this->conf->reset(); | ||
232 | |||
233 | // The ConfigIO is initialized with ConfigPhp. | ||
234 | $this->assertTrue($this->conf->getConfigIO() instanceof ConfigPhp); | ||
235 | |||
236 | $updater = new Updater(array(), array(), $this->conf, false); | ||
237 | $done = $updater->updateMethodConfigToJson(); | ||
238 | $this->assertTrue($done); | ||
239 | |||
240 | // The ConfigIO has been updated to ConfigJson. | ||
241 | $this->assertTrue($this->conf->getConfigIO() instanceof ConfigJson); | ||
242 | $this->assertTrue(file_exists($this->conf->getConfigFileExt())); | ||
243 | |||
244 | // Check JSON config data. | ||
245 | $this->conf->reload(); | ||
246 | $this->assertEquals('root', $this->conf->get('credentials.login')); | ||
247 | $this->assertEquals('lala', $this->conf->get('redirector.url')); | ||
248 | $this->assertEquals('data/datastore.php', $this->conf->get('resource.datastore')); | ||
249 | $this->assertEquals('1', $this->conf->get('plugins.WALLABAG_VERSION')); | ||
250 | |||
251 | rename($configFile . '.save.php', $configFile . '.php'); | ||
252 | unlink($this->conf->getConfigFileExt()); | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * Launch config conversion update with an existing JSON file => nothing to do. | ||
257 | */ | ||
258 | public function testConfigToJsonNothingToDo() | ||
259 | { | ||
260 | $filetime = filemtime($this->conf->getConfigFileExt()); | ||
261 | $updater = new Updater(array(), array(), $this->conf, false); | ||
262 | $done = $updater->updateMethodConfigToJson(); | ||
263 | $this->assertTrue($done); | ||
264 | $expected = filemtime($this->conf->getConfigFileExt()); | ||
265 | $this->assertEquals($expected, $filetime); | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * Test escapeUnescapedConfig with valid data. | ||
270 | */ | ||
271 | public function testEscapeConfig() | ||
272 | { | ||
273 | $sandbox = 'sandbox/config'; | ||
274 | copy(self::$configFile .'.json.php', $sandbox .'.json.php'); | ||
275 | $this->conf = new ConfigManager($sandbox); | ||
276 | $title = '<script>alert("title");</script>'; | ||
277 | $headerLink = '<script>alert("header_link");</script>'; | ||
278 | $redirectorUrl = '<script>alert("redirector");</script>'; | ||
279 | $this->conf->set('general.title', $title); | ||
280 | $this->conf->set('general.header_link', $headerLink); | ||
281 | $this->conf->set('redirector.url', $redirectorUrl); | ||
282 | $updater = new Updater(array(), array(), $this->conf, true); | ||
283 | $done = $updater->updateMethodEscapeUnescapedConfig(); | ||
284 | $this->assertTrue($done); | ||
285 | $this->conf->reload(); | ||
286 | $this->assertEquals(escape($title), $this->conf->get('general.title')); | ||
287 | $this->assertEquals(escape($headerLink), $this->conf->get('general.header_link')); | ||
288 | $this->assertEquals(escape($redirectorUrl), $this->conf->get('redirector.url')); | ||
289 | unlink($sandbox .'.json.php'); | ||
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 | } | ||
388 | |||
389 | /** | ||
390 | * Test updateMethodEscapeMarkdown with markdown plugin enabled | ||
391 | * => setting markdown_escape set to false. | ||
392 | */ | ||
393 | public function testEscapeMarkdownSettingToFalse() | ||
394 | { | ||
395 | $sandboxConf = 'sandbox/config'; | ||
396 | copy(self::$configFile . '.json.php', $sandboxConf . '.json.php'); | ||
397 | $this->conf = new ConfigManager($sandboxConf); | ||
398 | |||
399 | $this->conf->set('general.enabled_plugins', array('markdown')); | ||
400 | $updater = new Updater(array(), array(), $this->conf, true); | ||
401 | $this->assertTrue($updater->updateMethodEscapeMarkdown()); | ||
402 | $this->assertFalse($this->conf->get('security.markdown_escape')); | ||
403 | |||
404 | // reload from file | ||
405 | $this->conf = new ConfigManager($sandboxConf); | ||
406 | $this->assertFalse($this->conf->get('security.markdown_escape')); | ||
407 | } | ||
408 | |||
409 | /** | ||
410 | * Test updateMethodEscapeMarkdown with markdown plugin disabled | ||
411 | * => setting markdown_escape set to true. | ||
412 | */ | ||
413 | public function testEscapeMarkdownSettingToTrue() | ||
414 | { | ||
415 | $sandboxConf = 'sandbox/config'; | ||
416 | copy(self::$configFile . '.json.php', $sandboxConf . '.json.php'); | ||
417 | $this->conf = new ConfigManager($sandboxConf); | ||
418 | |||
419 | $this->conf->set('general.enabled_plugins', array()); | ||
420 | $updater = new Updater(array(), array(), $this->conf, true); | ||
421 | $this->assertTrue($updater->updateMethodEscapeMarkdown()); | ||
422 | $this->assertTrue($this->conf->get('security.markdown_escape')); | ||
423 | |||
424 | // reload from file | ||
425 | $this->conf = new ConfigManager($sandboxConf); | ||
426 | $this->assertTrue($this->conf->get('security.markdown_escape')); | ||
427 | } | ||
428 | |||
429 | /** | ||
430 | * Test updateMethodEscapeMarkdown with nothing to do (setting already enabled) | ||
431 | */ | ||
432 | public function testEscapeMarkdownSettingNothingToDoEnabled() | ||
433 | { | ||
434 | $sandboxConf = 'sandbox/config'; | ||
435 | copy(self::$configFile . '.json.php', $sandboxConf . '.json.php'); | ||
436 | $this->conf = new ConfigManager($sandboxConf); | ||
437 | $this->conf->set('security.markdown_escape', true); | ||
438 | $updater = new Updater(array(), array(), $this->conf, true); | ||
439 | $this->assertTrue($updater->updateMethodEscapeMarkdown()); | ||
440 | $this->assertTrue($this->conf->get('security.markdown_escape')); | ||
441 | } | ||
442 | |||
443 | /** | ||
444 | * Test updateMethodEscapeMarkdown with nothing to do (setting already disabled) | ||
445 | */ | ||
446 | public function testEscapeMarkdownSettingNothingToDoDisabled() | ||
447 | { | ||
448 | $this->conf->set('security.markdown_escape', false); | ||
449 | $updater = new Updater(array(), array(), $this->conf, true); | ||
450 | $this->assertTrue($updater->updateMethodEscapeMarkdown()); | ||
451 | $this->assertFalse($this->conf->get('security.markdown_escape')); | ||
452 | } | ||
244 | } | 453 | } |
diff --git a/tests/Url/UrlTest.php b/tests/Url/UrlTest.php index ce82265e..05862372 100644 --- a/tests/Url/UrlTest.php +++ b/tests/Url/UrlTest.php | |||
@@ -85,6 +85,7 @@ class UrlTest extends PHPUnit_Framework_TestCase | |||
85 | $this->assertUrlIsCleaned('?utm_term=1n4l'); | 85 | $this->assertUrlIsCleaned('?utm_term=1n4l'); |
86 | 86 | ||
87 | $this->assertUrlIsCleaned('?xtor=some-url'); | 87 | $this->assertUrlIsCleaned('?xtor=some-url'); |
88 | $this->assertUrlIsCleaned('?PHPSESSID=012345678910111213'); | ||
88 | } | 89 | } |
89 | 90 | ||
90 | /** | 91 | /** |
@@ -183,9 +184,9 @@ class UrlTest extends PHPUnit_Framework_TestCase | |||
183 | } | 184 | } |
184 | 185 | ||
185 | /** | 186 | /** |
186 | * Test IndToAscii. | 187 | * Test International Domain Name to ASCII conversion |
187 | */ | 188 | */ |
188 | function testIndToAscii() | 189 | function testIdnToAscii() |
189 | { | 190 | { |
190 | $ind = 'http://www.académie-française.fr/'; | 191 | $ind = 'http://www.académie-française.fr/'; |
191 | $expected = 'http://www.xn--acadmie-franaise-npb1a.fr/'; | 192 | $expected = 'http://www.xn--acadmie-franaise-npb1a.fr/'; |
diff --git a/tests/UtilsTest.php b/tests/UtilsTest.php index 3073b5eb..6a7870c4 100644 --- a/tests/UtilsTest.php +++ b/tests/UtilsTest.php | |||
@@ -253,41 +253,4 @@ class UtilsTest extends PHPUnit_Framework_TestCase | |||
253 | is_session_id_valid('c0ZqcWF3VFE2NmJBdm1HMVQ0ZHJ3UmZPbTFsNGhkNHI=') | 253 | is_session_id_valid('c0ZqcWF3VFE2NmJBdm1HMVQ0ZHJ3UmZPbTFsNGhkNHI=') |
254 | ); | 254 | ); |
255 | } | 255 | } |
256 | |||
257 | /** | ||
258 | * Test text2clickable without a redirector being set. | ||
259 | */ | ||
260 | public function testText2clickableWithoutRedirector() | ||
261 | { | ||
262 | $text = 'stuff http://hello.there/is=someone#here otherstuff'; | ||
263 | $expectedText = 'stuff <a href="http://hello.there/is=someone#here">http://hello.there/is=someone#here</a> otherstuff'; | ||
264 | $processedText = text2clickable($text, ''); | ||
265 | $this->assertEquals($expectedText, $processedText); | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * Test text2clickable a redirector set. | ||
270 | */ | ||
271 | public function testText2clickableWithRedirector() | ||
272 | { | ||
273 | $text = 'stuff http://hello.there/is=someone#here otherstuff'; | ||
274 | $redirector = 'http://redirector.to'; | ||
275 | $expectedText = 'stuff <a href="'. | ||
276 | $redirector . | ||
277 | urlencode('http://hello.there/is=someone#here') . | ||
278 | '">http://hello.there/is=someone#here</a> otherstuff'; | ||
279 | $processedText = text2clickable($text, $redirector); | ||
280 | $this->assertEquals($expectedText, $processedText); | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * Test testSpace2nbsp. | ||
285 | */ | ||
286 | public function testSpace2nbsp() | ||
287 | { | ||
288 | $text = ' Are you thrilled by flags ?'. PHP_EOL .' Really?'; | ||
289 | $expectedText = ' Are you thrilled by flags ?'. PHP_EOL .' Really?'; | ||
290 | $processedText = space2nbsp($text); | ||
291 | $this->assertEquals($expectedText, $processedText); | ||
292 | } | ||
293 | } | 256 | } |
diff --git a/tests/config/ConfigJsonTest.php b/tests/config/ConfigJsonTest.php new file mode 100644 index 00000000..07f6ab49 --- /dev/null +++ b/tests/config/ConfigJsonTest.php | |||
@@ -0,0 +1,133 @@ | |||
1 | <?php | ||
2 | |||
3 | require_once 'application/config/ConfigJson.php'; | ||
4 | |||
5 | /** | ||
6 | * Class ConfigJsonTest | ||
7 | */ | ||
8 | class ConfigJsonTest extends PHPUnit_Framework_TestCase | ||
9 | { | ||
10 | /** | ||
11 | * @var ConfigJson | ||
12 | */ | ||
13 | protected $configIO; | ||
14 | |||
15 | public function setUp() | ||
16 | { | ||
17 | $this->configIO = new ConfigJson(); | ||
18 | } | ||
19 | |||
20 | /** | ||
21 | * Read a simple existing config file. | ||
22 | */ | ||
23 | public function testRead() | ||
24 | { | ||
25 | $conf = $this->configIO->read('tests/utils/config/configJson.json.php'); | ||
26 | $this->assertEquals('root', $conf['credentials']['login']); | ||
27 | $this->assertEquals('lala', $conf['redirector']['url']); | ||
28 | $this->assertEquals('tests/utils/config/datastore.php', $conf['resource']['datastore']); | ||
29 | $this->assertEquals('1', $conf['plugins']['WALLABAG_VERSION']); | ||
30 | } | ||
31 | |||
32 | /** | ||
33 | * Read a non existent config file -> empty array. | ||
34 | */ | ||
35 | public function testReadNonExistent() | ||
36 | { | ||
37 | $this->assertEquals(array(), $this->configIO->read('nope')); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * Read a non existent config file -> empty array. | ||
42 | * | ||
43 | * @expectedException Exception | ||
44 | * @expectedExceptionMessage An error occurred while parsing JSON file: error code #4 | ||
45 | */ | ||
46 | public function testReadInvalidJson() | ||
47 | { | ||
48 | $this->configIO->read('tests/utils/config/configInvalid.json.php'); | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * Write a new config file. | ||
53 | */ | ||
54 | public function testWriteNew() | ||
55 | { | ||
56 | $dataFile = 'tests/utils/config/configWrite.json.php'; | ||
57 | $data = array( | ||
58 | 'credentials' => array( | ||
59 | 'login' => 'root', | ||
60 | ), | ||
61 | 'resource' => array( | ||
62 | 'datastore' => 'data/datastore.php', | ||
63 | ), | ||
64 | 'redirector' => array( | ||
65 | 'url' => 'lala', | ||
66 | ), | ||
67 | 'plugins' => array( | ||
68 | 'WALLABAG_VERSION' => '1', | ||
69 | ) | ||
70 | ); | ||
71 | $this->configIO->write($dataFile, $data); | ||
72 | // PHP 5.3 doesn't support json pretty print. | ||
73 | if (defined('JSON_PRETTY_PRINT')) { | ||
74 | $expected = '{ | ||
75 | "credentials": { | ||
76 | "login": "root" | ||
77 | }, | ||
78 | "resource": { | ||
79 | "datastore": "data\/datastore.php" | ||
80 | }, | ||
81 | "redirector": { | ||
82 | "url": "lala" | ||
83 | }, | ||
84 | "plugins": { | ||
85 | "WALLABAG_VERSION": "1" | ||
86 | } | ||
87 | }'; | ||
88 | } else { | ||
89 | $expected = '{"credentials":{"login":"root"},"resource":{"datastore":"data\/datastore.php"},"redirector":{"url":"lala"},"plugins":{"WALLABAG_VERSION":"1"}}'; | ||
90 | } | ||
91 | $expected = ConfigJson::getPhpHeaders() . $expected . ConfigJson::getPhpSuffix(); | ||
92 | $this->assertEquals($expected, file_get_contents($dataFile)); | ||
93 | unlink($dataFile); | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * Overwrite an existing setting. | ||
98 | */ | ||
99 | public function testOverwrite() | ||
100 | { | ||
101 | $source = 'tests/utils/config/configJson.json.php'; | ||
102 | $dest = 'tests/utils/config/configOverwrite.json.php'; | ||
103 | copy($source, $dest); | ||
104 | $conf = $this->configIO->read($dest); | ||
105 | $conf['redirector']['url'] = 'blabla'; | ||
106 | $this->configIO->write($dest, $conf); | ||
107 | $conf = $this->configIO->read($dest); | ||
108 | $this->assertEquals('blabla', $conf['redirector']['url']); | ||
109 | unlink($dest); | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * Write to invalid path. | ||
114 | * | ||
115 | * @expectedException IOException | ||
116 | */ | ||
117 | public function testWriteInvalidArray() | ||
118 | { | ||
119 | $conf = array('conf' => 'value'); | ||
120 | @$this->configIO->write(array(), $conf); | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Write to invalid path. | ||
125 | * | ||
126 | * @expectedException IOException | ||
127 | */ | ||
128 | public function testWriteInvalidBlank() | ||
129 | { | ||
130 | $conf = array('conf' => 'value'); | ||
131 | @$this->configIO->write('', $conf); | ||
132 | } | ||
133 | } | ||
diff --git a/tests/config/ConfigManagerTest.php b/tests/config/ConfigManagerTest.php new file mode 100644 index 00000000..436e3d67 --- /dev/null +++ b/tests/config/ConfigManagerTest.php | |||
@@ -0,0 +1,172 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Unit tests for Class ConfigManagerTest | ||
5 | * | ||
6 | * Note: it only test the manager with ConfigJson, | ||
7 | * ConfigPhp is only a workaround to handle the transition to JSON type. | ||
8 | */ | ||
9 | class ConfigManagerTest extends PHPUnit_Framework_TestCase | ||
10 | { | ||
11 | /** | ||
12 | * @var ConfigManager | ||
13 | */ | ||
14 | protected $conf; | ||
15 | |||
16 | public function setUp() | ||
17 | { | ||
18 | $this->conf = new ConfigManager('tests/utils/config/configJson'); | ||
19 | } | ||
20 | |||
21 | /** | ||
22 | * Simple config test: | ||
23 | * 1. Set settings. | ||
24 | * 2. Check settings value. | ||
25 | */ | ||
26 | public function testSetGet() | ||
27 | { | ||
28 | $this->conf->set('paramInt', 42); | ||
29 | $this->conf->set('paramString', 'value1'); | ||
30 | $this->conf->set('paramBool', false); | ||
31 | $this->conf->set('paramArray', array('foo' => 'bar')); | ||
32 | $this->conf->set('paramNull', null); | ||
33 | |||
34 | $this->assertEquals(42, $this->conf->get('paramInt')); | ||
35 | $this->assertEquals('value1', $this->conf->get('paramString')); | ||
36 | $this->assertFalse($this->conf->get('paramBool')); | ||
37 | $this->assertEquals(array('foo' => 'bar'), $this->conf->get('paramArray')); | ||
38 | $this->assertEquals(null, $this->conf->get('paramNull')); | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * Set/write/get config test: | ||
43 | * 1. Set settings. | ||
44 | * 2. Write it to the config file. | ||
45 | * 3. Read the file. | ||
46 | * 4. Check settings value. | ||
47 | */ | ||
48 | public function testSetWriteGet() | ||
49 | { | ||
50 | $this->conf->set('paramInt', 42); | ||
51 | $this->conf->set('paramString', 'value1'); | ||
52 | $this->conf->set('paramBool', false); | ||
53 | $this->conf->set('paramArray', array('foo' => 'bar')); | ||
54 | $this->conf->set('paramNull', null); | ||
55 | |||
56 | $this->conf->setConfigFile('tests/utils/config/configTmp'); | ||
57 | $this->conf->write(true); | ||
58 | $this->conf->reload(); | ||
59 | unlink($this->conf->getConfigFileExt()); | ||
60 | |||
61 | $this->assertEquals(42, $this->conf->get('paramInt')); | ||
62 | $this->assertEquals('value1', $this->conf->get('paramString')); | ||
63 | $this->assertFalse($this->conf->get('paramBool')); | ||
64 | $this->assertEquals(array('foo' => 'bar'), $this->conf->get('paramArray')); | ||
65 | $this->assertEquals(null, $this->conf->get('paramNull')); | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * Test set/write/get with nested keys. | ||
70 | */ | ||
71 | public function testSetWriteGetNested() | ||
72 | { | ||
73 | $this->conf->set('foo.bar.key.stuff', 'testSetWriteGetNested'); | ||
74 | |||
75 | $this->conf->setConfigFile('tests/utils/config/configTmp'); | ||
76 | $this->conf->write(true); | ||
77 | $this->conf->reload(); | ||
78 | unlink($this->conf->getConfigFileExt()); | ||
79 | |||
80 | $this->assertEquals('testSetWriteGetNested', $this->conf->get('foo.bar.key.stuff')); | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * Set with an empty key. | ||
85 | * | ||
86 | * @expectedException Exception | ||
87 | * @expectedExceptionMessageRegExp #^Invalid setting key parameter. String expected, got.*# | ||
88 | */ | ||
89 | public function testSetEmptyKey() | ||
90 | { | ||
91 | $this->conf->set('', 'stuff'); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Set with an array key. | ||
96 | * | ||
97 | * @expectedException Exception | ||
98 | * @expectedExceptionMessageRegExp #^Invalid setting key parameter. String expected, got.*# | ||
99 | */ | ||
100 | public function testSetArrayKey() | ||
101 | { | ||
102 | $this->conf->set(array('foo' => 'bar'), 'stuff'); | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * Try to write the config without mandatory parameter (e.g. 'login'). | ||
107 | * | ||
108 | * @expectedException MissingFieldConfigException | ||
109 | */ | ||
110 | public function testWriteMissingParameter() | ||
111 | { | ||
112 | $this->conf->setConfigFile('tests/utils/config/configTmp'); | ||
113 | $this->assertFalse(file_exists($this->conf->getConfigFileExt())); | ||
114 | $this->conf->reload(); | ||
115 | |||
116 | $this->conf->write(true); | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * Try to get non existent config keys. | ||
121 | */ | ||
122 | public function testGetNonExistent() | ||
123 | { | ||
124 | $this->assertEquals('', $this->conf->get('nope.test')); | ||
125 | $this->assertEquals('default', $this->conf->get('nope.test', 'default')); | ||
126 | } | ||
127 | |||
128 | /** | ||
129 | * Test the 'exists' method with existent values. | ||
130 | */ | ||
131 | public function testExistsOk() | ||
132 | { | ||
133 | $this->assertTrue($this->conf->exists('credentials.login')); | ||
134 | $this->assertTrue($this->conf->exists('config.foo')); | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * Test the 'exists' method with non existent or invalid values. | ||
139 | */ | ||
140 | public function testExistsKo() | ||
141 | { | ||
142 | $this->assertFalse($this->conf->exists('nope')); | ||
143 | $this->assertFalse($this->conf->exists('nope.nope')); | ||
144 | $this->assertFalse($this->conf->exists('')); | ||
145 | $this->assertFalse($this->conf->exists(false)); | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * Reset the ConfigManager instance. | ||
150 | */ | ||
151 | public function testReset() | ||
152 | { | ||
153 | $confIO = $this->conf->getConfigIO(); | ||
154 | $this->conf->reset(); | ||
155 | $this->assertFalse($confIO === $this->conf->getConfigIO()); | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * Reload the config from file. | ||
160 | */ | ||
161 | public function testReload() | ||
162 | { | ||
163 | $this->conf->setConfigFile('tests/utils/config/configTmp'); | ||
164 | $newConf = ConfigJson::getPhpHeaders() . '{ "key": "value" }'; | ||
165 | file_put_contents($this->conf->getConfigFileExt(), $newConf); | ||
166 | $this->conf->reload(); | ||
167 | unlink($this->conf->getConfigFileExt()); | ||
168 | // Previous conf no longer exists, and new values have been loaded. | ||
169 | $this->assertFalse($this->conf->exists('credentials.login')); | ||
170 | $this->assertEquals('value', $this->conf->get('key')); | ||
171 | } | ||
172 | } | ||
diff --git a/tests/config/ConfigPhpTest.php b/tests/config/ConfigPhpTest.php new file mode 100644 index 00000000..58cd8d2a --- /dev/null +++ b/tests/config/ConfigPhpTest.php | |||
@@ -0,0 +1,82 @@ | |||
1 | <?php | ||
2 | |||
3 | require_once 'application/config/ConfigPhp.php'; | ||
4 | |||
5 | /** | ||
6 | * Class ConfigPhpTest | ||
7 | */ | ||
8 | class ConfigPhpTest extends PHPUnit_Framework_TestCase | ||
9 | { | ||
10 | /** | ||
11 | * @var ConfigPhp | ||
12 | */ | ||
13 | protected $configIO; | ||
14 | |||
15 | public function setUp() | ||
16 | { | ||
17 | $this->configIO = new ConfigPhp(); | ||
18 | } | ||
19 | |||
20 | /** | ||
21 | * Read a simple existing config file. | ||
22 | */ | ||
23 | public function testRead() | ||
24 | { | ||
25 | $conf = $this->configIO->read('tests/utils/config/configPhp.php'); | ||
26 | $this->assertEquals('root', $conf['login']); | ||
27 | $this->assertEquals('lala', $conf['redirector']); | ||
28 | $this->assertEquals('data/datastore.php', $conf['config']['DATASTORE']); | ||
29 | $this->assertEquals('1', $conf['plugins']['WALLABAG_VERSION']); | ||
30 | } | ||
31 | |||
32 | /** | ||
33 | * Read a non existent config file -> empty array. | ||
34 | */ | ||
35 | public function testReadNonExistent() | ||
36 | { | ||
37 | $this->assertEquals(array(), $this->configIO->read('nope')); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * Write a new config file. | ||
42 | */ | ||
43 | public function testWriteNew() | ||
44 | { | ||
45 | $dataFile = 'tests/utils/config/configWrite.php'; | ||
46 | $data = array( | ||
47 | 'login' => 'root', | ||
48 | 'redirector' => 'lala', | ||
49 | 'config' => array( | ||
50 | 'DATASTORE' => 'data/datastore.php', | ||
51 | ), | ||
52 | 'plugins' => array( | ||
53 | 'WALLABAG_VERSION' => '1', | ||
54 | ) | ||
55 | ); | ||
56 | $this->configIO->write($dataFile, $data); | ||
57 | $expected = '<?php | ||
58 | $GLOBALS[\'login\'] = \'root\'; | ||
59 | $GLOBALS[\'redirector\'] = \'lala\'; | ||
60 | $GLOBALS[\'config\'][\'DATASTORE\'] = \'data/datastore.php\'; | ||
61 | $GLOBALS[\'plugins\'][\'WALLABAG_VERSION\'] = \'1\'; | ||
62 | '; | ||
63 | $this->assertEquals($expected, file_get_contents($dataFile)); | ||
64 | unlink($dataFile); | ||
65 | } | ||
66 | |||
67 | /** | ||
68 | * Overwrite an existing setting. | ||
69 | */ | ||
70 | public function testOverwrite() | ||
71 | { | ||
72 | $source = 'tests/utils/config/configPhp.php'; | ||
73 | $dest = 'tests/utils/config/configOverwrite.php'; | ||
74 | copy($source, $dest); | ||
75 | $conf = $this->configIO->read($dest); | ||
76 | $conf['redirector'] = 'blabla'; | ||
77 | $this->configIO->write($dest, $conf); | ||
78 | $conf = $this->configIO->read($dest); | ||
79 | $this->assertEquals('blabla', $conf['redirector']); | ||
80 | unlink($dest); | ||
81 | } | ||
82 | } | ||
diff --git a/tests/config/ConfigPluginTest.php b/tests/config/ConfigPluginTest.php new file mode 100644 index 00000000..3b37cd79 --- /dev/null +++ b/tests/config/ConfigPluginTest.php | |||
@@ -0,0 +1,121 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * Config' tests | ||
4 | */ | ||
5 | |||
6 | require_once 'application/config/ConfigPlugin.php'; | ||
7 | |||
8 | /** | ||
9 | * Unitary tests for Shaarli config related functions | ||
10 | */ | ||
11 | class ConfigPluginTest extends PHPUnit_Framework_TestCase | ||
12 | { | ||
13 | /** | ||
14 | * Test save_plugin_config with valid data. | ||
15 | * | ||
16 | * @throws PluginConfigOrderException | ||
17 | */ | ||
18 | public function testSavePluginConfigValid() | ||
19 | { | ||
20 | $data = array( | ||
21 | 'order_plugin1' => 2, // no plugin related | ||
22 | 'plugin2' => 0, // new - at the end | ||
23 | 'plugin3' => 0, // 2nd | ||
24 | 'order_plugin3' => 8, | ||
25 | 'plugin4' => 0, // 1st | ||
26 | 'order_plugin4' => 5, | ||
27 | ); | ||
28 | |||
29 | $expected = array( | ||
30 | 'plugin3', | ||
31 | 'plugin4', | ||
32 | 'plugin2', | ||
33 | ); | ||
34 | |||
35 | $out = save_plugin_config($data); | ||
36 | $this->assertEquals($expected, $out); | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * Test save_plugin_config with invalid data. | ||
41 | * | ||
42 | * @expectedException PluginConfigOrderException | ||
43 | */ | ||
44 | public function testSavePluginConfigInvalid() | ||
45 | { | ||
46 | $data = array( | ||
47 | 'plugin2' => 0, | ||
48 | 'plugin3' => 0, | ||
49 | 'order_plugin3' => 0, | ||
50 | 'plugin4' => 0, | ||
51 | 'order_plugin4' => 0, | ||
52 | ); | ||
53 | |||
54 | save_plugin_config($data); | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Test save_plugin_config without data. | ||
59 | */ | ||
60 | public function testSavePluginConfigEmpty() | ||
61 | { | ||
62 | $this->assertEquals(array(), save_plugin_config(array())); | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Test validate_plugin_order with valid data. | ||
67 | */ | ||
68 | public function testValidatePluginOrderValid() | ||
69 | { | ||
70 | $data = array( | ||
71 | 'order_plugin1' => 2, | ||
72 | 'plugin2' => 0, | ||
73 | 'plugin3' => 0, | ||
74 | 'order_plugin3' => 1, | ||
75 | 'plugin4' => 0, | ||
76 | 'order_plugin4' => 5, | ||
77 | ); | ||
78 | |||
79 | $this->assertTrue(validate_plugin_order($data)); | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * Test validate_plugin_order with invalid data. | ||
84 | */ | ||
85 | public function testValidatePluginOrderInvalid() | ||
86 | { | ||
87 | $data = array( | ||
88 | 'order_plugin1' => 2, | ||
89 | 'order_plugin3' => 1, | ||
90 | 'order_plugin4' => 1, | ||
91 | ); | ||
92 | |||
93 | $this->assertFalse(validate_plugin_order($data)); | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * Test load_plugin_parameter_values. | ||
98 | */ | ||
99 | public function testLoadPluginParameterValues() | ||
100 | { | ||
101 | $plugins = array( | ||
102 | 'plugin_name' => array( | ||
103 | 'parameters' => array( | ||
104 | 'param1' => array('value' => true), | ||
105 | 'param2' => array('value' => false), | ||
106 | 'param3' => array('value' => ''), | ||
107 | ) | ||
108 | ) | ||
109 | ); | ||
110 | |||
111 | $parameters = array( | ||
112 | 'param1' => 'value1', | ||
113 | 'param2' => 'value2', | ||
114 | ); | ||
115 | |||
116 | $result = load_plugin_parameter_values($plugins, $parameters); | ||
117 | $this->assertEquals('value1', $result['plugin_name']['parameters']['param1']['value']); | ||
118 | $this->assertEquals('value2', $result['plugin_name']['parameters']['param2']['value']); | ||
119 | $this->assertEquals('', $result['plugin_name']['parameters']['param3']['value']); | ||
120 | } | ||
121 | } | ||
diff --git a/tests/plugins/PluginArchiveorgTest.php b/tests/plugins/PluginArchiveorgTest.php index dbc52bc8..4daa4c9d 100644 --- a/tests/plugins/PluginArchiveorgTest.php +++ b/tests/plugins/PluginArchiveorgTest.php | |||
@@ -7,8 +7,8 @@ | |||
7 | require_once 'plugins/archiveorg/archiveorg.php'; | 7 | require_once 'plugins/archiveorg/archiveorg.php'; |
8 | 8 | ||
9 | /** | 9 | /** |
10 | * Class PlugQrcodeTest | 10 | * Class PluginArchiveorgTest |
11 | * Unit test for the QR-Code plugin | 11 | * Unit test for the archiveorg plugin |
12 | */ | 12 | */ |
13 | class PluginArchiveorgTest extends PHPUnit_Framework_TestCase | 13 | class PluginArchiveorgTest extends PHPUnit_Framework_TestCase |
14 | { | 14 | { |
@@ -21,21 +21,25 @@ class PluginArchiveorgTest extends PHPUnit_Framework_TestCase | |||
21 | } | 21 | } |
22 | 22 | ||
23 | /** | 23 | /** |
24 | * Test render_linklist hook. | 24 | * Test render_linklist hook on external links. |
25 | */ | 25 | */ |
26 | function testArchiveorgLinklist() | 26 | function testArchiveorgLinklistOnExternalLinks() |
27 | { | 27 | { |
28 | $str = 'http://randomstr.com/test'; | 28 | $str = 'http://randomstr.com/test'; |
29 | |||
29 | $data = array( | 30 | $data = array( |
30 | 'title' => $str, | 31 | 'title' => $str, |
31 | 'links' => array( | 32 | 'links' => array( |
32 | array( | 33 | array( |
33 | 'url' => $str, | 34 | 'url' => $str, |
35 | 'private' => 0, | ||
36 | 'real_url' => $str | ||
34 | ) | 37 | ) |
35 | ) | 38 | ) |
36 | ); | 39 | ); |
37 | 40 | ||
38 | $data = hook_archiveorg_render_linklist($data); | 41 | $data = hook_archiveorg_render_linklist($data); |
42 | |||
39 | $link = $data['links'][0]; | 43 | $link = $data['links'][0]; |
40 | // data shouldn't be altered | 44 | // data shouldn't be altered |
41 | $this->assertEquals($str, $data['title']); | 45 | $this->assertEquals($str, $data['title']); |
@@ -44,5 +48,95 @@ class PluginArchiveorgTest extends PHPUnit_Framework_TestCase | |||
44 | // plugin data | 48 | // plugin data |
45 | $this->assertEquals(1, count($link['link_plugin'])); | 49 | $this->assertEquals(1, count($link['link_plugin'])); |
46 | $this->assertNotFalse(strpos($link['link_plugin'][0], $str)); | 50 | $this->assertNotFalse(strpos($link['link_plugin'][0], $str)); |
51 | |||
52 | } | ||
53 | |||
54 | /** | ||
55 | * Test render_linklist hook on internal links. | ||
56 | */ | ||
57 | function testArchiveorgLinklistOnInternalLinks() | ||
58 | { | ||
59 | $internalLink1 = 'http://shaarli.shaarli/?qvMAqg'; | ||
60 | $internalLinkRealURL1 = '?qvMAqg'; | ||
61 | |||
62 | $internalLink2 = 'http://shaarli.shaarli/?2_7zww'; | ||
63 | $internalLinkRealURL2 = '?2_7zww'; | ||
64 | |||
65 | $internalLink3 = 'http://shaarli.shaarli/?z7u-_Q'; | ||
66 | $internalLinkRealURL3 = '?z7u-_Q'; | ||
67 | |||
68 | $data = array( | ||
69 | 'title' => $internalLink1, | ||
70 | 'links' => array( | ||
71 | array( | ||
72 | 'url' => $internalLink1, | ||
73 | 'private' => 0, | ||
74 | 'real_url' => $internalLinkRealURL1 | ||
75 | ), | ||
76 | array( | ||
77 | 'url' => $internalLink1, | ||
78 | 'private' => 1, | ||
79 | 'real_url' => $internalLinkRealURL1 | ||
80 | ), | ||
81 | array( | ||
82 | 'url' => $internalLink2, | ||
83 | 'private' => 0, | ||
84 | 'real_url' => $internalLinkRealURL2 | ||
85 | ), | ||
86 | array( | ||
87 | 'url' => $internalLink2, | ||
88 | 'private' => 1, | ||
89 | 'real_url' => $internalLinkRealURL2 | ||
90 | ), | ||
91 | array( | ||
92 | 'url' => $internalLink3, | ||
93 | 'private' => 0, | ||
94 | 'real_url' => $internalLinkRealURL3 | ||
95 | ), | ||
96 | array( | ||
97 | 'url' => $internalLink3, | ||
98 | 'private' => 1, | ||
99 | 'real_url' => $internalLinkRealURL3 | ||
100 | ) | ||
101 | ) | ||
102 | ); | ||
103 | |||
104 | |||
105 | $data = hook_archiveorg_render_linklist($data); | ||
106 | |||
107 | // Case n°1: first link type, public | ||
108 | $link = $data['links'][0]; | ||
109 | |||
110 | $this->assertEquals(1, count($link['link_plugin'])); | ||
111 | $this->assertNotFalse(strpos($link['link_plugin'][0], $internalLink1)); | ||
112 | |||
113 | // Case n°2: first link type, private | ||
114 | $link = $data['links'][1]; | ||
115 | |||
116 | $this->assertArrayNotHasKey('link_plugin', $link); | ||
117 | |||
118 | // Case n°3: second link type, public | ||
119 | $link = $data['links'][2]; | ||
120 | |||
121 | $this->assertEquals(1, count($link['link_plugin'])); | ||
122 | $this->assertNotFalse(strpos($link['link_plugin'][0], $internalLink2)); | ||
123 | |||
124 | // Case n°4: second link type, private | ||
125 | $link = $data['links'][3]; | ||
126 | |||
127 | $this->assertArrayNotHasKey('link_plugin', $link); | ||
128 | |||
129 | // Case n°5: third link type, public | ||
130 | $link = $data['links'][4]; | ||
131 | |||
132 | $this->assertEquals(1, count($link['link_plugin'])); | ||
133 | $this->assertNotFalse(strpos($link['link_plugin'][0], $internalLink3)); | ||
134 | |||
135 | // Case n°6: third link type, private | ||
136 | $link = $data['links'][5]; | ||
137 | |||
138 | $this->assertArrayNotHasKey('link_plugin', $link); | ||
139 | |||
47 | } | 140 | } |
141 | |||
48 | } | 142 | } |
diff --git a/tests/plugins/PluginIssoTest.php b/tests/plugins/PluginIssoTest.php new file mode 100644 index 00000000..6b7904dd --- /dev/null +++ b/tests/plugins/PluginIssoTest.php | |||
@@ -0,0 +1,151 @@ | |||
1 | <?php | ||
2 | |||
3 | require_once 'plugins/isso/isso.php'; | ||
4 | |||
5 | /** | ||
6 | * Class PluginIssoTest | ||
7 | * | ||
8 | * Test the Isso plugin (comment system). | ||
9 | */ | ||
10 | class PluginIssoTest extends PHPUnit_Framework_TestCase | ||
11 | { | ||
12 | /** | ||
13 | * Reset plugin path | ||
14 | */ | ||
15 | function setUp() | ||
16 | { | ||
17 | PluginManager::$PLUGINS_PATH = 'plugins'; | ||
18 | } | ||
19 | |||
20 | /** | ||
21 | * Test Isso init without errors. | ||
22 | */ | ||
23 | function testWallabagInitNoError() | ||
24 | { | ||
25 | $conf = new ConfigManager(''); | ||
26 | $conf->set('plugins.ISSO_SERVER', 'value'); | ||
27 | $errors = isso_init($conf); | ||
28 | $this->assertEmpty($errors); | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * Test Isso init with errors. | ||
33 | */ | ||
34 | function testWallabagInitError() | ||
35 | { | ||
36 | $conf = new ConfigManager(''); | ||
37 | $errors = isso_init($conf); | ||
38 | $this->assertNotEmpty($errors); | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * Test render_linklist hook with valid settings to display the comment form. | ||
43 | */ | ||
44 | function testIssoDisplayed() | ||
45 | { | ||
46 | $conf = new ConfigManager(''); | ||
47 | $conf->set('plugins.ISSO_SERVER', 'value'); | ||
48 | |||
49 | $str = 'http://randomstr.com/test'; | ||
50 | $date = '20161118_100001'; | ||
51 | $data = array( | ||
52 | 'title' => $str, | ||
53 | 'links' => array( | ||
54 | array( | ||
55 | 'id' => 12, | ||
56 | 'url' => $str, | ||
57 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date), | ||
58 | ) | ||
59 | ) | ||
60 | ); | ||
61 | |||
62 | $data = hook_isso_render_linklist($data, $conf); | ||
63 | |||
64 | // data shouldn't be altered | ||
65 | $this->assertEquals($str, $data['title']); | ||
66 | $this->assertEquals($str, $data['links'][0]['url']); | ||
67 | |||
68 | // plugin data | ||
69 | $this->assertEquals(1, count($data['plugin_end_zone'])); | ||
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 | )); | ||
78 | $this->assertNotFalse(strpos($data['plugin_end_zone'][0], 'embed.min.js')); | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * Test isso plugin when multiple links are displayed (shouldn't be displayed). | ||
83 | */ | ||
84 | function testIssoMultipleLinks() | ||
85 | { | ||
86 | $conf = new ConfigManager(''); | ||
87 | $conf->set('plugins.ISSO_SERVER', 'value'); | ||
88 | |||
89 | $str = 'http://randomstr.com/test'; | ||
90 | $date1 = '20161118_100001'; | ||
91 | $date2 = '20161118_100002'; | ||
92 | $data = array( | ||
93 | 'title' => $str, | ||
94 | 'links' => array( | ||
95 | array( | ||
96 | 'id' => 12, | ||
97 | 'url' => $str, | ||
98 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date1), | ||
99 | ), | ||
100 | array( | ||
101 | 'id' => 13, | ||
102 | 'url' => $str . '2', | ||
103 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date2), | ||
104 | ), | ||
105 | ) | ||
106 | ); | ||
107 | |||
108 | $processed = hook_isso_render_linklist($data, $conf); | ||
109 | // data shouldn't be altered | ||
110 | $this->assertEquals($data, $processed); | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * Test isso plugin when using search (shouldn't be displayed). | ||
115 | */ | ||
116 | function testIssoNotDisplayedWhenSearch() | ||
117 | { | ||
118 | $conf = new ConfigManager(''); | ||
119 | $conf->set('plugins.ISSO_SERVER', 'value'); | ||
120 | |||
121 | $str = 'http://randomstr.com/test'; | ||
122 | $date = '20161118_100001'; | ||
123 | $data = array( | ||
124 | 'title' => $str, | ||
125 | 'links' => array( | ||
126 | array( | ||
127 | 'id' => 12, | ||
128 | 'url' => $str, | ||
129 | 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date), | ||
130 | ) | ||
131 | ), | ||
132 | 'search_term' => $str | ||
133 | ); | ||
134 | |||
135 | $processed = hook_isso_render_linklist($data, $conf); | ||
136 | |||
137 | // data shouldn't be altered | ||
138 | $this->assertEquals($data, $processed); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * Test isso plugin without server configuration (shouldn't be displayed). | ||
143 | */ | ||
144 | function testIssoWithoutConf() | ||
145 | { | ||
146 | $data = 'abc'; | ||
147 | $conf = new ConfigManager(''); | ||
148 | $processed = hook_isso_render_linklist($data, $conf); | ||
149 | $this->assertEquals($data, $processed); | ||
150 | } | ||
151 | } | ||
diff --git a/tests/plugins/PluginMarkdownTest.php b/tests/plugins/PluginMarkdownTest.php index fa7e1d52..f1e1acf8 100644 --- a/tests/plugins/PluginMarkdownTest.php +++ b/tests/plugins/PluginMarkdownTest.php | |||
@@ -8,17 +8,23 @@ 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 | { |
16 | /** | 16 | /** |
17 | * @var ConfigManager instance. | ||
18 | */ | ||
19 | protected $conf; | ||
20 | |||
21 | /** | ||
17 | * Reset plugin path | 22 | * Reset plugin path |
18 | */ | 23 | */ |
19 | function setUp() | 24 | function setUp() |
20 | { | 25 | { |
21 | PluginManager::$PLUGINS_PATH = 'plugins'; | 26 | PluginManager::$PLUGINS_PATH = 'plugins'; |
27 | $this->conf = new ConfigManager('tests/utils/config/configJson'); | ||
22 | } | 28 | } |
23 | 29 | ||
24 | /** | 30 | /** |
@@ -36,7 +42,7 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase | |||
36 | ), | 42 | ), |
37 | ); | 43 | ); |
38 | 44 | ||
39 | $data = hook_markdown_render_linklist($data); | 45 | $data = hook_markdown_render_linklist($data, $this->conf); |
40 | $this->assertNotFalse(strpos($data['links'][0]['description'], '<h1>')); | 46 | $this->assertNotFalse(strpos($data['links'][0]['description'], '<h1>')); |
41 | $this->assertNotFalse(strpos($data['links'][0]['description'], '<p>')); | 47 | $this->assertNotFalse(strpos($data['links'][0]['description'], '<p>')); |
42 | } | 48 | } |
@@ -61,7 +67,7 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase | |||
61 | ), | 67 | ), |
62 | ); | 68 | ); |
63 | 69 | ||
64 | $data = hook_markdown_render_daily($data); | 70 | $data = hook_markdown_render_daily($data, $this->conf); |
65 | $this->assertNotFalse(strpos($data['cols'][0][0]['formatedDescription'], '<h1>')); | 71 | $this->assertNotFalse(strpos($data['cols'][0][0]['formatedDescription'], '<h1>')); |
66 | $this->assertNotFalse(strpos($data['cols'][0][0]['formatedDescription'], '<p>')); | 72 | $this->assertNotFalse(strpos($data['cols'][0][0]['formatedDescription'], '<p>')); |
67 | } | 73 | } |
@@ -110,6 +116,8 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase | |||
110 | $output = escape($input); | 116 | $output = escape($input); |
111 | $input .= '<a href="#" onmouseHover="alert(\'xss\');" attr="tt">link</a>'; | 117 | $input .= '<a href="#" onmouseHover="alert(\'xss\');" attr="tt">link</a>'; |
112 | $output .= '<a href="#" attr="tt">link</a>'; | 118 | $output .= '<a href="#" attr="tt">link</a>'; |
119 | $input .= '<a href="#" onmouseHover=alert(\'xss\'); attr="tt">link</a>'; | ||
120 | $output .= '<a href="#" attr="tt">link</a>'; | ||
113 | $this->assertEquals($output, sanitize_html($input)); | 121 | $this->assertEquals($output, sanitize_html($input)); |
114 | // Do not touch escaped HTML. | 122 | // Do not touch escaped HTML. |
115 | $input = escape($input); | 123 | $input = escape($input); |
@@ -125,12 +133,16 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase | |||
125 | $data = array( | 133 | $data = array( |
126 | 'links' => array(array( | 134 | 'links' => array(array( |
127 | 'description' => $str, | 135 | 'description' => $str, |
128 | 'tags' => NO_MD_TAG | 136 | 'tags' => NO_MD_TAG, |
137 | 'taglist' => array(NO_MD_TAG), | ||
129 | )) | 138 | )) |
130 | ); | 139 | ); |
131 | 140 | ||
132 | $data = hook_markdown_render_linklist($data); | 141 | $processed = hook_markdown_render_linklist($data, $this->conf); |
133 | $this->assertEquals($str, $data['links'][0]['description']); | 142 | $this->assertEquals($str, $processed['links'][0]['description']); |
143 | |||
144 | $processed = hook_markdown_render_feed($data, $this->conf); | ||
145 | $this->assertEquals($str, $processed['links'][0]['description']); | ||
134 | 146 | ||
135 | $data = array( | 147 | $data = array( |
136 | // Columns data | 148 | // Columns data |
@@ -140,13 +152,82 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase | |||
140 | // nth link | 152 | // nth link |
141 | 0 => array( | 153 | 0 => array( |
142 | 'formatedDescription' => $str, | 154 | 'formatedDescription' => $str, |
143 | 'tags' => NO_MD_TAG | 155 | 'tags' => NO_MD_TAG, |
156 | 'taglist' => array(), | ||
144 | ), | 157 | ), |
145 | ), | 158 | ), |
146 | ), | 159 | ), |
147 | ); | 160 | ); |
148 | 161 | ||
149 | $data = hook_markdown_render_daily($data); | 162 | $data = hook_markdown_render_daily($data, $this->conf); |
150 | $this->assertEquals($str, $data['cols'][0][0]['formatedDescription']); | 163 | $this->assertEquals($str, $data['cols'][0][0]['formatedDescription']); |
151 | } | 164 | } |
165 | |||
166 | /** | ||
167 | * Test that a close value to nomarkdown is not understand as nomarkdown (previous value `.nomarkdown`). | ||
168 | */ | ||
169 | function testNoMarkdownNotExcactlyMatching() | ||
170 | { | ||
171 | $str = 'All _work_ and `no play` makes Jack a *dull* boy.'; | ||
172 | $data = array( | ||
173 | 'links' => array(array( | ||
174 | 'description' => $str, | ||
175 | 'tags' => '.' . NO_MD_TAG, | ||
176 | 'taglist' => array('.'. NO_MD_TAG), | ||
177 | )) | ||
178 | ); | ||
179 | |||
180 | $data = hook_markdown_render_feed($data, $this->conf); | ||
181 | $this->assertContains('<em>', $data['links'][0]['description']); | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * Test hashtag links processed with markdown. | ||
186 | */ | ||
187 | function testMarkdownHashtagLinks() | ||
188 | { | ||
189 | $md = file_get_contents('tests/plugins/resources/markdown.md'); | ||
190 | $md = format_description($md); | ||
191 | $html = file_get_contents('tests/plugins/resources/markdown.html'); | ||
192 | |||
193 | $data = process_markdown($md); | ||
194 | $this->assertEquals($html, $data); | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * Make sure that the HTML tags are escaped. | ||
199 | */ | ||
200 | public function testMarkdownWithHtmlEscape() | ||
201 | { | ||
202 | $md = '**strong** <strong>strong</strong>'; | ||
203 | $html = '<div class="markdown"><p><strong>strong</strong> <strong>strong</strong></p></div>'; | ||
204 | $data = array( | ||
205 | 'links' => array( | ||
206 | 0 => array( | ||
207 | 'description' => $md, | ||
208 | ), | ||
209 | ), | ||
210 | ); | ||
211 | $data = hook_markdown_render_linklist($data, $this->conf); | ||
212 | $this->assertEquals($html, $data['links'][0]['description']); | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * Make sure that the HTML tags aren't escaped with the setting set to false. | ||
217 | */ | ||
218 | public function testMarkdownWithHtmlNoEscape() | ||
219 | { | ||
220 | $this->conf->set('security.markdown_escape', false); | ||
221 | $md = '**strong** <strong>strong</strong>'; | ||
222 | $html = '<div class="markdown"><p><strong>strong</strong> <strong>strong</strong></p></div>'; | ||
223 | $data = array( | ||
224 | 'links' => array( | ||
225 | 0 => array( | ||
226 | 'description' => $md, | ||
227 | ), | ||
228 | ), | ||
229 | ); | ||
230 | $data = hook_markdown_render_linklist($data, $this->conf); | ||
231 | $this->assertEquals($html, $data['links'][0]['description']); | ||
232 | } | ||
152 | } | 233 | } |
diff --git a/tests/plugins/PluginReadityourselfTest.php b/tests/plugins/PluginReadityourselfTest.php index 8bf17bf1..532db146 100644 --- a/tests/plugins/PluginReadityourselfTest.php +++ b/tests/plugins/PluginReadityourselfTest.php | |||
@@ -21,11 +21,33 @@ class PluginReadityourselfTest extends PHPUnit_Framework_TestCase | |||
21 | } | 21 | } |
22 | 22 | ||
23 | /** | 23 | /** |
24 | * Test Readityourself init without errors. | ||
25 | */ | ||
26 | function testReadityourselfInitNoError() | ||
27 | { | ||
28 | $conf = new ConfigManager(''); | ||
29 | $conf->set('plugins.READITYOUSELF_URL', 'value'); | ||
30 | $errors = readityourself_init($conf); | ||
31 | $this->assertEmpty($errors); | ||
32 | } | ||
33 | |||
34 | /** | ||
35 | * Test Readityourself init with errors. | ||
36 | */ | ||
37 | function testReadityourselfInitError() | ||
38 | { | ||
39 | $conf = new ConfigManager(''); | ||
40 | $errors = readityourself_init($conf); | ||
41 | $this->assertNotEmpty($errors); | ||
42 | } | ||
43 | |||
44 | /** | ||
24 | * Test render_linklist hook. | 45 | * Test render_linklist hook. |
25 | */ | 46 | */ |
26 | function testReadityourselfLinklist() | 47 | function testReadityourselfLinklist() |
27 | { | 48 | { |
28 | $GLOBALS['plugins']['READITYOUSELF_URL'] = 'value'; | 49 | $conf = new ConfigManager(''); |
50 | $conf->set('plugins.READITYOUSELF_URL', 'value'); | ||
29 | $str = 'http://randomstr.com/test'; | 51 | $str = 'http://randomstr.com/test'; |
30 | $data = array( | 52 | $data = array( |
31 | 'title' => $str, | 53 | 'title' => $str, |
@@ -36,7 +58,7 @@ class PluginReadityourselfTest extends PHPUnit_Framework_TestCase | |||
36 | ) | 58 | ) |
37 | ); | 59 | ); |
38 | 60 | ||
39 | $data = hook_readityourself_render_linklist($data); | 61 | $data = hook_readityourself_render_linklist($data, $conf); |
40 | $link = $data['links'][0]; | 62 | $link = $data['links'][0]; |
41 | // data shouldn't be altered | 63 | // data shouldn't be altered |
42 | $this->assertEquals($str, $data['title']); | 64 | $this->assertEquals($str, $data['title']); |
@@ -52,7 +74,8 @@ class PluginReadityourselfTest extends PHPUnit_Framework_TestCase | |||
52 | */ | 74 | */ |
53 | function testReadityourselfLinklistWithoutConfig() | 75 | function testReadityourselfLinklistWithoutConfig() |
54 | { | 76 | { |
55 | unset($GLOBALS['plugins']['READITYOUSELF_URL']); | 77 | $conf = new ConfigManager(''); |
78 | $conf->set('plugins.READITYOUSELF_URL', null); | ||
56 | $str = 'http://randomstr.com/test'; | 79 | $str = 'http://randomstr.com/test'; |
57 | $data = array( | 80 | $data = array( |
58 | 'title' => $str, | 81 | 'title' => $str, |
@@ -63,7 +86,7 @@ class PluginReadityourselfTest extends PHPUnit_Framework_TestCase | |||
63 | ) | 86 | ) |
64 | ); | 87 | ); |
65 | 88 | ||
66 | $data = hook_readityourself_render_linklist($data); | 89 | $data = hook_readityourself_render_linklist($data, $conf); |
67 | $link = $data['links'][0]; | 90 | $link = $data['links'][0]; |
68 | // data shouldn't be altered | 91 | // data shouldn't be altered |
69 | $this->assertEquals($str, $data['title']); | 92 | $this->assertEquals($str, $data['title']); |
diff --git a/tests/plugins/PluginWallabagTest.php b/tests/plugins/PluginWallabagTest.php index 5d3a60e0..2c268cbd 100644 --- a/tests/plugins/PluginWallabagTest.php +++ b/tests/plugins/PluginWallabagTest.php | |||
@@ -21,11 +21,33 @@ class PluginWallabagTest extends PHPUnit_Framework_TestCase | |||
21 | } | 21 | } |
22 | 22 | ||
23 | /** | 23 | /** |
24 | * Test wallabag init without errors. | ||
25 | */ | ||
26 | function testWallabagInitNoError() | ||
27 | { | ||
28 | $conf = new ConfigManager(''); | ||
29 | $conf->set('plugins.WALLABAG_URL', 'value'); | ||
30 | $errors = wallabag_init($conf); | ||
31 | $this->assertEmpty($errors); | ||
32 | } | ||
33 | |||
34 | /** | ||
35 | * Test wallabag init with errors. | ||
36 | */ | ||
37 | function testWallabagInitError() | ||
38 | { | ||
39 | $conf = new ConfigManager(''); | ||
40 | $errors = wallabag_init($conf); | ||
41 | $this->assertNotEmpty($errors); | ||
42 | } | ||
43 | |||
44 | /** | ||
24 | * Test render_linklist hook. | 45 | * Test render_linklist hook. |
25 | */ | 46 | */ |
26 | function testWallabagLinklist() | 47 | function testWallabagLinklist() |
27 | { | 48 | { |
28 | $GLOBALS['plugins']['WALLABAG_URL'] = 'value'; | 49 | $conf = new ConfigManager(''); |
50 | $conf->set('plugins.WALLABAG_URL', 'value'); | ||
29 | $str = 'http://randomstr.com/test'; | 51 | $str = 'http://randomstr.com/test'; |
30 | $data = array( | 52 | $data = array( |
31 | 'title' => $str, | 53 | 'title' => $str, |
@@ -36,7 +58,7 @@ class PluginWallabagTest extends PHPUnit_Framework_TestCase | |||
36 | ) | 58 | ) |
37 | ); | 59 | ); |
38 | 60 | ||
39 | $data = hook_wallabag_render_linklist($data); | 61 | $data = hook_wallabag_render_linklist($data, $conf); |
40 | $link = $data['links'][0]; | 62 | $link = $data['links'][0]; |
41 | // data shouldn't be altered | 63 | // data shouldn't be altered |
42 | $this->assertEquals($str, $data['title']); | 64 | $this->assertEquals($str, $data['title']); |
@@ -45,7 +67,6 @@ class PluginWallabagTest extends PHPUnit_Framework_TestCase | |||
45 | // plugin data | 67 | // plugin data |
46 | $this->assertEquals(1, count($link['link_plugin'])); | 68 | $this->assertEquals(1, count($link['link_plugin'])); |
47 | $this->assertNotFalse(strpos($link['link_plugin'][0], urlencode($str))); | 69 | $this->assertNotFalse(strpos($link['link_plugin'][0], urlencode($str))); |
48 | $this->assertNotFalse(strpos($link['link_plugin'][0], $GLOBALS['plugins']['WALLABAG_URL'])); | 70 | $this->assertNotFalse(strpos($link['link_plugin'][0], $conf->get('plugins.WALLABAG_URL'))); |
49 | } | 71 | } |
50 | } | 72 | } |
51 | |||
diff --git a/tests/plugins/resources/markdown.html b/tests/plugins/resources/markdown.html new file mode 100644 index 00000000..07a5a32e --- /dev/null +++ b/tests/plugins/resources/markdown.html | |||
@@ -0,0 +1,24 @@ | |||
1 | <div class="markdown"><ul> | ||
2 | <li>test: | ||
3 | <ul> | ||
4 | <li><a href="http://link.tld">zero</a></li> | ||
5 | <li><a href="http://link.tld">two</a></li> | ||
6 | <li><a href="http://link.tld">three</a></li> | ||
7 | </ul></li> | ||
8 | </ul> | ||
9 | <ol> | ||
10 | <li><a href="http://link.tld">zero</a> | ||
11 | <ol> | ||
12 | <li><a href="http://link.tld">two</a></li> | ||
13 | <li><a href="http://link.tld">three</a></li> | ||
14 | <li><a href="http://link.tld">four</a></li> | ||
15 | <li>foo <a href="?addtag=foobar" title="Hashtag foobar">#foobar</a></li> | ||
16 | </ol></li> | ||
17 | </ol> | ||
18 | <p><a href="?addtag=foobar" title="Hashtag foobar">#foobar</a> foo <code>lol #foo</code> <a href="?addtag=bar" title="Hashtag bar">#bar</a></p> | ||
19 | <p>fsdfs <a href="http://link.tld">http://link.tld</a> <a href="?addtag=foobar" title="Hashtag foobar">#foobar</a> <code>http://link.tld</code></p> | ||
20 | <pre><code>http://link.tld #foobar | ||
21 | next #foo</code></pre> | ||
22 | <p>Block:</p> | ||
23 | <pre><code>lorem ipsum #foobar http://link.tld | ||
24 | #foobar http://link.tld</code></pre></div> \ No newline at end of file | ||
diff --git a/tests/plugins/resources/markdown.md b/tests/plugins/resources/markdown.md new file mode 100644 index 00000000..0b8be7c5 --- /dev/null +++ b/tests/plugins/resources/markdown.md | |||
@@ -0,0 +1,24 @@ | |||
1 | * test: | ||
2 | * [zero](http://link.tld) | ||
3 | + [two](http://link.tld) | ||
4 | - [three](http://link.tld) | ||
5 | |||
6 | 1. [zero](http://link.tld) | ||
7 | 2. [two](http://link.tld) | ||
8 | 3. [three](http://link.tld) | ||
9 | 4. [four](http://link.tld) | ||
10 | 5. foo #foobar | ||
11 | |||
12 | #foobar foo `lol #foo` #bar | ||
13 | |||
14 | fsdfs http://link.tld #foobar `http://link.tld` | ||
15 | |||
16 | http://link.tld #foobar | ||
17 | next #foo | ||
18 | |||
19 | Block: | ||
20 | |||
21 | ``` | ||
22 | lorem ipsum #foobar http://link.tld | ||
23 | #foobar http://link.tld | ||
24 | ``` \ No newline at end of file | ||
diff --git a/tests/plugins/test/test.meta b/tests/plugins/test/test.meta index ab999ed4..26f243f0 100644 --- a/tests/plugins/test/test.meta +++ b/tests/plugins/test/test.meta | |||
@@ -1,2 +1,4 @@ | |||
1 | description="test plugin" | 1 | description="test plugin" |
2 | parameters="pop;hip" \ No newline at end of file | 2 | parameters="pop;hip" |
3 | parameter.pop="pop description" | ||
4 | parameter.hip= \ No newline at end of file | ||
diff --git a/tests/utils/ReferenceLinkDB.php b/tests/utils/ReferenceLinkDB.php index dc4f5dfa..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; |
@@ -13,86 +13,111 @@ class ReferenceLinkDB | |||
13 | /** | 13 | /** |
14 | * Populates the test DB with reference data | 14 | * Populates the test DB with reference data |
15 | */ | 15 | */ |
16 | 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.', | 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.', | 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' | 47 | 'free gnu software stallman -exclude stuff hashtag', |
48 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160803_093033') | ||
34 | ); | 49 | ); |
35 | 50 | ||
36 | $this->addLink( | 51 | $this->addLink( |
52 | 7, | ||
37 | 'MediaGoblin', | 53 | 'MediaGoblin', |
38 | 'http://mediagoblin.org/', | 54 | 'http://mediagoblin.org/', |
39 | 'A free software media publishing platform', | 55 | 'A free software media publishing platform #hashtagOther', |
40 | 0, | 56 | 0, |
41 | '20130614_184135', | 57 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20130614_184135'), |
42 | 'gnu media web .hidden' | 58 | 'gnu media web .hidden hashtag', |
59 | null, | ||
60 | 'IuWvgA' | ||
43 | ); | 61 | ); |
44 | 62 | ||
45 | $this->addLink( | 63 | $this->addLink( |
64 | 6, | ||
46 | 'w3c-markup-validator', | 65 | 'w3c-markup-validator', |
47 | 'https://dvcs.w3.org/hg/markup-validator/summary', | 66 | 'https://dvcs.w3.org/hg/markup-validator/summary', |
48 | 'Mercurial repository for the W3C Validator', | 67 | 'Mercurial repository for the W3C Validator #private', |
49 | 1, | 68 | 1, |
50 | '20141125_084734', | 69 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20141125_084734'), |
51 | 'css html w3c web Mercurial' | 70 | 'css html w3c web Mercurial' |
52 | ); | 71 | ); |
53 | 72 | ||
54 | $this->addLink( | 73 | $this->addLink( |
74 | 4, | ||
55 | 'UserFriendly - Web Designer', | 75 | 'UserFriendly - Web Designer', |
56 | 'http://ars.userfriendly.org/cartoons/?id=20121206', | 76 | 'http://ars.userfriendly.org/cartoons/?id=20121206', |
57 | 'Naming conventions...', | 77 | 'Naming conventions... #private', |
58 | 0, | 78 | 0, |
59 | '20121206_142300', | 79 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_142300'), |
60 | 'dev cartoon web' | 80 | 'dev cartoon web' |
61 | ); | 81 | ); |
62 | 82 | ||
63 | $this->addLink( | 83 | $this->addLink( |
84 | 1, | ||
64 | 'UserFriendly - Samba', | 85 | 'UserFriendly - Samba', |
65 | 'http://ars.userfriendly.org/cartoons/?id=20010306', | 86 | 'http://ars.userfriendly.org/cartoons/?id=20010306', |
66 | 'Tropical printing', | 87 | 'Tropical printing', |
67 | 0, | 88 | 0, |
68 | '20121206_172539', | 89 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_172539'), |
69 | 'samba cartoon web' | 90 | 'samba cartoon web' |
70 | ); | 91 | ); |
71 | 92 | ||
72 | $this->addLink( | 93 | $this->addLink( |
94 | 0, | ||
73 | 'Geek and Poke', | 95 | 'Geek and Poke', |
74 | 'http://geek-and-poke.com/', | 96 | 'http://geek-and-poke.com/', |
75 | '', | 97 | '', |
76 | 1, | 98 | 1, |
77 | '20121206_182539', | 99 | DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_182539'), |
78 | 'dev cartoon' | 100 | 'dev cartoon tag1 tag2 tag3 tag4 ' |
79 | ); | 101 | ); |
80 | } | 102 | } |
81 | 103 | ||
82 | /** | 104 | /** |
83 | * Adds a new link | 105 | * Adds a new link |
84 | */ | 106 | */ |
85 | protected function addLink($title, $url, $description, $private, $date, $tags) | 107 | protected function addLink($id, $title, $url, $description, $private, $date, $tags, $updated = '', $shorturl = '') |
86 | { | 108 | { |
87 | $link = array( | 109 | $link = array( |
110 | 'id' => $id, | ||
88 | 'title' => $title, | 111 | 'title' => $title, |
89 | 'url' => $url, | 112 | 'url' => $url, |
90 | 'description' => $description, | 113 | 'description' => $description, |
91 | 'private' => $private, | 114 | 'private' => $private, |
92 | 'linkdate' => $date, | ||
93 | 'tags' => $tags, | 115 | 'tags' => $tags, |
116 | 'created' => $date, | ||
117 | 'updated' => $updated, | ||
118 | 'shorturl' => $shorturl ? $shorturl : smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id), | ||
94 | ); | 119 | ); |
95 | $this->_links[$date] = $link; | 120 | $this->_links[$id] = $link; |
96 | 121 | ||
97 | if ($private) { | 122 | if ($private) { |
98 | $this->_privateCount++; | 123 | $this->_privateCount++; |
@@ -140,4 +165,14 @@ class ReferenceLinkDB | |||
140 | { | 165 | { |
141 | return $this->_links; | 166 | return $this->_links; |
142 | } | 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 | } | ||
143 | } | 178 | } |
diff --git a/tests/utils/config/configInvalid.json.php b/tests/utils/config/configInvalid.json.php new file mode 100644 index 00000000..e39d640a --- /dev/null +++ b/tests/utils/config/configInvalid.json.php | |||
@@ -0,0 +1,5 @@ | |||
1 | <?php /* | ||
2 | { | ||
3 | bad: bad, | ||
4 | } | ||
5 | */ ?> | ||
diff --git a/tests/utils/config/configJson.json.php b/tests/utils/config/configJson.json.php new file mode 100644 index 00000000..06a302e8 --- /dev/null +++ b/tests/utils/config/configJson.json.php | |||
@@ -0,0 +1,34 @@ | |||
1 | <?php /* | ||
2 | { | ||
3 | "credentials": { | ||
4 | "login":"root", | ||
5 | "hash":"hash", | ||
6 | "salt":"salt" | ||
7 | }, | ||
8 | "security": { | ||
9 | "session_protection_disabled":false | ||
10 | }, | ||
11 | "general": { | ||
12 | "timezone":"Europe\/Paris", | ||
13 | "title": "Shaarli", | ||
14 | "header_link": "?" | ||
15 | }, | ||
16 | "privacy": { | ||
17 | "default_private_links":true | ||
18 | }, | ||
19 | "redirector": { | ||
20 | "url":"lala" | ||
21 | }, | ||
22 | "config": { | ||
23 | "foo": "bar" | ||
24 | }, | ||
25 | "resource": { | ||
26 | "datastore": "tests\/utils\/config\/datastore.php", | ||
27 | "data_dir": "tests\/utils\/config" | ||
28 | }, | ||
29 | "plugins": { | ||
30 | "WALLABAG_VERSION": 1 | ||
31 | } | ||
32 | } | ||
33 | */ ?> | ||
34 | |||
diff --git a/tests/utils/config/configPhp.php b/tests/utils/config/configPhp.php new file mode 100644 index 00000000..0e034175 --- /dev/null +++ b/tests/utils/config/configPhp.php | |||
@@ -0,0 +1,14 @@ | |||
1 | <?php | ||
2 | $GLOBALS['login'] = 'root'; | ||
3 | $GLOBALS['hash'] = 'hash'; | ||
4 | $GLOBALS['salt'] = 'salt'; | ||
5 | $GLOBALS['timezone'] = 'Europe/Paris'; | ||
6 | $GLOBALS['title'] = 'title'; | ||
7 | $GLOBALS['titleLink'] = 'titleLink'; | ||
8 | $GLOBALS['redirector'] = 'lala'; | ||
9 | $GLOBALS['disablesessionprotection'] = false; | ||
10 | $GLOBALS['privateLinkByDefault'] = false; | ||
11 | $GLOBALS['config']['DATADIR'] = 'tests/Updater'; | ||
12 | $GLOBALS['config']['PAGECACHE'] = 'sandbox/pagecache'; | ||
13 | $GLOBALS['config']['DATASTORE'] = 'data/datastore.php'; | ||
14 | $GLOBALS['plugins']['WALLABAG_VERSION'] = '1'; | ||
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/configure.html b/tpl/configure.html index 77c8b7d9..983bcd08 100644 --- a/tpl/configure.html +++ b/tpl/configure.html | |||
@@ -3,48 +3,90 @@ | |||
3 | <head>{include="includes"}</head> | 3 | <head>{include="includes"}</head> |
4 | <body onload="document.configform.title.focus();"> | 4 | <body onload="document.configform.title.focus();"> |
5 | <div id="pageheader"> | 5 | <div id="pageheader"> |
6 | {include="page.header"} | 6 | {include="page.header"} |
7 | {$timezone_js} | 7 | {$timezone_js} |
8 | <form method="POST" action="#" name="configform" id="configform"> | 8 | <form method="POST" action="#" name="configform" id="configform"> |
9 | <input type="hidden" name="token" value="{$token}"> | 9 | <input type="hidden" name="token" value="{$token}"> |
10 | <table id="configuration_table"> | 10 | <table id="configuration_table"> |
11 | 11 | ||
12 | <tr><td><b>Page title:</b></td><td><input type="text" name="title" id="title" size="50" value="{$title}"></td></tr> | 12 | <tr> |
13 | <td><b>Page title:</b></td> | ||
14 | <td><input type="text" name="title" id="title" size="50" value="{$title}"></td> | ||
15 | </tr> | ||
13 | 16 | ||
14 | <tr><td><b>Title link:</b></td><td><input type="text" name="titleLink" id="titleLink" size="50" value="{$titleLink}"><br/><label for="titleLink">(default value is: ?)</label></td></tr> | 17 | <tr> |
15 | <tr><td><b>Timezone:</b></td><td>{$timezone_form}</td></tr> | 18 | <td><b>Title link:</b></td> |
19 | <td><input type="text" name="titleLink" id="titleLink" size="50" value="{$titleLink}"><br/><label | ||
20 | for="titleLink">(default value is: ?)</label></td> | ||
21 | </tr> | ||
22 | <tr> | ||
23 | <td><b>Timezone:</b></td> | ||
24 | <td>{$timezone_form}</td> | ||
25 | </tr> | ||
16 | 26 | ||
17 | <tr><td><b>Redirector</b></td><td><input type="text" name="redirector" id="redirector" size="50" value="{$redirector}"><br>(e.g. <i>http://anonym.to/?</i> will mask the HTTP_REFERER)</td></tr> | 27 | <tr> |
28 | <td><b>Redirector</b></td> | ||
29 | <td> | ||
30 | <input type="text" name="redirector" id="redirector" size="50" value="{$redirector}"><br> | ||
31 | (e.g. <i>http://anonym.to/?</i> will mask the HTTP_REFERER) | ||
32 | </td> | ||
33 | </tr> | ||
18 | 34 | ||
19 | <tr><td><b>Security:</b></td><td><input type="checkbox" name="disablesessionprotection" id="disablesessionprotection" {if="!empty($GLOBALS['disablesessionprotection'])"}checked{/if}><label for="disablesessionprotection"> Disable session cookie hijacking protection (Check this if you get disconnected often or if your IP address changes often.)</label></td></tr> | 35 | <tr> |
36 | <td><b>Security:</b></td> | ||
37 | <td> | ||
38 | <input type="checkbox" name="disablesessionprotection" id="disablesessionprotection" | ||
39 | {if="$session_protection_disabled"}checked{/if}> | ||
40 | <label | ||
41 | for="disablesessionprotection"> Disable session cookie hijacking protection (Check this if you get | ||
42 | disconnected often or if your IP address changes often.)</label> | ||
43 | </td> | ||
44 | </tr> | ||
20 | 45 | ||
21 | <tr><td valign="top"><b>New link:</b></td><td> | 46 | <tr> |
22 | <input type="checkbox" name="privateLinkByDefault" id="privateLinkByDefault" {if="!empty($GLOBALS['privateLinkByDefault'])"}checked{/if}/><label for="privateLinkByDefault"> All new links are private by default</label></td> | 47 | <td valign="top"><b>New link:</b></td> |
23 | </tr> | 48 | <td> |
24 | <tr> | 49 | <input type="checkbox" name="privateLinkByDefault" id="privateLinkByDefault" |
25 | <td valign="top"><b>RSS direct links</b></td> | 50 | {if="$private_links_default"}checked{/if}/> |
26 | <td> | 51 | <label for="privateLinkByDefault"> |
27 | <input type="checkbox" name="enableRssPermalinks" id="enableRssPermalinks" {if="!empty($GLOBALS['config']['ENABLE_RSS_PERMALINKS'])"}checked{/if}/> | 52 | All new links are private by default |
28 | <label for="enableRssPermalinks"> | 53 | </label> |
29 | Disable it to use permalinks in RSS feed instead of direct links to your shaared links. Currently <b>{if="$GLOBALS['config']['ENABLE_RSS_PERMALINKS']"}enabled{else}disabled{/if}.</b> | 54 | </td> |
30 | </label> | 55 | </tr> |
31 | </td> | 56 | <tr> |
32 | </tr> | 57 | <td valign="top"><b>RSS direct links</b></td> |
33 | <tr> | 58 | <td> |
34 | <td valign="top"><b>Hide public links</b></td> | 59 | <input type="checkbox" name="enableRssPermalinks" id="enableRssPermalinks" |
35 | <td> | 60 | {if="$enable_rss_permalinks"}checked{/if}/> |
36 | <input type="checkbox" name="hidePublicLinks" id="hidePublicLinks" {if="!empty($GLOBALS['config']['HIDE_PUBLIC_LINKS'])"}checked{/if}/><label for="hidePublicLinks"> | 61 | <label for="enableRssPermalinks"> |
37 | Do not show any links if the user is not logged in.</label> | 62 | Disable it to use permalinks in RSS feed instead of direct links to your shaared links. Currently <b> |
38 | </td> | 63 | {if="$enable_rss_permalinks"}enabled{else}disabled{/if}.</b> |
39 | </tr> | 64 | </label> |
40 | <tr><td valign="top"><b>Update:</b></td><td> | 65 | </td> |
41 | <input type="checkbox" name="updateCheck" id="updateCheck" {if="!empty($GLOBALS['config']['ENABLE_UPDATECHECK'])"}checked{/if}/> | 66 | </tr> |
42 | <label for="updateCheck"> Notify me when a new release is ready</label></td> | 67 | <tr> |
43 | </tr> | 68 | <td valign="top"><b>Hide public links</b></td> |
69 | <td> | ||
70 | <input type="checkbox" name="hidePublicLinks" id="hidePublicLinks" | ||
71 | {if="$hide_public_links"}checked{/if}/> | ||
72 | <label for="hidePublicLinks"> Do not show any links if the user is not logged in.</label> | ||
73 | </td> | ||
74 | </tr> | ||
75 | <tr> | ||
76 | <td valign="top"><b>Update:</b></td> | ||
77 | <td> | ||
78 | <input type="checkbox" name="updateCheck" id="updateCheck" | ||
79 | {if="$enable_update_check"}checked{/if}/> | ||
80 | <label for="updateCheck"> Notify me when a new release is ready</label> | ||
81 | </td> | ||
82 | </tr> | ||
44 | 83 | ||
45 | <tr><td></td><td class="right"><input type="submit" name="Save" value="Save config" class="bigbutton"></td></tr> | 84 | <tr> |
46 | </table> | 85 | <td></td> |
47 | </form> | 86 | <td class="right"><input type="submit" name="Save" value="Save config" class="bigbutton"></td> |
87 | </tr> | ||
88 | </table> | ||
89 | </form> | ||
48 | </div> | 90 | </div> |
49 | {include="page.footer"} | 91 | {include="page.footer"} |
50 | </body> | 92 | </body> |
diff --git a/tpl/daily.html b/tpl/daily.html index 063dc89a..eba0af3b 100644 --- a/tpl/daily.html +++ b/tpl/daily.html | |||
@@ -42,25 +42,25 @@ | |||
42 | <div class="clear"></div> | 42 | <div class="clear"></div> |
43 | 43 | ||
44 | {if="$linksToDisplay"} | 44 | {if="$linksToDisplay"} |
45 | {loop="cols"} | 45 | {loop="$cols"} |
46 | {if="isset($value[0])"} | 46 | {if="isset($value[0])"} |
47 | <div id="daily_col{$counter+1}"> | 47 | <div id="daily_col{$counter+1}"> |
48 | {loop="value"} | 48 | {loop="$value"} |
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="!$GLOBALS['config']['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"} |
62 | <div class="dailyEntryTags"> | 62 | <div class="dailyEntryTags"> |
63 | {loop="link.taglist"} | 63 | {loop="$link.taglist"} |
64 | {$value} - | 64 | {$value} - |
65 | {/loop} | 65 | {/loop} |
66 | </div> | 66 | </div> |
diff --git a/tpl/dailyrss.html b/tpl/dailyrss.html index 4133ca3e..ddbd6c5e 100644 --- a/tpl/dailyrss.html +++ b/tpl/dailyrss.html | |||
@@ -4,9 +4,9 @@ | |||
4 | <link>{$absurl}</link> | 4 | <link>{$absurl}</link> |
5 | <pubDate>{$rssdate}</pubDate> | 5 | <pubDate>{$rssdate}</pubDate> |
6 | <description><![CDATA[ | 6 | <description><![CDATA[ |
7 | {loop="links"} | 7 | {loop="$links"} |
8 | <h3><a href="{$value.url}">{$value.title}</a></h3> | 8 | <h3><a href="{$value.url}">{$value.title}</a></h3> |
9 | <small>{if="!$GLOBALS['config']['HIDE_TIMESTAMPS']"}{function="strftime('%c', $value.timestamp)"} - {/if}{if="$value.tags"}{$value.tags}{/if}<br> | 9 | <small>{if="!$hide_timestamps"}{function="strftime('%c', $value.timestamp)"} - {/if}{if="$value.tags"}{$value.tags}{/if}<br> |
10 | {$value.url}</small><br> | 10 | {$value.url}</small><br> |
11 | {if="$value.thumbnail"}{$value.thumbnail}{/if}<br> | 11 | {if="$value.thumbnail"}{$value.thumbnail}{/if}<br> |
12 | {if="$value.description"}{$value.formatedDescription}{/if} | 12 | {if="$value.description"}{$value.formatedDescription}{/if} |
diff --git a/tpl/editlink.html b/tpl/editlink.html index 14a2e6c8..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,30 +30,28 @@ | |||
25 | {$value} | 30 | {$value} |
26 | {/loop} | 31 | {/loop} |
27 | 32 | ||
28 | {if="($link_is_new && $GLOBALS['privateLinkByDefault']==true) || $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"} |
45 | {/if} | 50 | {/if} |
46 | {if="($GLOBALS['config']['OPEN_SHAARLI'] || isLoggedIn())"} | ||
47 | <script src="inc/awesomplete.min.js#"></script> | 51 | <script src="inc/awesomplete.min.js#"></script> |
48 | <script src="inc/awesomplete-multiple-tags.js#"></script> | 52 | <script src="inc/awesomplete-multiple-tags.js#"></script> |
49 | <script> | 53 | <script> |
50 | awesompleteUniqueTag('#lf_tags'); | 54 | awesompleteUniqueTag('#lf_tags'); |
51 | </script> | 55 | </script> |
52 | {/if} | ||
53 | </body> | 56 | </body> |
54 | </html> | 57 | </html> |
diff --git a/tpl/export.bookmarks.html b/tpl/export.bookmarks.html index da733257..127a5c20 100644 --- a/tpl/export.bookmarks.html +++ b/tpl/export.bookmarks.html | |||
@@ -5,6 +5,6 @@ | |||
5 | Do Not Edit! -->{ignore}The RainTPL loop is formatted to avoid generating extra newlines{/ignore} | 5 | Do Not Edit! -->{ignore}The RainTPL loop is formatted to avoid generating extra newlines{/ignore} |
6 | <TITLE>{$pagetitle}</TITLE> | 6 | <TITLE>{$pagetitle}</TITLE> |
7 | <H1>Shaarli export of {$selection} bookmarks on {$date}</H1> | 7 | <H1>Shaarli export of {$selection} bookmarks on {$date}</H1> |
8 | <DL><p>{loop="links"} | 8 | <DL><p>{loop="$links"} |
9 | <DT><A HREF="{$value.url}" ADD_DATE="{$value.timestamp}" PRIVATE="{$value.private}" TAGS="{$value.taglist}">{$value.title}</A>{if="$value.description"}{$eol}<DD>{$value.description}{/if}{/loop} | 9 | <DT><A HREF="{$value.url}" ADD_DATE="{$value.timestamp}" PRIVATE="{$value.private}" TAGS="{$value.taglist}">{$value.title}</A>{if="$value.description"}{$eol}<DD>{$value.description}{/if}{/loop} |
10 | </DL><p> | 10 | </DL><p> |
diff --git a/tpl/feed.atom.html b/tpl/feed.atom.html index 2ebb162a..aead0459 100644 --- a/tpl/feed.atom.html +++ b/tpl/feed.atom.html | |||
@@ -17,7 +17,7 @@ | |||
17 | </author> | 17 | </author> |
18 | <id>{$index_url}</id> | 18 | <id>{$index_url}</id> |
19 | <generator>Shaarli</generator> | 19 | <generator>Shaarli</generator> |
20 | {loop="links"} | 20 | {loop="$links"} |
21 | <entry> | 21 | <entry> |
22 | <title>{$value.title}</title> | 22 | <title>{$value.title}</title> |
23 | {if="$usepermalinks"} | 23 | {if="$usepermalinks"} |
@@ -27,11 +27,10 @@ | |||
27 | {/if} | 27 | {/if} |
28 | <id>{$value.guid}</id> | 28 | <id>{$value.guid}</id> |
29 | {if="$show_dates"} | 29 | {if="$show_dates"} |
30 | <updated>{$value.iso_date}</updated> | 30 | <published>{$value.pub_iso_date}</published> |
31 | <updated>{$value.up_iso_date}</updated> | ||
31 | {/if} | 32 | {/if} |
32 | <content type="html" xml:lang="{$language}"> | 33 | <content type="html" xml:lang="{$language}"><![CDATA[{$value.description}]]></content> |
33 | <![CDATA[{$value.description}]]> | ||
34 | </content> | ||
35 | {loop="$value.taglist"} | 34 | {loop="$value.taglist"} |
36 | <category scheme="{$index_url}?searchtags=" term="{$value|strtolower}" label="{$value}" /> | 35 | <category scheme="{$index_url}?searchtags=" term="{$value|strtolower}" label="{$value}" /> |
37 | {/loop} | 36 | {/loop} |
diff --git a/tpl/feed.rss.html b/tpl/feed.rss.html index 26de7f19..e18dbf9b 100644 --- a/tpl/feed.rss.html +++ b/tpl/feed.rss.html | |||
@@ -12,7 +12,7 @@ | |||
12 | <!-- PubSubHubbub Discovery --> | 12 | <!-- PubSubHubbub Discovery --> |
13 | <atom:link rel="hub" href="{$pubsubhub_url}" /> | 13 | <atom:link rel="hub" href="{$pubsubhub_url}" /> |
14 | {/if} | 14 | {/if} |
15 | {loop="links"} | 15 | {loop="$links"} |
16 | <item> | 16 | <item> |
17 | <title>{$value.title}</title> | 17 | <title>{$value.title}</title> |
18 | <guid isPermaLink="{if="$usepermalinks"}true{else}false{/if}">{$value.guid}</guid> | 18 | <guid isPermaLink="{if="$usepermalinks"}true{else}false{/if}">{$value.guid}</guid> |
@@ -22,7 +22,8 @@ | |||
22 | <link>{$value.url}</link> | 22 | <link>{$value.url}</link> |
23 | {/if} | 23 | {/if} |
24 | {if="$show_dates"} | 24 | {if="$show_dates"} |
25 | <pubDate>{$value.iso_date}</pubDate> | 25 | <pubDate>{$value.pub_iso_date}</pubDate> |
26 | <atom:modified>{$value.up_iso_date}</atom:modified> | ||
26 | {/if} | 27 | {/if} |
27 | <description><![CDATA[{$value.description}]]></description> | 28 | <description><![CDATA[{$value.description}]]></description> |
28 | {loop="$value.taglist"} | 29 | {loop="$value.taglist"} |
diff --git a/tpl/import.html b/tpl/import.html index 6c4f9421..071e1160 100644 --- a/tpl/import.html +++ b/tpl/import.html | |||
@@ -3,19 +3,31 @@ | |||
3 | <head>{include="includes"}</head> | 3 | <head>{include="includes"}</head> |
4 | <body onload="document.uploadform.filetoupload.focus();"> | 4 | <body onload="document.uploadform.filetoupload.focus();"> |
5 | <div id="pageheader"> | 5 | <div id="pageheader"> |
6 | {include="page.header"} | 6 | {include="page.header"} |
7 | <div id="uploaddiv"> | 7 | <div id="uploaddiv"> |
8 | Import Netscape HTML bookmarks (as exported from Firefox/Chrome/Opera/Delicious/Diigo...) (Max: {$maxfilesize} bytes). | 8 | Import Netscape HTML bookmarks (as exported from Firefox/Chrome/Opera/Delicious/Diigo...) (Max: {$maxfilesize} bytes). |
9 | <form method="POST" action="?do=upload" enctype="multipart/form-data" name="uploadform" id="uploadform"> | 9 | <form method="POST" action="?do=import" enctype="multipart/form-data" |
10 | <input type="hidden" name="token" value="{$token}"> | 10 | name="uploadform" id="uploadform"> |
11 | <input type="file" name="filetoupload"> | 11 | <input type="hidden" name="token" value="{$token}"> |
12 | <input type="hidden" name="MAX_FILE_SIZE" value="{$maxfilesize}"> | 12 | <input type="hidden" name="MAX_FILE_SIZE" value="{$maxfilesize}"> |
13 | <input type="submit" name="import_file" value="Import" class="bigbutton"><br> | 13 | <input type="file" name="filetoupload"> |
14 | <input type="checkbox" name="private" id="private"><label for="private"> Import all links as private</label><br> | 14 | <input type="submit" name="import_file" value="Import" class="bigbutton"><br> |
15 | <input type="checkbox" name="overwrite" id="overwrite"><label for="overwrite"> Overwrite existing links</label> | 15 | |
16 | </form> | 16 | <label for="privacy"> Visibility:</label><br> |
17 | </div> | 17 | <input type="radio" name="privacy" value="default" checked="true"> |
18 | Use values from the imported file, default to public<br> | ||
19 | <input type="radio" name="privacy" value="private"> | ||
20 | Import all bookmarks as private<br> | ||
21 | <input type="radio" name="privacy" value="public"> | ||
22 | Import all bookmarks as public<br> | ||
23 | |||
24 | <input type="checkbox" name="overwrite" id="overwrite"> | ||
25 | <label for="overwrite"> Overwrite existing bookmarks</label><br> | ||
26 | <label for="default_tags"> Add default tags</label> | ||
27 | <input type="text" name="default_tags" id="default_tags"> | ||
28 | </form> | ||
29 | </div> | ||
18 | </div> | 30 | </div> |
19 | {include="page.footer"} | 31 | {include="page.footer"} |
20 | </body> | 32 | </body> |
21 | </html> \ No newline at end of file | 33 | </html> |
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 c0d42006..0f1a5e8c 100644 --- a/tpl/linklist.html +++ b/tpl/linklist.html | |||
@@ -28,7 +28,17 @@ | |||
28 | <input type="submit" value="Search" class="bigbutton"> | 28 | <input type="submit" value="Search" class="bigbutton"> |
29 | </form> | 29 | </form> |
30 | {loop="$plugins_header.fields_toolbar"} | 30 | {loop="$plugins_header.fields_toolbar"} |
31 | {$value} | 31 | <form |
32 | {loop="$value.attr"} | ||
33 | {$key}="{$value}" | ||
34 | {/loop}> | ||
35 | {loop="$value.inputs"} | ||
36 | <input | ||
37 | {loop="$value"} | ||
38 | {$key}="{$value}" | ||
39 | {/loop}> | ||
40 | {/loop} | ||
41 | </form> | ||
32 | {/loop} | 42 | {/loop} |
33 | </div> | 43 | </div> |
34 | </div> | 44 | </div> |
@@ -63,7 +73,7 @@ | |||
63 | </div> | 73 | </div> |
64 | {/if} | 74 | {/if} |
65 | <ul> | 75 | <ul> |
66 | {loop="links"} | 76 | {loop="$links"} |
67 | <li{if="$value.class"} class="{$value.class}"{/if}> | 77 | <li{if="$value.class"} class="{$value.class}"{/if}> |
68 | <a id="{$value.shorturl}"></a> | 78 | <a id="{$value.shorturl}"></a> |
69 | <div class="thumbnail">{$value.url|thumbnail}</div> | 79 | <div class="thumbnail">{$value.url|thumbnail}</div> |
@@ -71,11 +81,11 @@ | |||
71 | {if="isLoggedIn()"} | 81 | {if="isLoggedIn()"} |
72 | <div class="linkeditbuttons"> | 82 | <div class="linkeditbuttons"> |
73 | <form method="GET" class="buttoneditform"> | 83 | <form method="GET" class="buttoneditform"> |
74 | <input type="hidden" name="edit_link" value="{$value.linkdate}"> | 84 | <input type="hidden" name="edit_link" value="{$value.id}"> |
75 | <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"> |
76 | </form><br> | 86 | </form><br> |
77 | <form method="POST" class="buttoneditform"> | 87 | <form method="POST" class="buttoneditform"> |
78 | <input type="hidden" name="lf_linkdate" value="{$value.linkdate}"> | 88 | <input type="hidden" name="lf_linkdate" value="{$value.id}"> |
79 | <input type="hidden" name="token" value="{$token}"> | 89 | <input type="hidden" name="token" value="{$token}"> |
80 | <input type="hidden" name="delete_link"> | 90 | <input type="hidden" name="delete_link"> |
81 | <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" |
@@ -88,8 +98,17 @@ | |||
88 | </span> | 98 | </span> |
89 | <br> | 99 | <br> |
90 | {if="$value.description"}<div class="linkdescription">{$value.description}</div>{/if} | 100 | {if="$value.description"}<div class="linkdescription">{$value.description}</div>{/if} |
91 | {if="!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()"} | 101 | {if="!$hide_timestamps || isLoggedIn()"} |
92 | <span class="linkdate" title="Permalink"><a href="?{$value.linkdate|smallHash}">{function="strftime('%c', $value.timestamp)"} - permalink</a> - </span> | 102 | {$updated=$value.updated_timestamp ? 'Edited: '. strftime('%c', $value.updated_timestamp) : 'Permalink'} |
103 | <span class="linkdate" title="Permalink"> | ||
104 | <a href="?{$value.shorturl}"> | ||
105 | <span title="{$updated}"> | ||
106 | {function="strftime('%c', $value.timestamp)"} | ||
107 | {if="$value.updated_timestamp"}*{/if} | ||
108 | </span> | ||
109 | - permalink | ||
110 | </a> - | ||
111 | </span> | ||
93 | {else} | 112 | {else} |
94 | <span class="linkdate" title="Short link here"><a href="?{$value.shorturl}">permalink</a> - </span> | 113 | <span class="linkdate" title="Short link here"><a href="?{$value.shorturl}">permalink</a> - </span> |
95 | {/if} | 114 | {/if} |
@@ -101,7 +120,7 @@ | |||
101 | <a href="{$value.real_url}"><span class="linkurl" title="Short link">{$value.url}</span></a><br> | 120 | <a href="{$value.real_url}"><span class="linkurl" title="Short link">{$value.url}</span></a><br> |
102 | {if="$value.tags"} | 121 | {if="$value.tags"} |
103 | <div class="linktaglist"> | 122 | <div class="linktaglist"> |
104 | {loop="value.taglist"}<span class="linktag" title="Add tag"><a href="?addtag={$value|urlencode}">{$value}</a></span> {/loop} | 123 | {loop="$value.taglist"}<span class="linktag" title="Add tag"><a href="?addtag={$value|urlencode}">{$value}</a></span> {/loop} |
105 | </div> | 124 | </div> |
106 | {/if} | 125 | {/if} |
107 | 126 | ||
diff --git a/tpl/linklist.paging.html b/tpl/linklist.paging.html index e91c8f86..86019c01 100644 --- a/tpl/linklist.paging.html +++ b/tpl/linklist.paging.html | |||
@@ -13,7 +13,14 @@ | |||
13 | </div> | 13 | </div> |
14 | {/if} | 14 | {/if} |
15 | {loop="$action_plugin"} | 15 | {loop="$action_plugin"} |
16 | {$value} | 16 | <div class="paging_privatelinks"> |
17 | <a | ||
18 | {loop="$value.attr"} | ||
19 | {$key}="{$value}" | ||
20 | {/loop}> | ||
21 | {$value.html} | ||
22 | </a> | ||
23 | </div> | ||
17 | {/loop} | 24 | {/loop} |
18 | <div class="paging_linksperpage"> | 25 | <div class="paging_linksperpage"> |
19 | Links per page: <a href="?linksperpage=20">20</a> <a href="?linksperpage=50">50</a> <a href="?linksperpage=100">100</a> | 26 | Links per page: <a href="?linksperpage=20">20</a> <a href="?linksperpage=50">50</a> <a href="?linksperpage=100">100</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 3a09ecd9..cce61ec4 100644 --- a/tpl/page.header.html +++ b/tpl/page.header.html | |||
@@ -21,21 +21,26 @@ | |||
21 | <li><a href="?do=logout">Logout</a></li> | 21 | <li><a href="?do=logout">Logout</a></li> |
22 | <li><a href="?do=tools">Tools</a></li> | 22 | <li><a href="?do=tools">Tools</a></li> |
23 | <li><a href="?do=addlink">Add link</a></li> | 23 | <li><a href="?do=addlink">Add link</a></li> |
24 | {elseif="$GLOBALS['config']['OPEN_SHAARLI']"} | 24 | {elseif="$openshaarli"} |
25 | <li><a href="?do=tools">Tools</a></li> | 25 | <li><a href="?do=tools">Tools</a></li> |
26 | <li><a href="?do=addlink">Add link</a></li> | 26 | <li><a href="?do=addlink">Add link</a></li> |
27 | {else} | 27 | {else} |
28 | <li><a href="?do=login">Login</a></li> | 28 | <li><a href="?do=login">Login</a></li> |
29 | {/if} | 29 | {/if} |
30 | <li><a href="{$feedurl}?do=rss{$searchcrits}" class="nomobile">RSS Feed</a></li> | 30 | <li><a href="{$feedurl}?do=rss{$searchcrits}" class="nomobile">RSS Feed</a></li> |
31 | {if="$GLOBALS['config']['SHOW_ATOM']"} | 31 | {if="$showatom"} |
32 | <li><a href="{$feedurl}?do=atom{$searchcrits}" class="nomobile">ATOM Feed</a></li> | 32 | <li><a href="{$feedurl}?do=atom{$searchcrits}" class="nomobile">ATOM Feed</a></li> |
33 | {/if} | 33 | {/if} |
34 | <li><a href="?do=tagcloud">Tag cloud</a></li> | 34 | <li><a href="?do=tagcloud">Tag cloud</a></li> |
35 | <li><a href="?do=picwall{$searchcrits}">Picture wall</a></li> | 35 | <li><a href="?do=picwall{$searchcrits}">Picture wall</a></li> |
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 | {$value} | 38 | <li><a |
39 | {loop="$value.attr"} | ||
40 | {$key}="{$value}" | ||
41 | {/loop}> | ||
42 | {$value.html} | ||
43 | </a></li> | ||
39 | {/loop} | 44 | {/loop} |
40 | {/if} | 45 | {/if} |
41 | </ul> | 46 | </ul> |
@@ -43,7 +48,7 @@ | |||
43 | 48 | ||
44 | {if="!empty($plugin_errors) && isLoggedIn()"} | 49 | {if="!empty($plugin_errors) && isLoggedIn()"} |
45 | <ul class="errors"> | 50 | <ul class="errors"> |
46 | {loop="plugin_errors"} | 51 | {loop="$plugin_errors"} |
47 | <li>{$value}</li> | 52 | <li>{$value}</li> |
48 | {/loop} | 53 | {/loop} |
49 | </ul> | 54 | </ul> |
diff --git a/tpl/picwall.html b/tpl/picwall.html index 230c948b..4e227e37 100644 --- a/tpl/picwall.html +++ b/tpl/picwall.html | |||
@@ -14,7 +14,7 @@ | |||
14 | 14 | ||
15 | <div class="center"> | 15 | <div class="center"> |
16 | <div id="picwall_container"> | 16 | <div id="picwall_container"> |
17 | {loop="linksToDisplay"} | 17 | {loop="$linksToDisplay"} |
18 | <div class="picwall_pictureframe"> | 18 | <div class="picwall_pictureframe"> |
19 | {$value.thumbnail}<a href="{$value.real_url}"><span class="info">{$value.title}</span></a> | 19 | {$value.thumbnail}<a href="{$value.real_url}"><span class="info">{$value.title}</span></a> |
20 | {loop="$value.picwall_plugin"} | 20 | {loop="$value.picwall_plugin"} |
diff --git a/tpl/pluginsadmin.html b/tpl/pluginsadmin.html index 5ddcf061..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> |
@@ -104,11 +104,14 @@ | |||
104 | <div class="plugin_parameter"> | 104 | <div class="plugin_parameter"> |
105 | <div class="float_label"> | 105 | <div class="float_label"> |
106 | <label for="{$key}"> | 106 | <label for="{$key}"> |
107 | <code>{$key}</code> | 107 | <code>{$key}</code><br> |
108 | {if="isset($value.desc)"} | ||
109 | {$value.desc} | ||
110 | {/if} | ||
108 | </label> | 111 | </label> |
109 | </div> | 112 | </div> |
110 | <div class="float_input"> | 113 | <div class="float_input"> |
111 | <input name="{$key}" value="{$value}" id="{$key}"/> | 114 | <input name="{$key}" value="{$value.value}" id="{$key}"/> |
112 | </div> | 115 | </div> |
113 | </div> | 116 | </div> |
114 | {/loop} | 117 | {/loop} |
diff --git a/tpl/tagcloud.html b/tpl/tagcloud.html index e449f293..05e45273 100644 --- a/tpl/tagcloud.html +++ b/tpl/tagcloud.html | |||
@@ -11,7 +11,7 @@ | |||
11 | </div> | 11 | </div> |
12 | 12 | ||
13 | <div id="cloudtag"> | 13 | <div id="cloudtag"> |
14 | {loop="tags"} | 14 | {loop="$tags"} |
15 | <span class="count">{$value.count}</span><a | 15 | <span class="count">{$value.count}</span><a |
16 | href="?searchtags={$key|urlencode}" style="font-size:{$value.size}em;">{$key}</a> | 16 | href="?searchtags={$key|urlencode}" style="font-size:{$value.size}em;">{$key}</a> |
17 | {loop="$value.tag_plugin"} | 17 | {loop="$value.tag_plugin"} |
diff --git a/tpl/tools.html b/tpl/tools.html index 78b81663..e06d239d 100644 --- a/tpl/tools.html +++ b/tpl/tools.html | |||
@@ -9,7 +9,7 @@ | |||
9 | <br><br> | 9 | <br><br> |
10 | <a href="?do=pluginadmin"><b>Plugin administration</b><span>: Enable, disable and configure plugins.</span></a> | 10 | <a href="?do=pluginadmin"><b>Plugin administration</b><span>: Enable, disable and configure plugins.</span></a> |
11 | <br><br> | 11 | <br><br> |
12 | {if="!$GLOBALS['config']['OPEN_SHAARLI']"}<a href="?do=changepasswd"><b>Change password</b><span>: Change your password.</span></a> | 12 | {if="!$openshaarli"}<a href="?do=changepasswd"><b>Change password</b><span>: Change your password.</span></a> |
13 | <br><br>{/if} | 13 | <br><br>{/if} |
14 | <a href="?do=changetag"><b>Rename/delete tags</b><span>: Rename or delete a tag in all links</span></a> | 14 | <a href="?do=changetag"><b>Rename/delete tags</b><span>: Rename or delete a tag in all links</span></a> |
15 | <br><br> | 15 | <br><br> |
@@ -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("/")); |
@@ -79,7 +83,7 @@ | |||
79 | icon32URL: baseURL + "/images/favicon.ico", | 83 | icon32URL: baseURL + "/images/favicon.ico", |
80 | icon64URL: baseURL + "/images/favicon.ico", | 84 | icon64URL: baseURL + "/images/favicon.ico", |
81 | 85 | ||
82 | shareURL: baseURL + "{noparse}?post=%{url}&title=%{title}&description=%{description}&source=firefoxsocialapi{/noparse}", | 86 | shareURL: baseURL + "{noparse}?post=%{url}&title=%{title}&description=%{text}&source=firefoxsocialapi{/noparse}", |
83 | homepageURL: baseURL | 87 | homepageURL: baseURL |
84 | }; | 88 | }; |
85 | node.setAttribute("data-service", JSON.stringify(data)); | 89 | node.setAttribute("data-service", JSON.stringify(data)); |
@@ -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; |