diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | COPYING | 66 | ||||
-rw-r--r-- | README.md | 89 | ||||
-rw-r--r-- | cache/.htaccess | 2 | ||||
-rw-r--r-- | data/.htaccess | 2 | ||||
-rw-r--r-- | images/calendar.png | bin | 675 -> 650 bytes | |||
-rw-r--r-- | images/delete_icon.png | bin | 150 -> 302 bytes | |||
-rw-r--r-- | images/edit_icon.png | bin | 394 -> 1548 bytes | |||
-rw-r--r-- | images/feed-icon-14x14.png | bin | 689 -> 658 bytes | |||
-rw-r--r-- | images/floral_left.png | bin | 2468 -> 1284 bytes | |||
-rw-r--r-- | images/floral_right.png | bin | 2447 -> 1309 bytes | |||
-rw-r--r-- | images/private.png | bin | 650 -> 2636 bytes | |||
-rw-r--r-- | images/private_16x16.png | bin | 439 -> 679 bytes | |||
-rw-r--r-- | images/private_16x16_active.png | bin | 475 -> 648 bytes | |||
-rw-r--r-- | images/qrcode.png | bin | 218 -> 321 bytes | |||
-rw-r--r-- | images/squiggle.png | bin | 950 -> 684 bytes | |||
-rw-r--r-- | images/squiggle2.png | bin | 898 -> 720 bytes | |||
-rw-r--r-- | images/squiggle_closing.png | bin | 2878 -> 1244 bytes | |||
-rw-r--r-- | images/tag_blue.png | bin | 586 -> 714 bytes | |||
-rw-r--r-- | inc/qr.min.js | 14 | ||||
-rw-r--r-- | inc/shaarli.css | 19 | ||||
-rw-r--r-- | index.php | 355 | ||||
-rw-r--r-- | pagecache/.htaccess | 2 | ||||
-rw-r--r-- | shaarli_version.txt | 1 | ||||
-rw-r--r-- | tmp/.htaccess | 2 | ||||
-rw-r--r-- | tpl/configure.html | 3 | ||||
-rw-r--r-- | tpl/import.html | 2 | ||||
-rw-r--r-- | tpl/linklist.html | 3 | ||||
-rw-r--r-- | tpl/page.header.html | 6 | ||||
-rw-r--r-- | tpl/tagcloud.html | 2 | ||||
-rw-r--r-- | tpl/tools.html | 2 |
31 files changed, 336 insertions, 239 deletions
@@ -7,4 +7,7 @@ pagecache | |||
7 | # Eclipse project files | 7 | # Eclipse project files |
8 | .settings | 8 | .settings |
9 | .buildpath | 9 | .buildpath |
10 | .project \ No newline at end of file | 10 | .project |
11 | |||
12 | # Ignore raintpl generated pages | ||
13 | *.rtpl.php \ No newline at end of file | ||
@@ -1,6 +1,52 @@ | |||
1 | Shaarli is distributed under the zlib/libpng License: | 1 | Files: * |
2 | License: zlib/libpng | ||
3 | Copyright: (c) 2011-2014 Sébastien SAUVAGE <sebsauvage@sebsauvage.net> | ||
4 | (c) 2011-2014 Alexandre Alapetite <alexandre@alapetite.fr> | ||
5 | (c) 2011-2014 David Sferruzza <david.sferruzza@gmail.com> | ||
6 | (c) 2011-2014 Christophe HENRY <christophe.henry@sbgodin.fr> | ||
7 | (c) 2011-2014 Mathieu Chabanon <git@matchab.fr> | ||
8 | (c) 2011-2014 BoboTiG <bobotig@gmail.com> | ||
9 | (c) 2011-2014 Bronco <bronco@warriordudimanche.net> | ||
10 | (c) 2011-2014 Emilien Klein <emilien@klein.st> | ||
11 | (c) 2011-2014 Knah Tsaeb <knah-tsaeb@knah-tsaeb.org> | ||
12 | (c) 2011-2014 Lionel Martin <renarddesmers@gmail.com> | ||
13 | (c) 2011-2014 lehollandaisvolant <levoltigeurhollandais@gmail.com> | ||
14 | (c) 2011-2014 timo van neerden <fire@lehollandaisvolant.net> | ||
15 | (c) 2011-2014 nodiscc <nodiscc@gmail.com> | ||
16 | |||
17 | |||
18 | |||
19 | Files: images/calendar.png, images/edit_icon.png, images/feed-icon-14x14.png, images/private.png, images/private_16x16.png, images/private_16x16_active.png, images/qrcode.png, images/tag_blue.png | ||
20 | License: CC-BY (http://creativecommons.org/licenses/by/3.0/) | ||
21 | Copyright: (c) 2014 Yusuke Kamiyamane | ||
22 | Source: http://p.yusukekamiyamane.com/ | ||
23 | |||
24 | Files: images/delete_icon.png | ||
25 | License: CC-BY (http://creativecommons.org/licenses/by/3.0/) | ||
26 | Copyright: (c) 2014 Designmodo | ||
27 | Source: http://designmodo.com/linecons-free/ | ||
28 | |||
29 | |||
30 | Files: images/floral_left.png, images/floral_right.png, images/squiggle.png, images/squiggle2.png, images/squiggle_closing.png | ||
31 | Licence: Public Domain | ||
32 | Source: https://openclipart.org/people/j4p4n/j4p4n_ornimental_bookend_-_left.svg | ||
33 | |||
34 | |||
35 | Files: images/Paper_texture_v5_by_bashcorpo_w1000.jpg | ||
36 | Licence: Public Domain | ||
37 | Source: http://bashcorpo.deviantart.com/art/Grungy-paper-texture-v-5-22966998 | ||
38 | |||
39 | Files: images/logo.png | ||
40 | License: zlib/libpng | ||
41 | Copyright: (c) 2011-2014 idleman idleman@idleman.fr | ||
42 | |||
43 | Files: ins/qr.min.js | ||
44 | License: GPLv3 | ||
45 | Copyright: (C) 2014 Alasdair Mercer, http://neocotic.com | ||
46 | |||
47 | -------------------------------------------------------- | ||
48 | ZLIB/LIBPNG LICENSE | ||
2 | 49 | ||
3 | Copyright (c) 2011 Sbastien SAUVAGE (sebsauvage.net) | ||
4 | 50 | ||
5 | This software is provided 'as-is', without any express or implied warranty. | 51 | This software is provided 'as-is', without any express or implied warranty. |
6 | In no event will the authors be held liable for any damages arising from | 52 | In no event will the authors be held liable for any damages arising from |
@@ -19,3 +65,19 @@ freely, subject to the following restrictions: | |||
19 | not be misrepresented as being the original software. | 65 | not be misrepresented as being the original software. |
20 | 66 | ||
21 | 3. This notice may not be removed or altered from any source distribution. | 67 | 3. This notice may not be removed or altered from any source distribution. |
68 | |||
69 | ---------------------------------------------------- | ||
70 | GPLv3 LICENSE | ||
71 | |||
72 | This program is free software: you can redistribute it and/or modify | ||
73 | it under the terms of the GNU General Public License as published by | ||
74 | the Free Software Foundation, either version 3 of the License, or | ||
75 | (at your option) any later version. | ||
76 | |||
77 | This program is distributed in the hope that it will be useful, | ||
78 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
79 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
80 | GNU General Public License for more details. | ||
81 | |||
82 | You should have received a copy of the GNU General Public License | ||
83 | along with this program. If not, see <http://www.gnu.org/licenses/>. \ No newline at end of file | ||
@@ -1,71 +1,76 @@ | |||
1 | ![Shaarli logo](http://sebsauvage.net/wiki/lib/exe/fetch.php?media=php:php_shaarli:php_shaarli_logo_inkscape_w600_transp-nq8.png) | 1 | ![Shaarli logo](https://cdn.mediacru.sh/W2NGCIHB3quT.png) |
2 | 2 | ||
3 | Shaarli, the personal, minimalist, super-fast, no-database delicious clone. | 3 | Shaarli, the personal, minimalist, super-fast, no-database delicious clone. |
4 | 4 | ||
5 | You want to share the links you discover ? Shaarli is a minimalist delicious clone you can install on your own website. | 5 | You want to share the links you discover ? Shaarli is a minimalist delicious clone you can install on your own website. |
6 | It is designed to be personal (single-user), fast and handy. | 6 | It is designed to be personal (single-user), fast and handy. |
7 | 7 | ||
8 | 8 | ||
9 | Features: | 9 | ## Features: |
10 | 10 | ||
11 | * Minimalist design (simple is beautiful) | 11 | * Minimalist design (simple is beautiful) |
12 | * **FAST** | 12 | * **FAST** |
13 | * Dead-simple installation: Drop the files, open the page. No database required. | 13 | * Dead-simple installation: Drop the files, open the page. No database required. |
14 | * Easy to use: Single button in your browser to bookmark a page | 14 | * Easy to use: Single button in your browser to bookmark a page (**bookmarklet**) |
15 | * Save url, title, description (unlimited size). Classify links with tags (with autocomplete) | 15 | * Save **URL, title, description** (unlimited size). |
16 | * Tag renaming, merging and deletion. | 16 | * Classify, search and filter links with **tags**. |
17 | * Automatic thumbnails for various services (imgur, imageshack.us, flickr, youtube, vimeo, dailymotion…) | 17 | * Tag autocompletion, renaming, merging and deletion. |
18 | * Automatic conversion of URLs to clickable links in descriptions. Support for http/ftp/file/apt/magnet protocols. | 18 | * Save links as **public or private** |
19 | * Save links as public or private | 19 | * Browse links by page, filter by tag or use the **full text search engine** |
20 | * 1-clic access to your private links/notes | 20 | * **Tag cloud** |
21 | * Browse links by page, filter by tag or use the full text search engine | 21 | * **Picture wall** (which can be filtered by tag or text search) |
22 | * Permalinks (with QR-Code) for easy reference | 22 | * **“Daily”** Newspaper-like digest, browsable by day. |
23 | * RSS and ATOM feeds (which can be filtered by tag or text search) | 23 | * **Permalinks** (with QR-Code) for easy reference |
24 | * Tag cloud | 24 | * **RSS** and ATOM feeds |
25 | * Picture wall (which can be filtered by tag or text search) | 25 | * Can be filtered by tag or text search! |
26 | * “Links of the day” Newspaper-like digest, browsable by day. | 26 | * “Daily” RSS feed: Get each day a digest of all new links. |
27 | * “Daily” RSS feed: Get each day a digest of all new links. | 27 | * Can **import/export** Netscape bookmarks (for import/export from/to Firefox, Opera, Chrome, Delicious…) |
28 | * [PubSubHubbub](https://code.google.com/p/pubsubhubbub/) protocol support | 28 | * Automatic **image/video thumbnails** for various services (imgur, imageshack.us, flickr, youtube, vimeo, dailymotion…) |
29 | * Support for http/ftp/file/apt/magnet protocol links | ||
30 | * URLs in descriptions are automatically converted to clickable links in descriptions | ||
29 | * Easy backup (Data stored in a single file) | 31 | * Easy backup (Data stored in a single file) |
32 | * 1-click access to your private links/notes | ||
30 | * Compact storage (1315 links stored in 150 kb) | 33 | * Compact storage (1315 links stored in 150 kb) |
31 | * Mobile browsers support | 34 | * Mobile browsers support |
32 | * Also works with javascript disabled | 35 | * Also works with javascript disabled |
33 | * Can import/export Netscape bookmarks (for import/export from/to Firefox, Opera, Chrome, Delicious…) | ||
34 | * Brute force protected login form | 36 | * Brute force protected login form |
35 | * Protected against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery), session cookie hijacking. | 37 | * [PubSubHubbub](https://code.google.com/p/pubsubhubbub/) protocol support |
36 | * Automatic removal of annoying FeedBurner/Google FeedProxy parameters in URL (?utm_source…) | 38 | * Automatic removal of annoying FeedBurner/Google FeedProxy parameters in URL (?utm_source…) |
37 | * Shaarli is a bookmarking application, but you can use it for micro-blogging (like Twitter), a pastebin, an online notepad, a snippet repository, etc. | ||
38 | * You will be automatically notified by a discreet popup if a new version is available | ||
39 | * Pages are easy to customize (using CSS and simple RainTPL templates) | 39 | * Pages are easy to customize (using CSS and simple RainTPL templates) |
40 | * Protected against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery), session cookie hijacking. | ||
41 | * You will be automatically notified by a discreet popup if a new version is available | ||
42 | * **Shaarli is a bookmarking application, but you can use it for micro-blogging (like Twitter), a pastebin, an online notepad, a snippet repository, etc. See [Usage examples](https://github.com/shaarli/Shaarli/wiki#usage-examples)** | ||
43 | |||
44 | ## Links | ||
45 | * **[Wiki/documentation](https://github.com/shaarli/Shaarli/wiki)** | ||
46 | * [Bugs/Feature requests/Discussion](https://github.com/shaarli/Shaarli/issues/) | ||
47 | |||
40 | 48 | ||
49 | ## Installing | ||
50 | Shaarli requires php 5.1 | ||
41 | 51 | ||
42 | Requires php 5.1 | 52 | * Download the latest stable release from https://github.com/shaarli/Shaarli/releases |
53 | * Unpack the archive in a directory on your web server | ||
54 | * Visit this directory from a web browser. | ||
55 | * Choose login, password, timezone and page title. Save. | ||
43 | 56 | ||
44 | More information on the project page: | 57 | _To get the development version, download https://github.com/shaarli/Shaarli/archive/master.zip or `git clone https://github.com/shaarli/Shaarli`_ |
45 | http://sebsauvage.net/wiki/doku.php?id=php:shaarli | ||
46 | 58 | ||
47 | ------------------------------------------------------------------------------ | 59 | ## Upgrading |
60 | Delete all files and directories except the `data` directory, then unzip the new version of Shaarli. | ||
61 | You will not lose your links and you will not have to reconfigure it. | ||
48 | 62 | ||
49 | Shaarli is distributed under the zlib/libpng License: | ||
50 | 63 | ||
51 | Copyright (c) 2011 Sébastien SAUVAGE (sebsauvage.net) | 64 | ## Screenshots |
52 | 65 | ||
53 | This software is provided 'as-is', without any express or implied warranty. | 66 | [![](https://cdn.mediacru.sh/AjZc6-emICeO.png)](https://cdn.mediacru.sh/kE8SyD-PvGuC.png) [![](https://cdn.mediacru.sh/MfC-DzklMYs2.png)](https://cdn.mediacru.sh/iqTvO1-yP9pU.png) [![](https://cdn.mediacru.sh/dxmXskaubYcg.png)](https://cdn.mediacru.sh/mMoi31f94wdL.png) [![](https://cdn.mediacru.sh/-ptB2veFivBp.png)](https://cdn.mediacru.sh/GcoZPZmCZ-DR.png) [![](https://cdn.mediacru.sh/QmRdTAr8x427.png)](https://cdn.mediacru.sh/TDDujpMWT31q.png) |
54 | In no event will the authors be held liable for any damages arising from | ||
55 | the use of this software. | ||
56 | 67 | ||
57 | Permission is granted to anyone to use this software for any purpose, | 68 | ## About |
58 | including commercial applications, and to alter it and redistribute it | ||
59 | freely, subject to the following restrictions: | ||
60 | 69 | ||
61 | 1. The origin of this software must not be misrepresented; you must not | 70 | Original Project page: http://sebsauvage.net/wiki/doku.php?id=php:shaarli |
62 | claim that you wrote the original software. If you use this software | 71 | Shaarli is developed by [Sébastien SAUVAGE](http://sebsauvage.net) and [contributors](COPYING). |
63 | in a product, an acknowledgment in the product documentation would | ||
64 | be appreciated but is not required. | ||
65 | 72 | ||
66 | 2. Altered source versions must be plainly marked as such, and must | 73 | Shaarli is [Free Software](https://en.wikipedia.org/wiki/Free_software) distributed under the [zlib/libpng License](http://www.gzip.org/zlib/zlib_license.html) |
67 | not be misrepresented as being the original software. | ||
68 | 74 | ||
69 | 3. This notice may not be removed or altered from any source distribution. | 75 | This friendly fork is maintained by the community at https://github.com/shaarli/Shaarli |
70 | 76 | ||
71 | ------------------------------------------------------------------------------ | ||
diff --git a/cache/.htaccess b/cache/.htaccess new file mode 100644 index 00000000..b584d98c --- /dev/null +++ b/cache/.htaccess | |||
@@ -0,0 +1,2 @@ | |||
1 | Allow from none | ||
2 | Deny from all | ||
diff --git a/data/.htaccess b/data/.htaccess new file mode 100644 index 00000000..b584d98c --- /dev/null +++ b/data/.htaccess | |||
@@ -0,0 +1,2 @@ | |||
1 | Allow from none | ||
2 | Deny from all | ||
diff --git a/images/calendar.png b/images/calendar.png index 65891385..81c74519 100644 --- a/images/calendar.png +++ b/images/calendar.png | |||
Binary files differ | |||
diff --git a/images/delete_icon.png b/images/delete_icon.png index 55e388b4..810b94d8 100644 --- a/images/delete_icon.png +++ b/images/delete_icon.png | |||
Binary files differ | |||
diff --git a/images/edit_icon.png b/images/edit_icon.png index 5cff5743..16c440c8 100644 --- a/images/edit_icon.png +++ b/images/edit_icon.png | |||
Binary files differ | |||
diff --git a/images/feed-icon-14x14.png b/images/feed-icon-14x14.png index b3c949d2..10161702 100644 --- a/images/feed-icon-14x14.png +++ b/images/feed-icon-14x14.png | |||
Binary files differ | |||
diff --git a/images/floral_left.png b/images/floral_left.png index 5a4ad89b..f09a861d 100644 --- a/images/floral_left.png +++ b/images/floral_left.png | |||
Binary files differ | |||
diff --git a/images/floral_right.png b/images/floral_right.png index cb7b201f..0dfb6112 100644 --- a/images/floral_right.png +++ b/images/floral_right.png | |||
Binary files differ | |||
diff --git a/images/private.png b/images/private.png index 5cea272a..1364b355 100644 --- a/images/private.png +++ b/images/private.png | |||
Binary files differ | |||
diff --git a/images/private_16x16.png b/images/private_16x16.png index d58c4823..8bb34d7d 100644 --- a/images/private_16x16.png +++ b/images/private_16x16.png | |||
Binary files differ | |||
diff --git a/images/private_16x16_active.png b/images/private_16x16_active.png index dd43baf2..af990d2c 100644 --- a/images/private_16x16_active.png +++ b/images/private_16x16_active.png | |||
Binary files differ | |||
diff --git a/images/qrcode.png b/images/qrcode.png index 99f25267..c2cfa476 100644 --- a/images/qrcode.png +++ b/images/qrcode.png | |||
Binary files differ | |||
diff --git a/images/squiggle.png b/images/squiggle.png index 9fd2129d..a6ce218c 100644 --- a/images/squiggle.png +++ b/images/squiggle.png | |||
Binary files differ | |||
diff --git a/images/squiggle2.png b/images/squiggle2.png index 23409ce3..c795f0a3 100644 --- a/images/squiggle2.png +++ b/images/squiggle2.png | |||
Binary files differ | |||
diff --git a/images/squiggle_closing.png b/images/squiggle_closing.png index 901fff5f..3f9d02b1 100644 --- a/images/squiggle_closing.png +++ b/images/squiggle_closing.png | |||
Binary files differ | |||
diff --git a/images/tag_blue.png b/images/tag_blue.png index 9757fc6e..7ec902fc 100644 --- a/images/tag_blue.png +++ b/images/tag_blue.png | |||
Binary files differ | |||
diff --git a/inc/qr.min.js b/inc/qr.min.js index 663ce930..19d704e1 100644 --- a/inc/qr.min.js +++ b/inc/qr.min.js | |||
@@ -1,9 +1,5 @@ | |||
1 | // [qr.js](http://neocotic.com/qr.js) 1.0.3 | 1 | /*! qr-js v1.1.3 | (c) 2014 Alasdair Mercer | GPL v3 License |
2 | // (c) 2011 Alasdair Mercer | 2 | jsqrencode | (c) 2010 tz@execpc.com | GPL v3 License |
3 | // Freely distributable under the MIT license. | 3 | */ |
4 | // Based on jsqrencode | 4 | !function(a){"use strict";function b(){return T?new r:a.document.createElement("canvas")}function c(){return T?new x:a.document.createElement("img")}function d(b,c,d){var e=c.mime||B;a.location.href=b.toDataURL(e).replace(e,C),"function"==typeof d&&d()}function e(a){return"string"==typeof a&&(a={value:a}),a||{}}function f(a){function b(b){a[b]=function(){throw new Error(b+" requires HTML5 canvas element support")}}var c,d=["canvas","image","save","saveSync","toDataURL"];for(c=0;c<d.length;c++)b(d[c])}function g(a,b,c){function d(){w.write(e,f,0,f.length,0,function(a){w.close(e),c(a)})}if("string"!=typeof b.path)return c(new TypeError("Invalid path type: "+typeof b.path));var e,f;a.toBuffer(function(a,b){return a?c(a):(f=b,void(e&&d()))}),w.open(b.path,"w",N,function(a,b){return a?c(a):(e=b,void(f&&d()))})}function h(a,b){if("string"!=typeof b.path)throw new TypeError("Invalid path type: "+typeof b.path);var c=a.toBuffer(),d=w.openSync(b.path,"w",N);try{w.writeSync(d,c,0,c.length,0)}finally{w.closeSync(d)}}function i(a,b){var c;a>b&&(c=a,a=b,b=c),c=b,c*=b,c+=b,c>>=1,c+=a,S[c]=1}function j(a,b){var c;for(R[a+z*b]=1,c=-2;2>c;c++)R[a+c+z*(b-2)]=1,R[a-2+z*(b+c+1)]=1,R[a+2+z*(b+c)]=1,R[a+c+1+z*(b+2)]=1;for(c=0;2>c;c++)i(a-1,b+c),i(a+1,b-c),i(a-c,b-1),i(a+c,b+1)}function k(a){for(;a>=255;)a-=255,a=(a>>8)+(255&a);return a}function l(a,b,c,d){var e,f,g;for(f=0;d>f;f++)W[c+f]=0;for(f=0;b>f;f++){if(e=H[W[a+f]^W[c]],255!==e)for(g=1;d>g;g++)W[c+g-1]=W[c+g]^G[k(e+U[d-g])];else for(g=c;c+d>g;g++)W[g]=W[g+1];W[c+d-1]=255===e?0:G[k(e+U[0])]}}function m(a,b){var c;return a>b&&(c=a,a=b,b=c),c=b,c+=b*b,c>>=1,c+=a,1===S[c]}function n(a){var b,c,d,e;switch(a){case 0:for(c=0;z>c;c++)for(b=0;z>b;b++)b+c&1||m(b,c)||(R[b+c*z]^=1);break;case 1:for(c=0;z>c;c++)for(b=0;z>b;b++)1&c||m(b,c)||(R[b+c*z]^=1);break;case 2:for(c=0;z>c;c++)for(d=0,b=0;z>b;b++,d++)3===d&&(d=0),d||m(b,c)||(R[b+c*z]^=1);break;case 3:for(e=0,c=0;z>c;c++,e++)for(3===e&&(e=0),d=e,b=0;z>b;b++,d++)3===d&&(d=0),d||m(b,c)||(R[b+c*z]^=1);break;case 4:for(c=0;z>c;c++)for(d=0,e=c>>1&1,b=0;z>b;b++,d++)3===d&&(d=0,e=!e),e||m(b,c)||(R[b+c*z]^=1);break;case 5:for(e=0,c=0;z>c;c++,e++)for(3===e&&(e=0),d=0,b=0;z>b;b++,d++)3===d&&(d=0),(b&c&1)+!(!d|!e)||m(b,c)||(R[b+c*z]^=1);break;case 6:for(e=0,c=0;z>c;c++,e++)for(3===e&&(e=0),d=0,b=0;z>b;b++,d++)3===d&&(d=0),(b&c&1)+(d&&d===e)&1||m(b,c)||(R[b+c*z]^=1);break;case 7:for(e=0,c=0;z>c;c++,e++)for(3===e&&(e=0),d=0,b=0;z>b;b++,d++)3===d&&(d=0),(d&&d===e)+(b+c&1)&1||m(b,c)||(R[b+c*z]^=1)}}function o(a){var b,c=0;for(b=0;a>=b;b++)O[b]>=5&&(c+=I+O[b]-5);for(b=3;a-1>b;b+=2)O[b-2]===O[b+2]&&O[b+2]===O[b-1]&&O[b-1]===O[b+1]&&3*O[b-1]===O[b]&&(0===O[b-3]||b+3>a||3*O[b-3]>=4*O[b]||3*O[b+3]>=4*O[b])&&(c+=K);return c}function p(){var a,b,c,d,e,f,g,h,i;for(c=e=f=0,i=0;z-1>i;i++)for(h=0;z-1>h;h++)(R[h+z*i]&&R[h+1+z*i]&&R[h+z*(i+1)]&&R[h+1+z*(i+1)]||!(R[h+z*i]||R[h+1+z*i]||R[h+z*(i+1)]||R[h+1+z*(i+1)]))&&(c+=J);for(i=0;z>i;i++){for(O[0]=0,g=a=h=0;z>h;h++)(b=R[h+z*i])===a?O[g]++:O[++g]=1,a=b,e+=a?1:-1;c+=o(g)}for(0>e&&(e=-e),d=e,d+=d<<2,d<<=1;d>z*z;)d-=z*z,f++;for(c+=f*L,h=0;z>h;h++){for(O[0]=0,g=a=i=0;z>i;i++)(b=R[h+z*i])===a?O[g]++:O[++g]=1,a=b;c+=o(g)}return c}function q(a){var b,c,d,e,f,g,h,o;f=a.length,y=0;do if(y++,d=4*(Q-1)+16*(y-1),u=D[d++],v=D[d++],s=D[d++],t=D[d],d=s*(u+v)+v-3+(9>=y),d>=f)break;while(40>y);for(z=17+4*y,g=s+(s+t)*(u+v)+v,f=0;g>f;f++)P[f]=0;for(W=a.slice(0),f=0;z*z>f;f++)R[f]=0;for(f=0;(z*(z+1)+1)/2>f;f++)S[f]=0;for(f=0;3>f;f++){for(d=o=0,1===f&&(d=z-7),2===f&&(o=z-7),R[o+3+z*(d+3)]=1,h=0;6>h;h++)R[o+h+z*d]=1,R[o+z*(d+h+1)]=1,R[o+6+z*(d+h)]=1,R[o+h+1+z*(d+6)]=1;for(h=1;5>h;h++)i(o+h,d+1),i(o+1,d+h+1),i(o+5,d+h),i(o+h+1,d+5);for(h=2;4>h;h++)R[o+h+z*(d+2)]=1,R[o+2+z*(d+h+1)]=1,R[o+4+z*(d+h)]=1,R[o+h+1+z*(d+4)]=1}if(y>1)for(f=A[y],o=z-7;;){for(h=z-7;h>f-3&&(j(h,o),!(f>h));)h-=f;if(f+9>=o)break;o-=f,j(6,o),j(o,6)}for(R[8+z*(z-8)]=1,o=0;7>o;o++)i(7,o),i(z-8,o),i(7,o+z-7);for(h=0;8>h;h++)i(h,7),i(h+z-8,7),i(h,z-8);for(h=0;9>h;h++)i(h,8);for(h=0;8>h;h++)i(h+z-8,8),i(8,h);for(o=0;7>o;o++)i(8,o+z-7);for(h=0;z-14>h;h++)1&h?(i(8+h,6),i(6,8+h)):(R[8+h+6*z]=1,R[6+z*(8+h)]=1);if(y>6)for(f=M[y-7],d=17,h=0;6>h;h++)for(o=0;3>o;o++,d--)1&(d>11?y>>d-12:f>>d)?(R[5-h+z*(2-o+z-11)]=1,R[2-o+z-11+z*(5-h)]=1):(i(5-h,2-o+z-11),i(2-o+z-11,5-h));for(o=0;z>o;o++)for(h=0;o>=h;h++)R[h+z*o]&&i(h,o);for(g=W.length,b=0;g>b;b++)P[b]=W.charCodeAt(b);if(W=P.slice(0),h=s*(u+v)+v,g>=h-2&&(g=h-2,y>9&&g--),b=g,y>9){for(W[b+2]=0,W[b+3]=0;b--;)f=W[b],W[b+3]|=255&f<<4,W[b+2]=f>>4;W[2]|=255&g<<4,W[1]=g>>4,W[0]=64|g>>12}else{for(W[b+1]=0,W[b+2]=0;b--;)f=W[b],W[b+2]|=255&f<<4,W[b+1]=f>>4;W[1]|=255&g<<4,W[0]=64|g>>4}for(b=g+3-(10>y);h>b;)W[b++]=236,W[b++]=17;for(U[0]=1,b=0;t>b;b++){for(U[b+1]=1,c=b;c>0;c--)U[c]=U[c]?U[c-1]^G[k(H[U[c]]+b)]:U[c-1];U[0]=G[k(H[U[0]]+b)]}for(b=0;t>=b;b++)U[b]=H[U[b]];for(d=h,o=0,b=0;u>b;b++)l(o,s,d,t),o+=s,d+=t;for(b=0;v>b;b++)l(o,s+1,d,t),o+=s+1,d+=t;for(o=0,b=0;s>b;b++){for(c=0;u>c;c++)P[o++]=W[b+c*s];for(c=0;v>c;c++)P[o++]=W[u*s+b+c*(s+1)]}for(c=0;v>c;c++)P[o++]=W[u*s+b+c*(s+1)];for(b=0;t>b;b++)for(c=0;u+v>c;c++)P[o++]=W[h+b+c*t];for(W=P,h=o=z-1,d=g=1,e=(s+t)*(u+v)+v,b=0;e>b;b++)for(f=W[b],c=0;8>c;c++,f<<=1){128&f&&(R[h+z*o]=1);do g?h--:(h++,d?0!==o?o--:(h-=2,d=!d,6===h&&(h--,o=9)):o!==z-1?o++:(h-=2,d=!d,6===h&&(h--,o-=8))),g=!g;while(m(h,o))}for(W=R.slice(0),f=0,o=3e4,d=0;8>d&&(n(d),h=p(),o>h&&(o=h,f=d),7!==f);d++)R=W.slice(0);for(f!==d&&n(f),o=F[f+(Q-1<<3)],d=0;8>d;d++,o>>=1)1&o&&(R[z-1-d+8*z]=1,6>d?R[8+z*d]=1:R[8+z*(d+1)]=1);for(d=0;7>d;d++,o>>=1)1&o&&(R[8+z*(z-7+d)]=1,d?R[6-d+8*z]=1:R[7+8*z]=1);return R}var r,s,t,u,v,w,x,y,z,A=[0,11,15,19,23,27,31,16,18,20,22,24,26,28,20,22,24,24,26,28,28,22,24,24,26,26,28,28,24,24,26,26,26,28,28,24,26,26,26,28,28],B="image/png",C="image/octet-stream",D=[1,0,19,7,1,0,16,10,1,0,13,13,1,0,9,17,1,0,34,10,1,0,28,16,1,0,22,22,1,0,16,28,1,0,55,15,1,0,44,26,2,0,17,18,2,0,13,22,1,0,80,20,2,0,32,18,2,0,24,26,4,0,9,16,1,0,108,26,2,0,43,24,2,2,15,18,2,2,11,22,2,0,68,18,4,0,27,16,4,0,19,24,4,0,15,28,2,0,78,20,4,0,31,18,2,4,14,18,4,1,13,26,2,0,97,24,2,2,38,22,4,2,18,22,4,2,14,26,2,0,116,30,3,2,36,22,4,4,16,20,4,4,12,24,2,2,68,18,4,1,43,26,6,2,19,24,6,2,15,28,4,0,81,20,1,4,50,30,4,4,22,28,3,8,12,24,2,2,92,24,6,2,36,22,4,6,20,26,7,4,14,28,4,0,107,26,8,1,37,22,8,4,20,24,12,4,11,22,3,1,115,30,4,5,40,24,11,5,16,20,11,5,12,24,5,1,87,22,5,5,41,24,5,7,24,30,11,7,12,24,5,1,98,24,7,3,45,28,15,2,19,24,3,13,15,30,1,5,107,28,10,1,46,28,1,15,22,28,2,17,14,28,5,1,120,30,9,4,43,26,17,1,22,28,2,19,14,28,3,4,113,28,3,11,44,26,17,4,21,26,9,16,13,26,3,5,107,28,3,13,41,26,15,5,24,30,15,10,15,28,4,4,116,28,17,0,42,26,17,6,22,28,19,6,16,30,2,7,111,28,17,0,46,28,7,16,24,30,34,0,13,24,4,5,121,30,4,14,47,28,11,14,24,30,16,14,15,30,6,4,117,30,6,14,45,28,11,16,24,30,30,2,16,30,8,4,106,26,8,13,47,28,7,22,24,30,22,13,15,30,10,2,114,28,19,4,46,28,28,6,22,28,33,4,16,30,8,4,122,30,22,3,45,28,8,26,23,30,12,28,15,30,3,10,117,30,3,23,45,28,4,31,24,30,11,31,15,30,7,7,116,30,21,7,45,28,1,37,23,30,19,26,15,30,5,10,115,30,19,10,47,28,15,25,24,30,23,25,15,30,13,3,115,30,2,29,46,28,42,1,24,30,23,28,15,30,17,0,115,30,10,23,46,28,10,35,24,30,19,35,15,30,17,1,115,30,14,21,46,28,29,19,24,30,11,46,15,30,13,6,115,30,14,23,46,28,44,7,24,30,59,1,16,30,12,7,121,30,12,26,47,28,39,14,24,30,22,41,15,30,6,14,121,30,6,34,47,28,46,10,24,30,2,64,15,30,17,4,122,30,29,14,46,28,49,10,24,30,24,46,15,30,4,18,122,30,13,32,46,28,48,14,24,30,42,32,15,30,20,4,117,30,40,7,47,28,43,22,24,30,10,67,15,30,19,6,118,30,18,31,47,28,34,34,24,30,20,61,15,30],E={L:1,M:2,Q:3,H:4},F=[30660,29427,32170,30877,26159,25368,27713,26998,21522,20773,24188,23371,17913,16590,20375,19104,13663,12392,16177,14854,9396,8579,11994,11245,5769,5054,7399,6608,1890,597,3340,2107],G=[1,2,4,8,16,32,64,128,29,58,116,232,205,135,19,38,76,152,45,90,180,117,234,201,143,3,6,12,24,48,96,192,157,39,78,156,37,74,148,53,106,212,181,119,238,193,159,35,70,140,5,10,20,40,80,160,93,186,105,210,185,111,222,161,95,190,97,194,153,47,94,188,101,202,137,15,30,60,120,240,253,231,211,187,107,214,177,127,254,225,223,163,91,182,113,226,217,175,67,134,17,34,68,136,13,26,52,104,208,189,103,206,129,31,62,124,248,237,199,147,59,118,236,197,151,51,102,204,133,23,46,92,184,109,218,169,79,158,33,66,132,21,42,84,168,77,154,41,82,164,85,170,73,146,57,114,228,213,183,115,230,209,191,99,198,145,63,126,252,229,215,179,123,246,241,255,227,219,171,75,150,49,98,196,149,55,110,220,165,87,174,65,130,25,50,100,200,141,7,14,28,56,112,224,221,167,83,166,81,162,89,178,121,242,249,239,195,155,43,86,172,69,138,9,18,36,72,144,61,122,244,245,247,243,251,235,203,139,11,22,44,88,176,125,250,233,207,131,27,54,108,216,173,71,142,0],H=[255,0,1,25,2,50,26,198,3,223,51,238,27,104,199,75,4,100,224,14,52,141,239,129,28,193,105,248,200,8,76,113,5,138,101,47,225,36,15,33,53,147,142,218,240,18,130,69,29,181,194,125,106,39,249,185,201,154,9,120,77,228,114,166,6,191,139,98,102,221,48,253,226,152,37,179,16,145,34,136,54,208,148,206,143,150,219,189,241,210,19,92,131,56,70,64,30,66,182,163,195,72,126,110,107,58,40,84,250,133,186,61,202,94,155,159,10,21,121,43,78,212,229,172,115,243,167,87,7,112,192,247,140,128,99,13,103,74,222,237,49,197,254,24,227,165,153,119,38,184,180,124,17,68,146,217,35,32,137,46,55,63,209,91,149,188,207,205,144,135,151,178,220,252,190,97,242,86,211,171,20,42,93,158,132,60,57,83,71,109,65,162,31,45,67,216,183,123,164,118,196,23,73,236,127,12,111,246,108,161,59,82,41,157,85,170,251,96,134,177,187,204,62,90,203,89,95,176,156,169,160,81,11,245,22,235,122,117,44,215,79,174,213,233,230,231,173,232,116,214,244,234,168,80,88,175],I=3,J=3,K=40,L=10,M=[3220,1468,2713,1235,3062,1890,2119,1549,2344,2936,1117,2583,1330,2470,1667,2249,2028,3780,481,4011,142,3098,831,3445,592,2517,1776,2234,1951,2827,1070,2660,1345,3177],N=parseInt("0666",8),O=[],P=[],Q=1,R=[],S=[],T=!1,U=[],V=a.qr,W=[],X={VERSION:"1.1.3",canvas:function(a){a=e(a);var c=a.size>=1&&a.size<=10?a.size:4;c*=25;var d=a.canvas||b(),f=d.getContext("2d");f.canvas.width=c,f.canvas.height=c,f.fillStyle=a.background||"#fff",f.fillRect(0,0,c,c),Q=E[a.level&&a.level.toUpperCase()||"L"];var g=q(a.value||"");f.lineWidth=1;var h=c;h/=z,h=Math.floor(h),f.clearRect(0,0,c,c),f.fillStyle=a.background||"#fff",f.fillRect(0,0,h*(z+8),h*(z+8)),f.fillStyle=a.foreground||"#000";var i,j;for(i=0;z>i;i++)for(j=0;z>j;j++)g[j*z+i]&&f.fillRect(h*i,h*j,h,h);return d},image:function(a){a=e(a);var b=this.canvas(a),d=a.image||c();return d.src=b.toDataURL(a.mime||B),d.height=b.height,d.width=b.width,d},save:function(a,b,c){function f(a){h||(h=!0,c(a))}switch(a=e(a),typeof b){case"function":c=b,b=null;break;case"string":a.path=b}if("function"!=typeof c)throw new TypeError("Invalid callback type: "+typeof c);var h=!1,i=this.canvas(a);T?g(i,a,f):d(i,a,f)},saveSync:function(a,b){a=e(a),"string"==typeof b&&(a.path=b);var c=this.canvas(a);T?h(c,a):d(c,a)},toDataURL:function(a){return a=e(a),this.canvas(a).toDataURL(a.mime||B)},noConflict:function(){return a.qr=V,this}};"undefined"!=typeof exports?(T=!0,"undefined"!=typeof module&&module.exports&&(exports=module.exports=X),exports.qr=X,r=require("canvas"),x=r.Image,w=require("fs")):"function"==typeof define&&define.amd?define(function(){return X}):(a.HTMLCanvasElement||f(X),a.qr=X)}(this); |
5 | // (c) 2010 tz@execpc.com | 5 | //# sourceMappingURL=qr.min.map \ No newline at end of file |
6 | // Licensed under the GPL Version 3 license. | ||
7 | // For all details and documentation: | ||
8 | // http://neocotic.com/qr.js | ||
9 | (function(a){function Q(a){var c,h,i,j,k,m,n,u;k=a.length;C=0;do{C++;i=(r-1)*4+(C-1)*16;z=d[i++];A=d[i++];o=d[i++];p=d[i];i=o*(z+A)+A-3+(C<=9);if(k<=i)break}while(C<40);D=17+4*C;m=o+(o+p)*(z+A)+A;for(k=0;k<m;k++)q[k]=0;B=a.slice(0);for(k=0;k<D*D;k++)s[k]=0;for(k=0;k<(D*(D+1)+1)/2;k++)t[k]=0;for(k=0;k<3;k++){i=0;u=0;if(k===1)i=D-7;if(k===2)u=D-7;s[u+3+D*(i+3)]=1;for(n=0;n<6;n++){s[u+n+D*i]=1;s[u+D*(i+n+1)]=1;s[u+6+D*(i+n)]=1;s[u+n+1+D*(i+6)]=1}for(n=1;n<5;n++){I(u+n,i+1);I(u+1,i+n+1);I(u+5,i+n);I(u+n+1,i+5)}for(n=2;n<4;n++){s[u+n+D*(i+2)]=1;s[u+2+D*(i+n+1)]=1;s[u+4+D*(i+n)]=1;s[u+n+1+D*(i+4)]=1}}if(C>1){k=b[C];u=D-7;for(;;){n=D-7;while(n>k-3){J(n,u);if(n<k)break;n-=k}if(u<=k+9)break;u-=k;J(6,u);J(u,6)}}s[8+D*(D-8)]=1;for(u=0;u<7;u++){I(7,u);I(D-8,u);I(7,u+D-7)}for(n=0;n<8;n++){I(n,7);I(n+D-8,7);I(n,D-8)}for(n=0;n<9;n++)I(n,8);for(n=0;n<8;n++){I(n+D-8,8);I(8,n)}for(u=0;u<7;u++)I(8,u+D-7);for(n=0;n<D-14;n++){if(n&1){I(8+n,6);I(6,8+n)}else{s[8+n+D*6]=1;s[6+D*(8+n)]=1}}if(C>6){k=l[C-7];i=17;for(n=0;n<6;n++){for(u=0;u<3;u++,i--){if(1&(i>11?C>>i-12:k>>i)){s[5-n+D*(2-u+D-11)]=1;s[2-u+D-11+D*(5-n)]=1}else{I(5-n,2-u+D-11);I(2-u+D-11,5-n)}}}}for(u=0;u<D;u++){for(n=0;n<=u;n++){if(s[n+D*u])I(n,u)}}m=B.length;for(c=0;c<m;c++)q[c]=B.charCodeAt(c);B=q.slice(0);n=o*(z+A)+A;if(m>=n-2){m=n-2;if(C>9)m--}c=m;if(C>9){B[c+2]=0;B[c+3]=0;while(c--){k=B[c];B[c+3]|=255&k<<4;B[c+2]=k>>4}B[2]|=255&m<<4;B[1]=m>>4;B[0]=64|m>>12}else{B[c+1]=0;B[c+2]=0;while(c--){k=B[c];B[c+2]|=255&k<<4;B[c+1]=k>>4}B[1]|=255&m<<4;B[0]=64|m>>4}c=m+3-(C<10);while(c<n){B[c++]=236;B[c++]=17}x[0]=1;for(c=0;c<p;c++){x[c+1]=1;for(h=c;h>0;h--){x[h]=x[h]?x[h-1]^f[K(g[x[h]]+c)]:x[h-1]}x[0]=f[K(g[x[0]]+c)]}for(c=0;c<=p;c++)x[c]=g[x[c]];i=n;u=0;for(c=0;c<z;c++){L(u,o,i,p);u+=o;i+=p}for(c=0;c<A;c++){L(u,o+1,i,p);u+=o+1;i+=p}u=0;for(c=0;c<o;c++){for(h=0;h<z;h++){q[u++]=B[c+h*o]}for(h=0;h<A;h++){q[u++]=B[z*o+c+h*(o+1)]}}for(h=0;h<A;h++){q[u++]=B[z*o+c+h*(o+1)]}for(c=0;c<p;c++){for(h=0;h<z+A;h++){q[u++]=B[n+c+h*p]}}B=q;n=u=D-1;i=m=1;j=(o+p)*(z+A)+A;for(c=0;c<j;c++){k=B[c];for(h=0;h<8;h++,k<<=1){if(128&k)s[n+D*u]=1;do{if(m){n--}else{n++;if(i){if(u!==0){u--}else{n-=2;i=!i;if(n===6){n--;u=9}}}else{if(u!==D-1){u++}else{n-=2;i=!i;if(n===6){n--;u-=8}}}}m=!m}while(M(n,u))}}B=s.slice(0);k=0;u=3e4;for(i=0;i<8;i++){N(i);n=P();if(n<u){u=n;k=i}if(k===7)break;s=B.slice(0)}if(k!==i)N(k);u=e[k+(r-1<<3)];for(i=0;i<8;i++,u>>=1){if(u&1){s[D-1-i+D*8]=1;if(i<6){s[8+D*i]=1}else{s[8+D*(i+1)]=1}}}for(i=0;i<7;i++,u>>=1){if(u&1){s[8+D*(D-7+i)]=1;if(i){s[6-i+D*8]=1}else{s[7+D*8]=1}}}return s}function P(){var a,b,c,d,e,f,g=0,h=0,j=0;for(f=0;f<D-1;f++){for(e=0;e<D-1;e++){if(s[e+D*f]&&s[e+1+D*f]&&s[e+D*(f+1)]&&s[e+1+D*(f+1)]||!(s[e+D*f]||s[e+1+D*f]||s[e+D*(f+1)]||s[e+1+D*(f+1)])){g+=i}}}for(f=0;f<D;f++){m[0]=0;for(d=a=e=0;e<D;e++){if((b=s[e+D*f])===a){m[d]++}else{m[++d]=1}a=b;h+=a?1:-1}g+=O(d)}if(h<0)h=-h;c=h;c+=c<<2;c<<=1;while(c>D*D){c-=D*D;j++}g+=j*k;for(e=0;e<D;e++){m[0]=0;for(d=a=f=0;f<D;f++){if((b=s[e+D*f])===a){m[d]++}else{m[++d]=1}a=b}g+=O(d)}return g}function O(a){var b=0,c;for(c=0;c<=a;c++){if(m[c]>=5)b+=h+m[c]-5}for(c=3;c<a-1;c+=2){if(m[c-2]===m[c+2]&&m[c+2]===m[c-1]&&m[c-1]===m[c+1]&&m[c-1]*3===m[c]&&(m[c-3]===0||c+3>a||m[c-3]*3>=m[c]*4||m[c+3]*3>=m[c]*4)){b+=j}}return b}function N(a){var b,c,d,e;switch(a){case 0:for(c=0;c<D;c++){for(b=0;b<D;b++){if(!(b+c&1)&&!M(b,c)){s[b+c*D]^=1}}}break;case 1:for(c=0;c<D;c++){for(b=0;b<D;b++){if(!(c&1)&&!M(b,c))s[b+c*D]^=1}}break;case 2:for(c=0;c<D;c++){for(d=0,b=0;b<D;b++,d++){if(d===3)d=0;if(!d&&!M(b,c))s[b+c*D]^=1}}break;case 3:for(e=0,c=0;c<D;c++,e++){if(e===3)e=0;for(d=e,b=0;b<D;b++,d++){if(d===3)d=0;if(!d&&!M(b,c))s[b+c*D]^=1}}break;case 4:for(c=0;c<D;c++){for(d=0,e=c>>1&1,b=0;b<D;b++,d++){if(d===3){d=0;e=!e}if(!e&&!M(b,c))s[b+c*D]^=1}}break;case 5:for(e=0,c=0;c<D;c++,e++){if(e===3)e=0;for(d=0,b=0;b<D;b++,d++){if(d===3)d=0;if(!((b&c&1)+!(!d|!e))&&!M(b,c)){s[b+c*D]^=1}}}break;case 6:for(e=0,c=0;c<D;c++,e++){if(e===3)e=0;for(d=0,b=0;b<D;b++,d++){if(d===3)d=0;if(!((b&c&1)+(d&&d===e)&1)&&!M(b,c)){s[b+c*D]^=1}}}break;case 7:for(e=0,c=0;c<D;c++,e++){if(e===3)e=0;for(d=0,b=0;b<D;b++,d++){if(d===3)d=0;if(!((d&&d===e)+(b+c&1)&1)&&!M(b,c)){s[b+c*D]^=1}}}break}}function M(a,b){var c;if(a>b){c=a;a=b;b=c}c=b;c+=b*b;c>>=1;c+=a;return t[c]}function L(a,b,c,d){var e,h,i;for(h=0;h<d;h++)B[c+h]=0;for(h=0;h<b;h++){e=g[B[a+h]^B[c]];if(e!==255){for(i=1;i<d;i++){B[c+i-1]=B[c+i]^f[K(e+x[d-i])]}}else{for(i=c;i<c+d;i++){B[i]=B[i+1]}}B[c+d-1]=e===255?0:f[K(e+x[0])]}}function K(a){while(a>=255){a-=255;a=(a>>8)+(a&255)}return a}function J(a,b){var c;s[a+D*b]=1;for(c=-2;c<2;c++){s[a+c+D*(b-2)]=1;s[a-2+D*(b+c+1)]=1;s[a+2+D*(b+c)]=1;s[a+c+1+D*(b+2)]=1}for(c=0;c<2;c++){I(a-1,b+c);I(a+1,b-c);I(a-c,b-1);I(a+c,b+1)}}function I(a,b){var c;if(a>b){c=a;a=b;b=c}c=b;c*=b;c+=b;c>>=1;c+=a;t[c]=1}function H(a,b,c){try{var d=a.apply(c||this);if(typeof b==="function")return b(null,d);return d}catch(e){if(typeof b==="function")return b(e);throw e}}function G(){for(var a=arguments.length;a>=0;--a){if(typeof arguments[a]==="function")return arguments[a]}}function F(){return w?new v:a.document.createElement("img")}function E(){return w?new n:a.document.createElement("canvas")}var b=[0,11,15,19,23,27,31,16,18,20,22,24,26,28,20,22,24,24,26,28,28,22,24,24,26,26,28,28,24,24,26,26,26,28,28,24,26,26,26,28,28],c="image/octet-stream",d=[1,0,19,7,1,0,16,10,1,0,13,13,1,0,9,17,1,0,34,10,1,0,28,16,1,0,22,22,1,0,16,28,1,0,55,15,1,0,44,26,2,0,17,18,2,0,13,22,1,0,80,20,2,0,32,18,2,0,24,26,4,0,9,16,1,0,108,26,2,0,43,24,2,2,15,18,2,2,11,22,2,0,68,18,4,0,27,16,4,0,19,24,4,0,15,28,2,0,78,20,4,0,31,18,2,4,14,18,4,1,13,26,2,0,97,24,2,2,38,22,4,2,18,22,4,2,14,26,2,0,116,30,3,2,36,22,4,4,16,20,4,4,12,24,2,2,68,18,4,1,43,26,6,2,19,24,6,2,15,28,4,0,81,20,1,4,50,30,4,4,22,28,3,8,12,24,2,2,92,24,6,2,36,22,4,6,20,26,7,4,14,28,4,0,107,26,8,1,37,22,8,4,20,24,12,4,11,22,3,1,115,30,4,5,40,24,11,5,16,20,11,5,12,24,5,1,87,22,5,5,41,24,5,7,24,30,11,7,12,24,5,1,98,24,7,3,45,28,15,2,19,24,3,13,15,30,1,5,107,28,10,1,46,28,1,15,22,28,2,17,14,28,5,1,120,30,9,4,43,26,17,1,22,28,2,19,14,28,3,4,113,28,3,11,44,26,17,4,21,26,9,16,13,26,3,5,107,28,3,13,41,26,15,5,24,30,15,10,15,28,4,4,116,28,17,0,42,26,17,6,22,28,19,6,16,30,2,7,111,28,17,0,46,28,7,16,24,30,34,0,13,24,4,5,121,30,4,14,47,28,11,14,24,30,16,14,15,30,6,4,117,30,6,14,45,28,11,16,24,30,30,2,16,30,8,4,106,26,8,13,47,28,7,22,24,30,22,13,15,30,10,2,114,28,19,4,46,28,28,6,22,28,33,4,16,30,8,4,122,30,22,3,45,28,8,26,23,30,12,28,15,30,3,10,117,30,3,23,45,28,4,31,24,30,11,31,15,30,7,7,116,30,21,7,45,28,1,37,23,30,19,26,15,30,5,10,115,30,19,10,47,28,15,25,24,30,23,25,15,30,13,3,115,30,2,29,46,28,42,1,24,30,23,28,15,30,17,0,115,30,10,23,46,28,10,35,24,30,19,35,15,30,17,1,115,30,14,21,46,28,29,19,24,30,11,46,15,30,13,6,115,30,14,23,46,28,44,7,24,30,59,1,16,30,12,7,121,30,12,26,47,28,39,14,24,30,22,41,15,30,6,14,121,30,6,34,47,28,46,10,24,30,2,64,15,30,17,4,122,30,29,14,46,28,49,10,24,30,24,46,15,30,4,18,122,30,13,32,46,28,48,14,24,30,42,32,15,30,20,4,117,30,40,7,47,28,43,22,24,30,10,67,15,30,19,6,118,30,18,31,47,28,34,34,24,30,20,61,15,30],e=[30660,29427,32170,30877,26159,25368,27713,26998,21522,20773,24188,23371,17913,16590,20375,19104,13663,12392,16177,14854,9396,8579,11994,11245,5769,5054,7399,6608,1890,597,3340,2107],f=[1,2,4,8,16,32,64,128,29,58,116,232,205,135,19,38,76,152,45,90,180,117,234,201,143,3,6,12,24,48,96,192,157,39,78,156,37,74,148,53,106,212,181,119,238,193,159,35,70,140,5,10,20,40,80,160,93,186,105,210,185,111,222,161,95,190,97,194,153,47,94,188,101,202,137,15,30,60,120,240,253,231,211,187,107,214,177,127,254,225,223,163,91,182,113,226,217,175,67,134,17,34,68,136,13,26,52,104,208,189,103,206,129,31,62,124,248,237,199,147,59,118,236,197,151,51,102,204,133,23,46,92,184,109,218,169,79,158,33,66,132,21,42,84,168,77,154,41,82,164,85,170,73,146,57,114,228,213,183,115,230,209,191,99,198,145,63,126,252,229,215,179,123,246,241,255,227,219,171,75,150,49,98,196,149,55,110,220,165,87,174,65,130,25,50,100,200,141,7,14,28,56,112,224,221,167,83,166,81,162,89,178,121,242,249,239,195,155,43,86,172,69,138,9,18,36,72,144,61,122,244,245,247,243,251,235,203,139,11,22,44,88,176,125,250,233,207,131,27,54,108,216,173,71,142,0],g=[255,0,1,25,2,50,26,198,3,223,51,238,27,104,199,75,4,100,224,14,52,141,239,129,28,193,105,248,200,8,76,113,5,138,101,47,225,36,15,33,53,147,142,218,240,18,130,69,29,181,194,125,106,39,249,185,201,154,9,120,77,228,114,166,6,191,139,98,102,221,48,253,226,152,37,179,16,145,34,136,54,208,148,206,143,150,219,189,241,210,19,92,131,56,70,64,30,66,182,163,195,72,126,110,107,58,40,84,250,133,186,61,202,94,155,159,10,21,121,43,78,212,229,172,115,243,167,87,7,112,192,247,140,128,99,13,103,74,222,237,49,197,254,24,227,165,153,119,38,184,180,124,17,68,146,217,35,32,137,46,55,63,209,91,149,188,207,205,144,135,151,178,220,252,190,97,242,86,211,171,20,42,93,158,132,60,57,83,71,109,65,162,31,45,67,216,183,123,164,118,196,23,73,236,127,12,111,246,108,161,59,82,41,157,85,170,251,96,134,177,187,204,62,90,203,89,95,176,156,169,160,81,11,245,22,235,122,117,44,215,79,174,213,233,230,231,173,232,116,214,244,234,168,80,88,175],h=3,i=3,j=40,k=10,l=[3220,1468,2713,1235,3062,1890,2119,1549,2344,2936,1117,2583,1330,2470,1667,2249,2028,3780,481,4011,142,3098,831,3445,592,2517,1776,2234,1951,2827,1070,2660,1345,3177];var m=[],n,o,p,q=[],r=1,s=[],t=[],u,v,w=false,x=[],y=a.qr,z,A,B=[],C,D;var R={VERSION:"1.0.3",canvas:function(a,b){b=G(a,b);return H(function c(){switch(typeof a){case"object":break;case"string":a={value:a};break;default:a={};break}var b,c,d,e,f,g,h=4,i=25;if(a.size>=1&&a.size<=10)h=a.size;h*=i;b=a.canvas||E();c=b.getContext("2d");c.canvas.width=h;c.canvas.height=h;c.fillStyle=a.background||"#fff";c.fillRect(0,0,h,h);if(a.level){switch(a.level.toUpperCase()){case"L":r=1;break;case"M":r=2;break;case"Q":r=3;break;case"H":r=4;break}}g=Q(a.value||"");c.lineWidth=1;f=h;f/=D;f=Math.round(f-.5);c.clearRect(0,0,h,h);c.fillStyle=a.background||"#fff";c.fillRect(0,0,f*(D+8),f*(D+8));c.fillStyle=a.foreground||"#000";for(d=0;d<D;d++){for(e=0;e<D;e++){if(g[e*D+d])c.fillRect(f*d,f*e,f,f)}}return b},b,this)},image:function(a,b){b=G(a,b);return H(function c(){switch(typeof a){case"object":break;case"string":a={value:a};break;default:a={};break}var b=this.canvas(a),c=a.image||F();c.src=b.toDataURL();c.height=b.height;c.width=b.width;return c},b,this)},save:function(b,d,e){e=G(b,d,e);return H(function f(){switch(typeof b){case"object":if(typeof d==="string"&&!b.path)b.path=d;break;case"string":b={value:b};if(typeof d==="string")b.path=d;break;default:b={};break}var e=this.canvas(b);if(w){if(typeof b.path!=="string"){throw new TypeError("Invalid path type: "+typeof b.path)}var f,g,h=function i(){u.write(f,g,0,g.length,0,function a(a){u.close(f);if(a)throw a})};e.toBuffer(function j(a,b){if(a)throw a;g=b;if(f)h()});u.open(b.path,"w",438,function k(a,b){if(a)throw a;f=b;if(g)h()})}else{a.location.href=e.toDataURL().replace("image/png",c)}},e,this)},toDataURL:function(a,b){b=G(a,b);return H(function c(){return this.canvas(a).toDataURL()},b,this)},noConflict:function(b){return H(function c(){a.qr=y;return this},b,this)}};if(typeof exports!=="undefined"){w=true;if(typeof module!=="undefined"&&module.exports){exports=module.exports=R}exports.qr=R;n=require("canvas");v=n.Image;u=require("fs")}else if(typeof define==="function"&&define.amd){define("qr",function S(){return R})}else{if(!a.HTMLCanvasElement){R.canvas=function(){};R.image=function(){};R.save=function(){};R.toDataURL=function(){}}a.qr=R}})(this) | ||
diff --git a/inc/shaarli.css b/inc/shaarli.css index 52a48208..28394ed5 100644 --- a/inc/shaarli.css +++ b/inc/shaarli.css | |||
@@ -1,4 +1,4 @@ | |||
1 | /* CSS Stylsheet for Shaarli - http://sebsauvage.net/wiki/doku.php?id=php:shaarli */ | 1 | /* Cascading Stylesheet for Shaarli - http://sebsauvage.net/wiki/doku.php?id=php:shaarli */ |
2 | 2 | ||
3 | /* CSS Reset from Yahoo to cope with browsers CSS inconsistencies. */ | 3 | /* CSS Reset from Yahoo to cope with browsers CSS inconsistencies. */ |
4 | /* | 4 | /* |
@@ -7,7 +7,7 @@ version: 2.8.2r1 | |||
7 | */ | 7 | */ |
8 | html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;} | 8 | html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;} |
9 | 9 | ||
10 | body { font-family: "Trebuchet MS",Verdana,Arial,Helvetica,sans-serif; font-size:10pt; background-color: #ffffff; } | 10 | body { font-family: "Trebuchet MS",Verdana,Arial,Helvetica,sans-serif; font-size:10pt; background-color: #ffffff; word-wrap: break-word; } |
11 | input, textarea { | 11 | input, textarea { |
12 | background-color: #dedede; | 12 | background-color: #dedede; |
13 | background: -webkit-gradient(linear, 0 0, 0 bottom, from(#dedede), to(#ffffff)); | 13 | background: -webkit-gradient(linear, 0 0, 0 bottom, from(#dedede), to(#ffffff)); |
@@ -232,14 +232,15 @@ cursor:pointer; | |||
232 | .linktitle { font-size:14pt; font-weight:bold; } | 232 | .linktitle { font-size:14pt; font-weight:bold; } |
233 | .linktitle a { text-decoration: none; color:#80AD48; } | 233 | .linktitle a { text-decoration: none; color:#80AD48; } |
234 | .linktitle a:hover { color:#F57900; } | 234 | .linktitle a:hover { color:#F57900; } |
235 | .linkdate { font-size:8pt; color:#888; } | 235 | .linkdate, .linkarchive { font-size:8pt; color:#888; } |
236 | .linkdate a { background-image:url('../images/calendar.png');padding:2px 0 3px 20px;background-repeat:no-repeat;text-decoration: none; color:#E28E3F; } | 236 | .linkdate a, .linkarchive a { color:#E28E3F; } |
237 | .linkdate a:hover { color: #F57900 } | 237 | .linkdate a {background-image:url('../images/calendar.png');padding:2px 0 3px 20px;background-repeat:no-repeat;text-decoration: none; } |
238 | .linkdate a:hover, .linkarchive a:hover { color: #F57900 } | ||
238 | .linkurl { font-size:8pt; color:#4BAA74; } | 239 | .linkurl { font-size:8pt; color:#4BAA74; } |
239 | .linkdescription { color:#000; margin-top:0; margin-bottom:12px; font-weight:normal; max-height:400px; overflow:auto; } | 240 | .linkdescription { color:#000; margin-top:0; margin-bottom:12px; font-weight:normal; max-height:400px; overflow:auto; } |
240 | .linkdescription a { text-decoration: none; color:#3465A4; } | 241 | .linkdescription a { text-decoration: none; color:#3465A4; } |
241 | .linkdescription a:hover { color:#F57900; } | 242 | .linkdescription a:hover { color:#F57900; } |
242 | .linktaglist { padding-top:10px;} | 243 | .linktaglist { padding-top:10px; line-height:200%;} |
243 | .linktag { | 244 | .linktag { |
244 | 245 | ||
245 | font-size:9pt; | 246 | font-size:9pt; |
@@ -408,13 +409,13 @@ div.dailyEntryDescription | |||
408 | overflow:auto; | 409 | overflow:auto; |
409 | } | 410 | } |
410 | 411 | ||
411 | /* Common css screwdriver */ | 412 | /* Common CSS screwdriver */ |
412 | .clear{ | 413 | .clear{ |
413 | clear:both; | 414 | clear:both; |
414 | } | 415 | } |
415 | 416 | ||
416 | /* For lazy images loading in picture wall. | 417 | /* For lazy images loading in picture wall. |
417 | using http://www.appelsiini.net/projects/lazyload | 418 | Using http://www.appelsiini.net/projects/lazyload |
418 | */ | 419 | */ |
419 | .lazyimage { display:none; } | 420 | .lazyimage { display:none; } |
420 | 421 | ||
@@ -451,7 +452,7 @@ a {color:#000!important;text-decoration:none!important;} | |||
451 | #searchform_value { width:70% !important; } | 452 | #searchform_value { width:70% !important; } |
452 | #tagfilter_value { width:70% !important; } | 453 | #tagfilter_value { width:70% !important; } |
453 | div.qrcode { position:relative; float:left; top:-10px; left:0px; } | 454 | div.qrcode { position:relative; float:left; top:-10px; left:0px; } |
454 | #paging_privatelinks { float;none; } | 455 | #paging_privatelinks { float:none; } |
455 | #paging_linksperpage { float:none; margin-bottom:10px; font-size:smaller;} | 456 | #paging_linksperpage { float:none; margin-bottom:10px; font-size:smaller;} |
456 | #paging_older,#paging_newer,#paging_linksperpage a { border: 1px solid black; padding:3px 5px 3px 5px; background-color:#666; color:#fff; border-radius: 5px 5px 5px 5px;} | 457 | #paging_older,#paging_newer,#paging_linksperpage a { border: 1px solid black; padding:3px 5px 3px 5px; background-color:#666; color:#fff; border-radius: 5px 5px 5px 5px;} |
457 | .thumbnail { float:none; height:auto; margin: 0px; text-align:center;} | 458 | .thumbnail { float:none; height:auto; margin: 0px; text-align:center;} |
@@ -1,9 +1,9 @@ | |||
1 | <?php | 1 | <?php |
2 | // Shaarli 0.0.41 beta - Shaare your links... | 2 | // Shaarli 0.0.42 beta - Shaare your links... |
3 | // The personal, minimalist, super-fast, no-database delicious clone. By sebsauvage.net | 3 | // The personal, minimalist, super-fast, no-database Delicious clone. By sebsauvage.net |
4 | // http://sebsauvage.net/wiki/doku.php?id=php:shaarli | 4 | // http://sebsauvage.net/wiki/doku.php?id=php:shaarli |
5 | // Licence: http://www.opensource.org/licenses/zlib-license.php | 5 | // Licence: http://www.opensource.org/licenses/zlib-license.php |
6 | // Requires: php 5.1.x (but autocomplete fields will only work if you have php 5.2.x) | 6 | // Requires: PHP 5.1.x (but autocomplete fields will only work if you have PHP 5.2.x) |
7 | // ----------------------------------------------------------------------------------------------- | 7 | // ----------------------------------------------------------------------------------------------- |
8 | // NEVER TRUST IN PHP.INI | 8 | // NEVER TRUST IN PHP.INI |
9 | // Some hosts do not define a default timezone in php.ini, | 9 | // Some hosts do not define a default timezone in php.ini, |
@@ -21,22 +21,28 @@ $GLOBALS['config']['BAN_AFTER'] = 4; // Ban IP after this many failures. | |||
21 | $GLOBALS['config']['BAN_DURATION'] = 1800; // Ban duration for IP address after login failures (in seconds) (1800 sec. = 30 minutes) | 21 | $GLOBALS['config']['BAN_DURATION'] = 1800; // Ban duration for IP address after login failures (in seconds) (1800 sec. = 30 minutes) |
22 | $GLOBALS['config']['OPEN_SHAARLI'] = false; // If true, anyone can add/edit/delete links without having to login | 22 | $GLOBALS['config']['OPEN_SHAARLI'] = false; // If true, anyone can add/edit/delete links without having to login |
23 | $GLOBALS['config']['HIDE_TIMESTAMPS'] = false; // If true, the moment when links were saved are not shown to users that are not logged in. | 23 | $GLOBALS['config']['HIDE_TIMESTAMPS'] = false; // If true, the moment when links were saved are not shown to users that are not logged in. |
24 | $GLOBALS['config']['SHOW_ATOM'] = false; // If true, an extra "ATOM feed" button will be displayed in the toolbar | ||
24 | $GLOBALS['config']['ENABLE_THUMBNAILS'] = true; // Enable thumbnails in links. | 25 | $GLOBALS['config']['ENABLE_THUMBNAILS'] = true; // Enable thumbnails in links. |
25 | $GLOBALS['config']['CACHEDIR'] = 'cache'; // Cache directory for thumbnails for SLOW services (like flickr) | 26 | $GLOBALS['config']['CACHEDIR'] = 'cache'; // Cache directory for thumbnails for SLOW services (like flickr) |
26 | $GLOBALS['config']['PAGECACHE'] = 'pagecache'; // Page cache directory. | 27 | $GLOBALS['config']['PAGECACHE'] = 'pagecache'; // Page cache directory. |
27 | $GLOBALS['config']['ENABLE_LOCALCACHE'] = true; // Enable Shaarli to store thumbnail in a local cache. Disable to reduce webspace usage. | 28 | $GLOBALS['config']['ENABLE_LOCALCACHE'] = true; // Enable Shaarli to store thumbnail in a local cache. Disable to reduce web space usage. |
28 | $GLOBALS['config']['PUBSUBHUB_URL'] = ''; // PubSubHubbub support. Put an empty string to disable, or put your hub url here to enable. | 29 | $GLOBALS['config']['PUBSUBHUB_URL'] = ''; // PubSubHubbub support. Put an empty string to disable, or put your hub url here to enable. |
30 | $GLOBALS['config']['RAINTPL_TMP'] = 'tmp/' ; // Raintpl cache directory (keep the trailing slash!) | ||
31 | $GLOBALS['config']['RAINTPL_TPL'] = 'tpl/' ; // Raintpl template directory (keep the trailing slash!) | ||
29 | $GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt'; // For updates check of Shaarli. | 32 | $GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt'; // For updates check of Shaarli. |
30 | $GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours | 33 | $GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours |
31 | // Note: You must have publisher.php in the same directory as Shaarli index.php | 34 | // Note: You must have publisher.php in the same directory as Shaarli index.php |
35 | $GLOBALS['config']['ARCHIVE_ORG'] = false; // For each link, add a link to an archived version on archive.org | ||
32 | // ----------------------------------------------------------------------------------------------- | 36 | // ----------------------------------------------------------------------------------------------- |
33 | // You should not touch below (or at your own risks !) | 37 | // You should not touch below (or at your own risks!) |
34 | // Optionnal config file. | 38 | // Optional config file. |
35 | if (is_file($GLOBALS['config']['DATADIR'].'/options.php')) require($GLOBALS['config']['DATADIR'].'/options.php'); | 39 | if (is_file($GLOBALS['config']['DATADIR'].'/options.php')) require($GLOBALS['config']['DATADIR'].'/options.php'); |
36 | 40 | ||
37 | define('shaarli_version','0.0.41 beta'); | 41 | define('shaarli_version','0.0.42 beta'); |
38 | define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in php code. | 42 | define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in PHP code. |
39 | define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in php code. | 43 | define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in PHP code. |
44 | // http://server.com/x/shaarli --> /shaarli/ | ||
45 | define('WEB_PATH', substr($_SERVER["REQUEST_URI"], 0, 1+strrpos($_SERVER["REQUEST_URI"], '/', 0))); | ||
40 | 46 | ||
41 | // Force cookie path (but do not change lifetime) | 47 | // Force cookie path (but do not change lifetime) |
42 | $cookie=session_get_cookie_params(); | 48 | $cookie=session_get_cookie_params(); |
@@ -46,8 +52,8 @@ session_set_cookie_params($cookie['lifetime'],$cookiedir,$_SERVER['HTTP_HOST']); | |||
46 | // Set session parameters on server side. | 52 | // Set session parameters on server side. |
47 | define('INACTIVITY_TIMEOUT',3600); // (in seconds). If the user does not access any page within this time, his/her session is considered expired. | 53 | define('INACTIVITY_TIMEOUT',3600); // (in seconds). If the user does not access any page within this time, his/her session is considered expired. |
48 | ini_set('session.use_cookies', 1); // Use cookies to store session. | 54 | ini_set('session.use_cookies', 1); // Use cookies to store session. |
49 | ini_set('session.use_only_cookies', 1); // Force cookies for session (phpsessionID forbidden in URL) | 55 | ini_set('session.use_only_cookies', 1); // Force cookies for session (phpsessionID forbidden in URL). |
50 | ini_set('session.use_trans_sid', false); // Prevent php to use sessionID in URL if cookies are disabled. | 56 | ini_set('session.use_trans_sid', false); // Prevent PHP form using sessionID in URL if cookies are disabled. |
51 | session_name('shaarli'); | 57 | session_name('shaarli'); |
52 | if (session_id() == '') session_start(); // Start session if needed (Some server auto-start sessions). | 58 | if (session_id() == '') session_start(); // Start session if needed (Some server auto-start sessions). |
53 | 59 | ||
@@ -61,9 +67,8 @@ error_reporting(E_ALL^E_WARNING); // See all error except warnings. | |||
61 | //error_reporting(-1); // See all errors (for debugging only) | 67 | //error_reporting(-1); // See all errors (for debugging only) |
62 | 68 | ||
63 | include "inc/rain.tpl.class.php"; //include Rain TPL | 69 | include "inc/rain.tpl.class.php"; //include Rain TPL |
64 | raintpl::$tpl_dir = "tpl/"; // template directory | 70 | raintpl::$tpl_dir = $GLOBALS['config']['RAINTPL_TPL']; // template directory |
65 | if (!is_dir('tmp')) { mkdir('tmp',0705); chmod('tmp',0705); } | 71 | raintpl::$cache_dir = $GLOBALS['config']['RAINTPL_TMP']; // cache directory |
66 | raintpl::$cache_dir = "tmp/"; // cache directory | ||
67 | 72 | ||
68 | ob_start(); // Output buffering for the page cache. | 73 | ob_start(); // Output buffering for the page cache. |
69 | 74 | ||
@@ -83,18 +88,8 @@ header("Cache-Control: no-store, no-cache, must-revalidate"); | |||
83 | header("Cache-Control: post-check=0, pre-check=0", false); | 88 | header("Cache-Control: post-check=0, pre-check=0", false); |
84 | header("Pragma: no-cache"); | 89 | header("Pragma: no-cache"); |
85 | 90 | ||
86 | // Directories creations (Note that your web host may require differents rights than 705.) | 91 | // Directories creations (Note that your web host may require different rights than 705.) |
87 | if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory ('.realpath(dirname(__FILE__)).').</pre>'); | 92 | if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory ('.realpath(dirname(__FILE__)).').</pre>'); |
88 | if (!is_dir($GLOBALS['config']['DATADIR'])) { mkdir($GLOBALS['config']['DATADIR'],0705); chmod($GLOBALS['config']['DATADIR'],0705); } | ||
89 | if (!is_dir('tmp')) { mkdir('tmp',0705); chmod('tmp',0705); } // For RainTPL temporary files. | ||
90 | if (!is_file($GLOBALS['config']['DATADIR'].'/.htaccess')) { file_put_contents($GLOBALS['config']['DATADIR'].'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files. | ||
91 | // Second check to see if Shaarli can write in its directory, because on some hosts is_writable() is not reliable. | ||
92 | if (!is_file($GLOBALS['config']['DATADIR'].'/.htaccess')) die('<pre>ERROR: Shaarli does not have the right to write in its data directory ('.realpath($GLOBALS['config']['DATADIR']).').</pre>'); | ||
93 | if ($GLOBALS['config']['ENABLE_LOCALCACHE']) | ||
94 | { | ||
95 | if (!is_dir($GLOBALS['config']['CACHEDIR'])) { mkdir($GLOBALS['config']['CACHEDIR'],0705); chmod($GLOBALS['config']['CACHEDIR'],0705); } | ||
96 | if (!is_file($GLOBALS['config']['CACHEDIR'].'/.htaccess')) { file_put_contents($GLOBALS['config']['CACHEDIR'].'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files. | ||
97 | } | ||
98 | 93 | ||
99 | // Handling of old config file which do not have the new parameters. | 94 | // Handling of old config file which do not have the new parameters. |
100 | if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.htmlspecialchars(indexUrl()); | 95 | if (empty($GLOBALS['title'])) $GLOBALS['title']='Shared links on '.htmlspecialchars(indexUrl()); |
@@ -103,6 +98,7 @@ if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']=''; | |||
103 | if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false; | 98 | if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false; |
104 | if (empty($GLOBALS['disablejquery'])) $GLOBALS['disablejquery']=false; | 99 | if (empty($GLOBALS['disablejquery'])) $GLOBALS['disablejquery']=false; |
105 | if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=false; | 100 | if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=false; |
101 | if (empty($GLOBALS['titleLink'])) $GLOBALS['titleLink']='?'; | ||
106 | // I really need to rewrite Shaarli with a proper configuation manager. | 102 | // I really need to rewrite Shaarli with a proper configuation manager. |
107 | 103 | ||
108 | // Run config screen if first run: | 104 | // Run config screen if first run: |
@@ -110,17 +106,19 @@ if (!is_file($GLOBALS['config']['CONFIG_FILE'])) install(); | |||
110 | 106 | ||
111 | require $GLOBALS['config']['CONFIG_FILE']; // Read login/password hash into $GLOBALS. | 107 | require $GLOBALS['config']['CONFIG_FILE']; // Read login/password hash into $GLOBALS. |
112 | 108 | ||
109 | // a token depending of deployment salt, user password, and the current ip | ||
110 | define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GLOBALS['salt'])); | ||
113 | 111 | ||
114 | autoLocale(); // Sniff browser language and set date format accordingly. | 112 | autoLocale(); // Sniff browser language and set date format accordingly. |
115 | header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling. | 113 | header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling. |
116 | 114 | ||
117 | // Check php version | 115 | // Check PHP version |
118 | function checkphpversion() | 116 | function checkphpversion() |
119 | { | 117 | { |
120 | if (version_compare(PHP_VERSION, '5.1.0') < 0) | 118 | if (version_compare(PHP_VERSION, '5.1.0') < 0) |
121 | { | 119 | { |
122 | header('Content-Type: text/plain; charset=utf-8'); | 120 | header('Content-Type: text/plain; charset=utf-8'); |
123 | echo 'Your server supports php '.PHP_VERSION.'. Shaarli requires at least php 5.1.0, and thus cannot run. Sorry.'; | 121 | echo 'Your server supports PHP '.PHP_VERSION.'. Shaarli requires at least php 5.1.0, and thus cannot run. Sorry.'; |
124 | exit; | 122 | exit; |
125 | } | 123 | } |
126 | } | 124 | } |
@@ -137,9 +135,9 @@ function checkUpdate() | |||
137 | if (!is_file($GLOBALS['config']['UPDATECHECK_FILENAME']) || (filemtime($GLOBALS['config']['UPDATECHECK_FILENAME'])<time()-($GLOBALS['config']['UPDATECHECK_INTERVAL']))) | 135 | if (!is_file($GLOBALS['config']['UPDATECHECK_FILENAME']) || (filemtime($GLOBALS['config']['UPDATECHECK_FILENAME'])<time()-($GLOBALS['config']['UPDATECHECK_INTERVAL']))) |
138 | { | 136 | { |
139 | $version=shaarli_version; | 137 | $version=shaarli_version; |
140 | list($httpstatus,$headers,$data) = getHTTP('http://sebsauvage.net/files/shaarli_version.txt',2); | 138 | list($httpstatus,$headers,$data) = getHTTP('https://raw.githubusercontent.com/shaarli/Shaarli/master/shaarli_version.txt',2); |
141 | if (strpos($httpstatus,'200 OK')!==false) $version=$data; | 139 | if (strpos($httpstatus,'200 OK')!==false) $version=$data; |
142 | // If failed, nevermind. We don't want to bother the user with that. | 140 | // If failed, never mind. We don't want to bother the user with that. |
143 | file_put_contents($GLOBALS['config']['UPDATECHECK_FILENAME'],$version); // touch file date | 141 | file_put_contents($GLOBALS['config']['UPDATECHECK_FILENAME'],$version); // touch file date |
144 | } | 142 | } |
145 | // Compare versions: | 143 | // Compare versions: |
@@ -155,11 +153,11 @@ function checkUpdate() | |||
155 | class pageCache | 153 | class pageCache |
156 | { | 154 | { |
157 | private $url; // Full URL of the page to cache (typically the value returned by pageUrl()) | 155 | private $url; // Full URL of the page to cache (typically the value returned by pageUrl()) |
158 | private $shouldBeCached; // boolean: Should this url be cached ? | 156 | private $shouldBeCached; // boolean: Should this url be cached? |
159 | private $filename; // Name of the cache file for this url | 157 | private $filename; // Name of the cache file for this url. |
160 | 158 | ||
161 | /* | 159 | /* |
162 | $url = url (typically the value returned by pageUrl()) | 160 | $url = URL (typically the value returned by pageUrl()) |
163 | $shouldBeCached = boolean. If false, the cache will be disabled. | 161 | $shouldBeCached = boolean. If false, the cache will be disabled. |
164 | */ | 162 | */ |
165 | public function __construct($url,$shouldBeCached) | 163 | public function __construct($url,$shouldBeCached) |
@@ -182,7 +180,6 @@ class pageCache | |||
182 | public function cache($page) | 180 | public function cache($page) |
183 | { | 181 | { |
184 | if (!$this->shouldBeCached) return; | 182 | if (!$this->shouldBeCached) return; |
185 | if (!is_dir($GLOBALS['config']['PAGECACHE'])) { mkdir($GLOBALS['config']['PAGECACHE'],0705); chmod($GLOBALS['config']['PAGECACHE'],0705); } | ||
186 | file_put_contents($this->filename,$page); | 183 | file_put_contents($this->filename,$page); |
187 | } | 184 | } |
188 | 185 | ||
@@ -221,8 +218,8 @@ function nl2br_escaped($html) | |||
221 | return str_replace('>','>',str_replace('<','<',nl2br($html))); | 218 | return str_replace('>','>',str_replace('<','<',nl2br($html))); |
222 | } | 219 | } |
223 | 220 | ||
224 | /* Returns the small hash of a string | 221 | /* Returns the small hash of a string, using RFC 4648 base64url format |
225 | eg. smallHash('20111006_131924') --> yZH23w | 222 | e.g. smallHash('20111006_131924') --> yZH23w |
226 | Small hashes: | 223 | Small hashes: |
227 | - are unique (well, as unique as crc32, at last) | 224 | - are unique (well, as unique as crc32, at last) |
228 | - are always 6 characters long. | 225 | - are always 6 characters long. |
@@ -233,13 +230,10 @@ function nl2br_escaped($html) | |||
233 | function smallHash($text) | 230 | function smallHash($text) |
234 | { | 231 | { |
235 | $t = rtrim(base64_encode(hash('crc32',$text,true)),'='); | 232 | $t = rtrim(base64_encode(hash('crc32',$text,true)),'='); |
236 | $t = str_replace('+','-',$t); // Get rid of characters which need encoding in URLs. | 233 | return strtr($t, '+/', '-_'); |
237 | $t = str_replace('/','_',$t); | ||
238 | $t = str_replace('=','@',$t); | ||
239 | return $t; | ||
240 | } | 234 | } |
241 | 235 | ||
242 | // In a string, converts urls to clickable links. | 236 | // In a string, converts URLs to clickable links. |
243 | // Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 | 237 | // Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 |
244 | function text2clickable($url) | 238 | function text2clickable($url) |
245 | { | 239 | { |
@@ -260,8 +254,8 @@ function keepMultipleSpaces($text) | |||
260 | function autoLocale() | 254 | function autoLocale() |
261 | { | 255 | { |
262 | $loc='en_US'; // Default if browser does not send HTTP_ACCEPT_LANGUAGE | 256 | $loc='en_US'; // Default if browser does not send HTTP_ACCEPT_LANGUAGE |
263 | if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // eg. "fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3" | 257 | if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // e.g. "fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3" |
264 | { // (It's a bit crude, but it works very well. Prefered language is always presented first.) | 258 | { // (It's a bit crude, but it works very well. Preferred language is always presented first.) |
265 | if (preg_match('/([a-z]{2}(-[a-z]{2})?)/i',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches)) $loc=$matches[1]; | 259 | if (preg_match('/([a-z]{2}(-[a-z]{2})?)/i',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches)) $loc=$matches[1]; |
266 | } | 260 | } |
267 | setlocale(LC_TIME,$loc); // LC_TIME = Set local for date/time format only. | 261 | setlocale(LC_TIME,$loc); // LC_TIME = Set local for date/time format only. |
@@ -297,16 +291,20 @@ function allIPs() | |||
297 | return $ip; | 291 | return $ip; |
298 | } | 292 | } |
299 | 293 | ||
294 | function fillSessionInfo() { | ||
295 | $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid) | ||
296 | $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked. | ||
297 | $_SESSION['username']=$GLOBALS['login']; | ||
298 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration. | ||
299 | } | ||
300 | |||
300 | // Check that user/password is correct. | 301 | // Check that user/password is correct. |
301 | function check_auth($login,$password) | 302 | function check_auth($login,$password) |
302 | { | 303 | { |
303 | $hash = sha1($password.$login.$GLOBALS['salt']); | 304 | $hash = sha1($password.$login.$GLOBALS['salt']); |
304 | if ($login==$GLOBALS['login'] && $hash==$GLOBALS['hash']) | 305 | if ($login==$GLOBALS['login'] && $hash==$GLOBALS['hash']) |
305 | { // Login/password is correct. | 306 | { // Login/password is correct. |
306 | $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // generate unique random number (different than phpsessionid) | 307 | fillSessionInfo(); |
307 | $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked. | ||
308 | $_SESSION['username']=$login; | ||
309 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration. | ||
310 | logm('Login successful'); | 308 | logm('Login successful'); |
311 | return True; | 309 | return True; |
312 | } | 310 | } |
@@ -321,6 +319,11 @@ function isLoggedIn() | |||
321 | 319 | ||
322 | if (!isset($GLOBALS['login'])) return false; // Shaarli is not configured yet. | 320 | if (!isset($GLOBALS['login'])) return false; // Shaarli is not configured yet. |
323 | 321 | ||
322 | if (@$_COOKIE['shaarli_staySignedIn']===STAY_SIGNED_IN_TOKEN) | ||
323 | { | ||
324 | fillSessionInfo(); | ||
325 | return true; | ||
326 | } | ||
324 | // If session does not exist on server side, or IP address has changed, or session has expired, logout. | 327 | // If session does not exist on server side, or IP address has changed, or session has expired, logout. |
325 | if (empty($_SESSION['uid']) || ($GLOBALS['disablesessionprotection']==false && $_SESSION['ip']!=allIPs()) || time()>=$_SESSION['expires_on']) | 328 | if (empty($_SESSION['uid']) || ($GLOBALS['disablesessionprotection']==false && $_SESSION['ip']!=allIPs()) || time()>=$_SESSION['expires_on']) |
326 | { | 329 | { |
@@ -334,7 +337,9 @@ function isLoggedIn() | |||
334 | } | 337 | } |
335 | 338 | ||
336 | // Force logout. | 339 | // Force logout. |
337 | function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); } } | 340 | function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); } |
341 | setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH); | ||
342 | } | ||
338 | 343 | ||
339 | 344 | ||
340 | // ------------------------------------------------------------------------------------------ | 345 | // ------------------------------------------------------------------------------------------ |
@@ -391,17 +396,18 @@ if (isset($_POST['login'])) | |||
391 | { | 396 | { |
392 | if (!ban_canLogin()) die('I said: NO. You are banned for the moment. Go away.'); | 397 | if (!ban_canLogin()) die('I said: NO. You are banned for the moment. Go away.'); |
393 | if (isset($_POST['password']) && tokenOk($_POST['token']) && (check_auth($_POST['login'], $_POST['password']))) | 398 | if (isset($_POST['password']) && tokenOk($_POST['token']) && (check_auth($_POST['login'], $_POST['password']))) |
394 | { // Login/password is ok. | 399 | { // Login/password is OK. |
395 | ban_loginOk(); | 400 | ban_loginOk(); |
396 | // If user wants to keep the session cookie even after the browser closes: | 401 | // If user wants to keep the session cookie even after the browser closes: |
397 | if (!empty($_POST['longlastingsession'])) | 402 | if (!empty($_POST['longlastingsession'])) |
398 | { | 403 | { |
404 | setcookie('shaarli_staySignedIn', STAY_SIGNED_IN_TOKEN, time()+31536000, WEB_PATH); | ||
399 | $_SESSION['longlastingsession']=31536000; // (31536000 seconds = 1 year) | 405 | $_SESSION['longlastingsession']=31536000; // (31536000 seconds = 1 year) |
400 | $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // Set session expiration on server-side. | 406 | $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // Set session expiration on server-side. |
401 | 407 | ||
402 | $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/'; | 408 | $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/'; |
403 | session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['HTTP_HOST']); // Set session cookie expiration on client side | 409 | session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['HTTP_HOST']); // Set session cookie expiration on client side |
404 | // Note: Never forget the trailing slash on the cookie path ! | 410 | // Note: Never forget the trailing slash on the cookie path! |
405 | session_regenerate_id(true); // Send cookie with new expiration date to browser. | 411 | session_regenerate_id(true); // Send cookie with new expiration date to browser. |
406 | } | 412 | } |
407 | else // Standard session expiration (=when browser closes) | 413 | else // Standard session expiration (=when browser closes) |
@@ -411,7 +417,7 @@ if (isset($_POST['login'])) | |||
411 | session_regenerate_id(true); | 417 | session_regenerate_id(true); |
412 | } | 418 | } |
413 | // Optional redirect after login: | 419 | // Optional redirect after login: |
414 | if (isset($_GET['post'])) { header('Location: ?post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); exit; } | 420 | if (isset($_GET['post'])) { header('Location: ?post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); exit; } |
415 | if (isset($_POST['returnurl'])) | 421 | if (isset($_POST['returnurl'])) |
416 | { | 422 | { |
417 | if (endsWith($_POST['returnurl'],'?do=login')) { header('Location: ?'); exit; } // Prevent loops over login screen. | 423 | if (endsWith($_POST['returnurl'],'?do=login')) { header('Location: ?'); exit; } // Prevent loops over login screen. |
@@ -423,7 +429,7 @@ if (isset($_POST['login'])) | |||
423 | { | 429 | { |
424 | ban_loginFailed(); | 430 | ban_loginFailed(); |
425 | $redir = ''; | 431 | $redir = ''; |
426 | if (isset($_GET['post'])) { $redir = '&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):''); } | 432 | if (isset($_GET['post'])) { $redir = '&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):''); } |
427 | echo '<script language="JavaScript">alert("Wrong login/password.");document.location=\'?do=login'.$redir.'\';</script>'; // Redirect to login screen. | 433 | echo '<script language="JavaScript">alert("Wrong login/password.");document.location=\'?do=login'.$redir.'\';</script>'; // Redirect to login screen. |
428 | exit; | 434 | exit; |
429 | } | 435 | } |
@@ -433,7 +439,7 @@ if (isset($_POST['login'])) | |||
433 | // Misc utility functions: | 439 | // Misc utility functions: |
434 | 440 | ||
435 | // Returns the server URL (including port and http/https), without path. | 441 | // Returns the server URL (including port and http/https), without path. |
436 | // eg. "http://myserver.com:8080" | 442 | // e.g. "http://myserver.com:8080" |
437 | // You can append $_SERVER['SCRIPT_NAME'] to get the current script URL. | 443 | // You can append $_SERVER['SCRIPT_NAME'] to get the current script URL. |
438 | function serverUrl() | 444 | function serverUrl() |
439 | { | 445 | { |
@@ -443,24 +449,24 @@ function serverUrl() | |||
443 | } | 449 | } |
444 | 450 | ||
445 | // Returns the absolute URL of current script, without the query. | 451 | // Returns the absolute URL of current script, without the query. |
446 | // (eg. http://sebsauvage.net/links/) | 452 | // (e.g. http://sebsauvage.net/links/) |
447 | function indexUrl() | 453 | function indexUrl() |
448 | { | 454 | { |
449 | $scriptname = $_SERVER["SCRIPT_NAME"]; | 455 | $scriptname = $_SERVER["SCRIPT_NAME"]; |
450 | // If the script is named 'index.php', we remove it (for better looking URLs, | 456 | // If the script is named 'index.php', we remove it (for better looking URLs, |
451 | // eg. http://mysite.com/shaarli/?abcde instead of http://mysite.com/shaarli/index.php?abcde) | 457 | // e.g. http://mysite.com/shaarli/?abcde instead of http://mysite.com/shaarli/index.php?abcde) |
452 | if (endswith($scriptname,'index.php')) $scriptname = substr($scriptname,0,strlen($scriptname)-9); | 458 | if (endswith($scriptname,'index.php')) $scriptname = substr($scriptname,0,strlen($scriptname)-9); |
453 | return serverUrl() . $scriptname; | 459 | return serverUrl() . $scriptname; |
454 | } | 460 | } |
455 | 461 | ||
456 | // Returns the absolute URL of current script, WITH the query. | 462 | // Returns the absolute URL of current script, WITH the query. |
457 | // (eg. http://sebsauvage.net/links/?toto=titi&spamspamspam=humbug) | 463 | // (e.g. http://sebsauvage.net/links/?toto=titi&spamspamspam=humbug) |
458 | function pageUrl() | 464 | function pageUrl() |
459 | { | 465 | { |
460 | return indexUrl().(!empty($_SERVER["QUERY_STRING"]) ? '?'.$_SERVER["QUERY_STRING"] : ''); | 466 | return indexUrl().(!empty($_SERVER["QUERY_STRING"]) ? '?'.$_SERVER["QUERY_STRING"] : ''); |
461 | } | 467 | } |
462 | 468 | ||
463 | // Convert post_max_size/upload_max_filesize (eg.'16M') parameters to bytes. | 469 | // Convert post_max_size/upload_max_filesize (e.g. '16M') parameters to bytes. |
464 | function return_bytes($val) | 470 | function return_bytes($val) |
465 | { | 471 | { |
466 | $val = trim($val); $last=strtolower($val[strlen($val)-1]); | 472 | $val = trim($val); $last=strtolower($val[strlen($val)-1]); |
@@ -481,7 +487,7 @@ function getMaxFileSize() | |||
481 | $size2 = return_bytes(ini_get('upload_max_filesize')); | 487 | $size2 = return_bytes(ini_get('upload_max_filesize')); |
482 | // Return the smaller of two: | 488 | // Return the smaller of two: |
483 | $maxsize = min($size1,$size2); | 489 | $maxsize = min($size1,$size2); |
484 | // FIXME: Then convert back to readable notations ? (eg. 2M instead of 2000000) | 490 | // FIXME: Then convert back to readable notations ? (e.g. 2M instead of 2000000) |
485 | return $maxsize; | 491 | return $maxsize; |
486 | } | 492 | } |
487 | 493 | ||
@@ -529,7 +535,7 @@ function linkdate2iso8601($linkdate) | |||
529 | function linkdate2locale($linkdate) | 535 | function linkdate2locale($linkdate) |
530 | { | 536 | { |
531 | return utf8_encode(strftime('%c',linkdate2timestamp($linkdate))); // %c is for automatic date format according to locale. | 537 | return utf8_encode(strftime('%c',linkdate2timestamp($linkdate))); // %c is for automatic date format according to locale. |
532 | // Note that if you use a local which is not installed on your webserver, | 538 | // Note that if you use a locale which is not installed on your webserver, |
533 | // the date will not be displayed in the chosen locale, but probably in US notation. | 539 | // the date will not be displayed in the chosen locale, but probably in US notation. |
534 | } | 540 | } |
535 | 541 | ||
@@ -551,10 +557,10 @@ function http_parse_headers_shaarli( $headers ) | |||
551 | } | 557 | } |
552 | 558 | ||
553 | /* GET an URL. | 559 | /* GET an URL. |
554 | Input: $url : url to get (http://...) | 560 | Input: $url : URL to get (http://...) |
555 | $timeout : Network timeout (will wait this many seconds for an anwser before giving up). | 561 | $timeout : Network timeout (will wait this many seconds for an anwser before giving up). |
556 | Output: An array. [0] = HTTP status message (eg. "HTTP/1.1 200 OK") or error message | 562 | Output: An array. [0] = HTTP status message (e.g. "HTTP/1.1 200 OK") or error message |
557 | [1] = associative array containing HTTP response headers (eg. echo getHTTP($url)[1]['Content-Type']) | 563 | [1] = associative array containing HTTP response headers (e.g. echo getHTTP($url)[1]['Content-Type']) |
558 | [2] = data | 564 | [2] = data |
559 | Example: list($httpstatus,$headers,$data) = getHTTP('http://sebauvage.net/'); | 565 | Example: list($httpstatus,$headers,$data) = getHTTP('http://sebauvage.net/'); |
560 | if (strpos($httpstatus,'200 OK')!==false) | 566 | if (strpos($httpstatus,'200 OK')!==false) |
@@ -570,11 +576,11 @@ function getHTTP($url,$timeout=30) | |||
570 | $context = stream_context_create($options); | 576 | $context = stream_context_create($options); |
571 | $data=file_get_contents($url,false,$context,-1, 4000000); // We download at most 4 Mb from source. | 577 | $data=file_get_contents($url,false,$context,-1, 4000000); // We download at most 4 Mb from source. |
572 | if (!$data) { return array('HTTP Error',array(),''); } | 578 | if (!$data) { return array('HTTP Error',array(),''); } |
573 | $httpStatus=$http_response_header[0]; // eg. "HTTP/1.1 200 OK" | 579 | $httpStatus=$http_response_header[0]; // e.g. "HTTP/1.1 200 OK" |
574 | $responseHeaders=http_parse_headers_shaarli($http_response_header); | 580 | $responseHeaders=http_parse_headers_shaarli($http_response_header); |
575 | return array($httpStatus,$responseHeaders,$data); | 581 | return array($httpStatus,$responseHeaders,$data); |
576 | } | 582 | } |
577 | catch (Exception $e) // getHTTP *can* fail silentely (we don't care if the title cannot be fetched) | 583 | catch (Exception $e) // getHTTP *can* fail silently (we don't care if the title cannot be fetched) |
578 | { | 584 | { |
579 | return array($e->getMessage(),'',''); | 585 | return array($e->getMessage(),'',''); |
580 | } | 586 | } |
@@ -600,14 +606,14 @@ function getToken() | |||
600 | return $rnd; | 606 | return $rnd; |
601 | } | 607 | } |
602 | 608 | ||
603 | // Tells if a token is ok. Using this function will destroy the token. | 609 | // Tells if a token is OK. Using this function will destroy the token. |
604 | // true=token is ok. | 610 | // true=token is OK. |
605 | function tokenOk($token) | 611 | function tokenOk($token) |
606 | { | 612 | { |
607 | if (isset($_SESSION['tokens'][$token])) | 613 | if (isset($_SESSION['tokens'][$token])) |
608 | { | 614 | { |
609 | unset($_SESSION['tokens'][$token]); // Token is used: destroy it. | 615 | unset($_SESSION['tokens'][$token]); // Token is used: destroy it. |
610 | return true; // Token is ok. | 616 | return true; // Token is OK. |
611 | } | 617 | } |
612 | return false; // Wrong token, or already used. | 618 | return false; // Wrong token, or already used. |
613 | } | 619 | } |
@@ -642,8 +648,9 @@ class pageBuilder | |||
642 | $this->tpl->assign('version',shaarli_version); | 648 | $this->tpl->assign('version',shaarli_version); |
643 | $this->tpl->assign('scripturl',indexUrl()); | 649 | $this->tpl->assign('scripturl',indexUrl()); |
644 | $this->tpl->assign('pagetitle','Shaarli'); | 650 | $this->tpl->assign('pagetitle','Shaarli'); |
645 | $this->tpl->assign('privateonly',!empty($_SESSION['privateonly'])); // Show only private links ? | 651 | $this->tpl->assign('privateonly',!empty($_SESSION['privateonly'])); // Show only private links? |
646 | if (!empty($GLOBALS['title'])) $this->tpl->assign('pagetitle',$GLOBALS['title']); | 652 | if (!empty($GLOBALS['title'])) $this->tpl->assign('pagetitle',$GLOBALS['title']); |
653 | if (!empty($GLOBALS['titleLink'])) $this->tpl->assign('titleLink',$GLOBALS['titleLink']); | ||
647 | if (!empty($GLOBALS['pagetitle'])) $this->tpl->assign('pagetitle',$GLOBALS['pagetitle']); | 654 | if (!empty($GLOBALS['pagetitle'])) $this->tpl->assign('pagetitle',$GLOBALS['pagetitle']); |
648 | $this->tpl->assign('shaarlititle',empty($GLOBALS['title']) ? 'Shaarli': $GLOBALS['title'] ); | 655 | $this->tpl->assign('shaarlititle',empty($GLOBALS['title']) ? 'Shaarli': $GLOBALS['title'] ); |
649 | return; | 656 | return; |
@@ -657,7 +664,7 @@ class pageBuilder | |||
657 | } | 664 | } |
658 | 665 | ||
659 | // Render a specific page (using a template). | 666 | // Render a specific page (using a template). |
660 | // eg. pb.renderPage('picwall') | 667 | // e.g. pb.renderPage('picwall') |
661 | public function renderPage($page) | 668 | public function renderPage($page) |
662 | { | 669 | { |
663 | if ($this->tpl===false) $this->initialize(); // Lazy initialization | 670 | if ($this->tpl===false) $this->initialize(); // Lazy initialization |
@@ -676,10 +683,10 @@ class pageBuilder | |||
676 | 683 | ||
677 | Available keys: | 684 | Available keys: |
678 | title : Title of the link | 685 | title : Title of the link |
679 | url : URL of the link. Can be absolute or relative. Relative URLs are permalinks (eg.'?m-ukcw') | 686 | url : URL of the link. Can be absolute or relative. Relative URLs are permalinks (e.g.'?m-ukcw') |
680 | description : description of the entry | 687 | description : description of the entry |
681 | private : Is this link private ? 0=no, other value=yes | 688 | private : Is this link private? 0=no, other value=yes |
682 | linkdate : date of the creation of this entry, in the form YYYYMMDD_HHMMSS (eg.'20110914_192317') | 689 | linkdate : date of the creation of this entry, in the form YYYYMMDD_HHMMSS (e.g.'20110914_192317') |
683 | tags : tags attached to this entry (separated by spaces) | 690 | tags : tags attached to this entry (separated by spaces) |
684 | 691 | ||
685 | We implement 3 interfaces: | 692 | We implement 3 interfaces: |
@@ -689,15 +696,15 @@ class pageBuilder | |||
689 | */ | 696 | */ |
690 | class linkdb implements Iterator, Countable, ArrayAccess | 697 | class linkdb implements Iterator, Countable, ArrayAccess |
691 | { | 698 | { |
692 | private $links; // List of links (associative array. Key=linkdate (eg. "20110823_124546"), value= associative array (keys:title,description...) | 699 | private $links; // List of links (associative array. Key=linkdate (e.g. "20110823_124546"), value= associative array (keys:title,description...) |
693 | private $urls; // List of all recorded URLs (key=url, value=linkdate) for fast reserve search (url-->linkdate) | 700 | private $urls; // List of all recorded URLs (key=url, value=linkdate) for fast reserve search (url-->linkdate) |
694 | private $keys; // List of linkdate keys (for the Iterator interface implementation) | 701 | private $keys; // List of linkdate keys (for the Iterator interface implementation) |
695 | private $position; // Position in the $this->keys array. (for the Iterator interface implementation.) | 702 | private $position; // Position in the $this->keys array. (for the Iterator interface implementation.) |
696 | private $loggedin; // Is the used logged in ? (used to filter private links) | 703 | private $loggedin; // Is the user logged in? (used to filter private links) |
697 | 704 | ||
698 | // Constructor: | 705 | // Constructor: |
699 | function __construct($isLoggedIn) | 706 | function __construct($isLoggedIn) |
700 | // Input : $isLoggedIn : is the used logged in ? | 707 | // Input : $isLoggedIn : is the user logged in? |
701 | { | 708 | { |
702 | $this->loggedin = $isLoggedIn; | 709 | $this->loggedin = $isLoggedIn; |
703 | $this->checkdb(); // Make sure data file exists. | 710 | $this->checkdb(); // Make sure data file exists. |
@@ -711,7 +718,7 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
711 | public function offsetSet($offset, $value) | 718 | public function offsetSet($offset, $value) |
712 | { | 719 | { |
713 | if (!$this->loggedin) die('You are not authorized to add a link.'); | 720 | if (!$this->loggedin) die('You are not authorized to add a link.'); |
714 | if (empty($value['linkdate']) || empty($value['url'])) die('Internal Error: A link should always have a linkdate and url.'); | 721 | if (empty($value['linkdate']) || empty($value['url'])) die('Internal Error: A link should always have a linkdate and URL.'); |
715 | if (empty($offset)) die('You must specify a key.'); | 722 | if (empty($offset)) die('You must specify a key.'); |
716 | $this->links[$offset] = $value; | 723 | $this->links[$offset] = $value; |
717 | $this->urls[$value['url']]=$offset; | 724 | $this->urls[$value['url']]=$offset; |
@@ -774,19 +781,19 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
774 | invalidateCaches(); | 781 | invalidateCaches(); |
775 | } | 782 | } |
776 | 783 | ||
777 | // Returns the link for a given URL (if it exists). false it does not exist. | 784 | // Returns the link for a given URL (if it exists). False if it does not exist. |
778 | public function getLinkFromUrl($url) | 785 | public function getLinkFromUrl($url) |
779 | { | 786 | { |
780 | if (isset($this->urls[$url])) return $this->links[$this->urls[$url]]; | 787 | if (isset($this->urls[$url])) return $this->links[$this->urls[$url]]; |
781 | return false; | 788 | return false; |
782 | } | 789 | } |
783 | 790 | ||
784 | // Case insentitive search among links (in url, title and description). Returns filtered list of links. | 791 | // Case insensitive search among links (in the URLs, title and description). Returns filtered list of links. |
785 | // eg. print_r($mydb->filterFulltext('hollandais')); | 792 | // e.g. print_r($mydb->filterFulltext('hollandais')); |
786 | public function filterFulltext($searchterms) | 793 | public function filterFulltext($searchterms) |
787 | { | 794 | { |
788 | // FIXME: explode(' ',$searchterms) and perform a AND search. | 795 | // FIXME: explode(' ',$searchterms) and perform a AND search. |
789 | // FIXME: accept double-quotes to search for a string "as is" ? | 796 | // FIXME: accept double-quotes to search for a string "as is"? |
790 | $filtered=array(); | 797 | $filtered=array(); |
791 | $s = strtolower($searchterms); | 798 | $s = strtolower($searchterms); |
792 | foreach($this->links as $l) | 799 | foreach($this->links as $l) |
@@ -803,7 +810,7 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
803 | 810 | ||
804 | // Filter by tag. | 811 | // Filter by tag. |
805 | // You can specify one or more tags (tags can be separated by space or comma). | 812 | // You can specify one or more tags (tags can be separated by space or comma). |
806 | // eg. print_r($mydb->filterTags('linux programming')); | 813 | // e.g. print_r($mydb->filterTags('linux programming')); |
807 | public function filterTags($tags,$casesensitive=false) | 814 | public function filterTags($tags,$casesensitive=false) |
808 | { | 815 | { |
809 | $t = str_replace(',',' ',($casesensitive?$tags:strtolower($tags))); | 816 | $t = str_replace(',',' ',($casesensitive?$tags:strtolower($tags))); |
@@ -819,9 +826,9 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
819 | return $filtered; | 826 | return $filtered; |
820 | } | 827 | } |
821 | 828 | ||
822 | // Filter by day. Day must be in the form 'YYYYMMDD' (eg. '20120125') | 829 | // Filter by day. Day must be in the form 'YYYYMMDD' (e.g. '20120125') |
823 | // Sort order is: older articles first. | 830 | // Sort order is: older articles first. |
824 | // eg. print_r($mydb->filterDay('20120125')); | 831 | // e.g. print_r($mydb->filterDay('20120125')); |
825 | public function filterDay($day) | 832 | public function filterDay($day) |
826 | { | 833 | { |
827 | $filtered=array(); | 834 | $filtered=array(); |
@@ -876,13 +883,13 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
876 | } | 883 | } |
877 | 884 | ||
878 | // ------------------------------------------------------------------------------------------ | 885 | // ------------------------------------------------------------------------------------------ |
879 | // Ouput the last N links in RSS 2.0 format. | 886 | // Output the last N links in RSS 2.0 format. |
880 | function showRSS() | 887 | function showRSS() |
881 | { | 888 | { |
882 | header('Content-Type: application/rss+xml; charset=utf-8'); | 889 | header('Content-Type: application/rss+xml; charset=utf-8'); |
883 | 890 | ||
884 | // $usepermalink : If true, use permalink instead of final link. | 891 | // $usepermalink : If true, use permalink instead of final link. |
885 | // User just has to add 'permalink' in URL parameters. eg. http://mysite.com/shaarli/?do=rss&permalinks | 892 | // User just has to add 'permalink' in URL parameters. e.g. http://mysite.com/shaarli/?do=rss&permalinks |
886 | $usepermalinks = isset($_GET['permalinks']); | 893 | $usepermalinks = isset($_GET['permalinks']); |
887 | 894 | ||
888 | // Cache system | 895 | // Cache system |
@@ -891,9 +898,9 @@ function showRSS() | |||
891 | $cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; } | 898 | $cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; } |
892 | 899 | ||
893 | // If cached was not found (or not usable), then read the database and build the response: | 900 | // If cached was not found (or not usable), then read the database and build the response: |
894 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). | 901 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if user it not logged in). |
895 | 902 | ||
896 | // Optionnaly filter the results: | 903 | // Optionally filter the results: |
897 | $linksToDisplay=array(); | 904 | $linksToDisplay=array(); |
898 | if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); | 905 | if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); |
899 | elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); | 906 | elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); |
@@ -942,7 +949,7 @@ function showRSS() | |||
942 | echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$descriptionlink.']]></description>'."\n</item>\n"; | 949 | echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$descriptionlink.']]></description>'."\n</item>\n"; |
943 | $i++; | 950 | $i++; |
944 | } | 951 | } |
945 | echo '</channel></rss><!-- Cached version of '.pageUrl().' -->'; | 952 | echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; |
946 | 953 | ||
947 | $cache->cache(ob_get_contents()); | 954 | $cache->cache(ob_get_contents()); |
948 | ob_end_flush(); | 955 | ob_end_flush(); |
@@ -950,13 +957,13 @@ function showRSS() | |||
950 | } | 957 | } |
951 | 958 | ||
952 | // ------------------------------------------------------------------------------------------ | 959 | // ------------------------------------------------------------------------------------------ |
953 | // Ouput the last N links in ATOM format. | 960 | // Output the last N links in ATOM format. |
954 | function showATOM() | 961 | function showATOM() |
955 | { | 962 | { |
956 | header('Content-Type: application/atom+xml; charset=utf-8'); | 963 | header('Content-Type: application/atom+xml; charset=utf-8'); |
957 | 964 | ||
958 | // $usepermalink : If true, use permalink instead of final link. | 965 | // $usepermalink : If true, use permalink instead of final link. |
959 | // User just has to add 'permalink' in URL parameters. eg. http://mysite.com/shaarli/?do=atom&permalinks | 966 | // User just has to add 'permalink' in URL parameters. e.g. http://mysite.com/shaarli/?do=atom&permalinks |
960 | $usepermalinks = isset($_GET['permalinks']); | 967 | $usepermalinks = isset($_GET['permalinks']); |
961 | 968 | ||
962 | // Cache system | 969 | // Cache system |
@@ -968,7 +975,7 @@ function showATOM() | |||
968 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). | 975 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). |
969 | 976 | ||
970 | 977 | ||
971 | // Optionnaly filter the results: | 978 | // Optionally filter the results: |
972 | $linksToDisplay=array(); | 979 | $linksToDisplay=array(); |
973 | if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); | 980 | if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); |
974 | elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); | 981 | elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); |
@@ -1027,7 +1034,7 @@ function showATOM() | |||
1027 | $feed.='<author><name>'.htmlspecialchars($pageaddr).'</name><uri>'.htmlspecialchars($pageaddr).'</uri></author>'; | 1034 | $feed.='<author><name>'.htmlspecialchars($pageaddr).'</name><uri>'.htmlspecialchars($pageaddr).'</uri></author>'; |
1028 | $feed.='<id>'.htmlspecialchars($pageaddr).'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do. | 1035 | $feed.='<id>'.htmlspecialchars($pageaddr).'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do. |
1029 | $feed.=$entries; | 1036 | $feed.=$entries; |
1030 | $feed.='</feed><!-- Cached version of '.pageUrl().' -->'; | 1037 | $feed.='</feed><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; |
1031 | echo $feed; | 1038 | echo $feed; |
1032 | 1039 | ||
1033 | $cache->cache(ob_get_contents()); | 1040 | $cache->cache(ob_get_contents()); |
@@ -1064,7 +1071,7 @@ function showDailyRSS() | |||
1064 | if (empty($days[$day])) $days[$day]=array(); | 1071 | if (empty($days[$day])) $days[$day]=array(); |
1065 | $days[$day][]=$linkdate; | 1072 | $days[$day][]=$linkdate; |
1066 | } | 1073 | } |
1067 | if (count($days)>$nb_of_days) break; // Have we collected enough days ? | 1074 | if (count($days)>$nb_of_days) break; // Have we collected enough days? |
1068 | } | 1075 | } |
1069 | 1076 | ||
1070 | // Build the RSS feed. | 1077 | // Build the RSS feed. |
@@ -1104,7 +1111,7 @@ function showDailyRSS() | |||
1104 | echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n"; | 1111 | echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n"; |
1105 | 1112 | ||
1106 | } | 1113 | } |
1107 | echo '</channel></rss><!-- Cached version of '.pageUrl().' -->'; | 1114 | echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->'; |
1108 | 1115 | ||
1109 | $cache->cache(ob_get_contents()); | 1116 | $cache->cache(ob_get_contents()); |
1110 | ob_end_flush(); | 1117 | ob_end_flush(); |
@@ -1143,7 +1150,7 @@ function showDaily() | |||
1143 | } | 1150 | } |
1144 | 1151 | ||
1145 | /* We need to spread the articles on 3 columns. | 1152 | /* We need to spread the articles on 3 columns. |
1146 | I did not want to use a javascript lib like http://masonry.desandro.com/ | 1153 | I did not want to use a JavaScript lib like http://masonry.desandro.com/ |
1147 | so I manually spread entries with a simple method: I roughly evaluate the | 1154 | so I manually spread entries with a simple method: I roughly evaluate the |
1148 | height of a div according to title and description length. | 1155 | height of a div according to title and description length. |
1149 | */ | 1156 | */ |
@@ -1154,7 +1161,7 @@ function showDaily() | |||
1154 | // Roughly estimate length of entry (by counting characters) | 1161 | // Roughly estimate length of entry (by counting characters) |
1155 | // Title: 30 chars = 1 line. 1 line is 30 pixels height. | 1162 | // Title: 30 chars = 1 line. 1 line is 30 pixels height. |
1156 | // Description: 836 characters gives roughly 342 pixel height. | 1163 | // Description: 836 characters gives roughly 342 pixel height. |
1157 | // This is not perfect, but it's usually ok. | 1164 | // This is not perfect, but it's usually OK. |
1158 | $length=strlen($link['title'])+(342*strlen($link['description']))/836; | 1165 | $length=strlen($link['title'])+(342*strlen($link['description']))/836; |
1159 | if ($link['thumbnail']) $length +=100; // 1 thumbnails roughly takes 100 pixels height. | 1166 | if ($link['thumbnail']) $length +=100; // 1 thumbnails roughly takes 100 pixels height. |
1160 | // Then put in column which is the less filled: | 1167 | // Then put in column which is the less filled: |
@@ -1207,7 +1214,7 @@ function renderPage() | |||
1207 | // -------- Picture wall | 1214 | // -------- Picture wall |
1208 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=picwall')) | 1215 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=picwall')) |
1209 | { | 1216 | { |
1210 | // Optionnaly filter the results: | 1217 | // Optionally filter the results: |
1211 | $links=array(); | 1218 | $links=array(); |
1212 | if (!empty($_GET['searchterm'])) $links = $LINKSDB->filterFulltext($_GET['searchterm']); | 1219 | if (!empty($_GET['searchterm'])) $links = $LINKSDB->filterFulltext($_GET['searchterm']); |
1213 | elseif (!empty($_GET['searchtags'])) $links = $LINKSDB->filterTags(trim($_GET['searchtags'])); | 1220 | elseif (!empty($_GET['searchtags'])) $links = $LINKSDB->filterTags(trim($_GET['searchtags'])); |
@@ -1287,7 +1294,7 @@ function renderPage() | |||
1287 | if (isset($_GET['linksperpage'])) | 1294 | if (isset($_GET['linksperpage'])) |
1288 | { | 1295 | { |
1289 | if (is_numeric($_GET['linksperpage'])) { $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); } | 1296 | if (is_numeric($_GET['linksperpage'])) { $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); } |
1290 | // Make sure the referer is from Shaarli itself. | 1297 | // Make sure the referrer is Shaarli itself. |
1291 | $referer = '?'; | 1298 | $referer = '?'; |
1292 | if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0) | 1299 | if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0) |
1293 | $referer = $_SERVER['HTTP_REFERER']; | 1300 | $referer = $_SERVER['HTTP_REFERER']; |
@@ -1306,7 +1313,7 @@ function renderPage() | |||
1306 | { | 1313 | { |
1307 | unset($_SESSION['privateonly']); // See all links | 1314 | unset($_SESSION['privateonly']); // See all links |
1308 | } | 1315 | } |
1309 | // Make sure the referer is from Shaarli itself. | 1316 | // Make sure the referrer is Shaarli itself. |
1310 | $referer = '?'; | 1317 | $referer = '?'; |
1311 | if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0) | 1318 | if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0) |
1312 | $referer = $_SERVER['HTTP_REFERER']; | 1319 | $referer = $_SERVER['HTTP_REFERER']; |
@@ -1317,17 +1324,17 @@ function renderPage() | |||
1317 | // -------- Handle other actions allowed for non-logged in users: | 1324 | // -------- Handle other actions allowed for non-logged in users: |
1318 | if (!isLoggedIn()) | 1325 | if (!isLoggedIn()) |
1319 | { | 1326 | { |
1320 | // User tries to post new link but is not loggedin: | 1327 | // User tries to post new link but is not logged in: |
1321 | // Show login screen, then redirect to ?post=... | 1328 | // Show login screen, then redirect to ?post=... |
1322 | if (isset($_GET['post'])) | 1329 | if (isset($_GET['post'])) |
1323 | { | 1330 | { |
1324 | header('Location: ?do=login&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link. | 1331 | header('Location: ?do=login&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link. |
1325 | exit; | 1332 | exit; |
1326 | } | 1333 | } |
1327 | $PAGE = new pageBuilder; | 1334 | $PAGE = new pageBuilder; |
1328 | buildLinkList($PAGE,$LINKSDB); // Compute list of links to display | 1335 | buildLinkList($PAGE,$LINKSDB); // Compute list of links to display |
1329 | $PAGE->renderPage('linklist'); | 1336 | $PAGE->renderPage('linklist'); |
1330 | exit; // Never remove this one ! All operations below are reserved for logged in user. | 1337 | exit; // Never remove this one! All operations below are reserved for logged in user. |
1331 | } | 1338 | } |
1332 | 1339 | ||
1333 | // -------- All other functions are reserved for the registered user: | 1340 | // -------- All other functions are reserved for the registered user: |
@@ -1348,7 +1355,7 @@ function renderPage() | |||
1348 | if ($GLOBALS['config']['OPEN_SHAARLI']) die('You are not supposed to change a password on an Open Shaarli.'); | 1355 | if ($GLOBALS['config']['OPEN_SHAARLI']) die('You are not supposed to change a password on an Open Shaarli.'); |
1349 | if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) | 1356 | if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) |
1350 | { | 1357 | { |
1351 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away ! | 1358 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! |
1352 | 1359 | ||
1353 | // Make sure old password is correct. | 1360 | // Make sure old password is correct. |
1354 | $oldhash = sha1($_POST['oldpassword'].$GLOBALS['login'].$GLOBALS['salt']); | 1361 | $oldhash = sha1($_POST['oldpassword'].$GLOBALS['login'].$GLOBALS['salt']); |
@@ -1375,13 +1382,14 @@ function renderPage() | |||
1375 | { | 1382 | { |
1376 | if (!empty($_POST['title']) ) | 1383 | if (!empty($_POST['title']) ) |
1377 | { | 1384 | { |
1378 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away ! | 1385 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! |
1379 | $tz = 'UTC'; | 1386 | $tz = 'UTC'; |
1380 | if (!empty($_POST['continent']) && !empty($_POST['city'])) | 1387 | if (!empty($_POST['continent']) && !empty($_POST['city'])) |
1381 | if (isTZvalid($_POST['continent'],$_POST['city'])) | 1388 | if (isTZvalid($_POST['continent'],$_POST['city'])) |
1382 | $tz = $_POST['continent'].'/'.$_POST['city']; | 1389 | $tz = $_POST['continent'].'/'.$_POST['city']; |
1383 | $GLOBALS['timezone'] = $tz; | 1390 | $GLOBALS['timezone'] = $tz; |
1384 | $GLOBALS['title']=$_POST['title']; | 1391 | $GLOBALS['title']=$_POST['title']; |
1392 | $GLOBALS['titleLink']=$_POST['titleLink']; | ||
1385 | $GLOBALS['redirector']=$_POST['redirector']; | 1393 | $GLOBALS['redirector']=$_POST['redirector']; |
1386 | $GLOBALS['disablesessionprotection']=!empty($_POST['disablesessionprotection']); | 1394 | $GLOBALS['disablesessionprotection']=!empty($_POST['disablesessionprotection']); |
1387 | $GLOBALS['disablejquery']=!empty($_POST['disablejquery']); | 1395 | $GLOBALS['disablejquery']=!empty($_POST['disablejquery']); |
@@ -1398,7 +1406,7 @@ function renderPage() | |||
1398 | $PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES)); | 1406 | $PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES)); |
1399 | $PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES)); | 1407 | $PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES)); |
1400 | list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']); | 1408 | list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']); |
1401 | $PAGE->assign('timezone_form',$timezone_form); // FIXME: put entire tz form generation in template ? | 1409 | $PAGE->assign('timezone_form',$timezone_form); // FIXME: Put entire tz form generation in template? |
1402 | $PAGE->assign('timezone_js',$timezone_js); | 1410 | $PAGE->assign('timezone_js',$timezone_js); |
1403 | $PAGE->renderPage('configure'); | 1411 | $PAGE->renderPage('configure'); |
1404 | exit; | 1412 | exit; |
@@ -1422,7 +1430,7 @@ function renderPage() | |||
1422 | if (!empty($_POST['deletetag']) && !empty($_POST['fromtag'])) | 1430 | if (!empty($_POST['deletetag']) && !empty($_POST['fromtag'])) |
1423 | { | 1431 | { |
1424 | $needle=trim($_POST['fromtag']); | 1432 | $needle=trim($_POST['fromtag']); |
1425 | $linksToAlter = $LINKSDB->filterTags($needle,true); // true for case-sensitive tag search. | 1433 | $linksToAlter = $LINKSDB->filterTags($needle,true); // True for case-sensitive tag search. |
1426 | foreach($linksToAlter as $key=>$value) | 1434 | foreach($linksToAlter as $key=>$value) |
1427 | { | 1435 | { |
1428 | $tags = explode(' ',trim($value['tags'])); | 1436 | $tags = explode(' ',trim($value['tags'])); |
@@ -1430,7 +1438,7 @@ function renderPage() | |||
1430 | $value['tags']=trim(implode(' ',$tags)); | 1438 | $value['tags']=trim(implode(' ',$tags)); |
1431 | $LINKSDB[$key]=$value; | 1439 | $LINKSDB[$key]=$value; |
1432 | } | 1440 | } |
1433 | $LINKSDB->savedb(); // save to disk | 1441 | $LINKSDB->savedb(); // Save to disk. |
1434 | echo '<script language="JavaScript">alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>'; | 1442 | echo '<script language="JavaScript">alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>'; |
1435 | exit; | 1443 | exit; |
1436 | } | 1444 | } |
@@ -1443,17 +1451,17 @@ function renderPage() | |||
1443 | foreach($linksToAlter as $key=>$value) | 1451 | foreach($linksToAlter as $key=>$value) |
1444 | { | 1452 | { |
1445 | $tags = explode(' ',trim($value['tags'])); | 1453 | $tags = explode(' ',trim($value['tags'])); |
1446 | $tags[array_search($needle,$tags)] = trim($_POST['totag']); // Remplace tags value. | 1454 | $tags[array_search($needle,$tags)] = trim($_POST['totag']); // Replace tags value. |
1447 | $value['tags']=trim(implode(' ',$tags)); | 1455 | $value['tags']=trim(implode(' ',$tags)); |
1448 | $LINKSDB[$key]=$value; | 1456 | $LINKSDB[$key]=$value; |
1449 | } | 1457 | } |
1450 | $LINKSDB->savedb(); // save to disk | 1458 | $LINKSDB->savedb(); // Save to disk. |
1451 | echo '<script language="JavaScript">alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>'; | 1459 | echo '<script language="JavaScript">alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>'; |
1452 | exit; | 1460 | exit; |
1453 | } | 1461 | } |
1454 | } | 1462 | } |
1455 | 1463 | ||
1456 | // -------- User wants to add a link without using the bookmarklet: show form. | 1464 | // -------- User wants to add a link without using the bookmarklet: Show form. |
1457 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=addlink')) | 1465 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=addlink')) |
1458 | { | 1466 | { |
1459 | $PAGE = new pageBuilder; | 1467 | $PAGE = new pageBuilder; |
@@ -1465,7 +1473,7 @@ function renderPage() | |||
1465 | // -------- User clicked the "Save" button when editing a link: Save link to database. | 1473 | // -------- User clicked the "Save" button when editing a link: Save link to database. |
1466 | if (isset($_POST['save_edit'])) | 1474 | if (isset($_POST['save_edit'])) |
1467 | { | 1475 | { |
1468 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away ! | 1476 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! |
1469 | $tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces. | 1477 | $tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces. |
1470 | $linkdate=$_POST['lf_linkdate']; | 1478 | $linkdate=$_POST['lf_linkdate']; |
1471 | $url = trim($_POST['lf_url']); | 1479 | $url = trim($_POST['lf_url']); |
@@ -1475,7 +1483,7 @@ function renderPage() | |||
1475 | 'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags)); | 1483 | 'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags)); |
1476 | if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title. | 1484 | if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title. |
1477 | $LINKSDB[$linkdate] = $link; | 1485 | $LINKSDB[$linkdate] = $link; |
1478 | $LINKSDB->savedb(); // save to disk | 1486 | $LINKSDB->savedb(); // Save to disk. |
1479 | pubsubhub(); | 1487 | pubsubhub(); |
1480 | 1488 | ||
1481 | // If we are called from the bookmarklet, we must close the popup: | 1489 | // If we are called from the bookmarklet, we must close the popup: |
@@ -1489,7 +1497,7 @@ function renderPage() | |||
1489 | // -------- User clicked the "Cancel" button when editing a link. | 1497 | // -------- User clicked the "Cancel" button when editing a link. |
1490 | if (isset($_POST['cancel_edit'])) | 1498 | if (isset($_POST['cancel_edit'])) |
1491 | { | 1499 | { |
1492 | // If we are called from the bookmarklet, we must close the popup; | 1500 | // If we are called from the bookmarklet, we must close the popup: |
1493 | if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; exit; } | 1501 | if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; exit; } |
1494 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); | 1502 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); |
1495 | $returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited. | 1503 | $returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited. |
@@ -1497,12 +1505,12 @@ function renderPage() | |||
1497 | exit; | 1505 | exit; |
1498 | } | 1506 | } |
1499 | 1507 | ||
1500 | // -------- User clicked the "Delete" button when editing a link : Delete link from database. | 1508 | // -------- User clicked the "Delete" button when editing a link: Delete link from database. |
1501 | if (isset($_POST['delete_link'])) | 1509 | if (isset($_POST['delete_link'])) |
1502 | { | 1510 | { |
1503 | if (!tokenOk($_POST['token'])) die('Wrong token.'); | 1511 | if (!tokenOk($_POST['token'])) die('Wrong token.'); |
1504 | // We do not need to ask for confirmation: | 1512 | // We do not need to ask for confirmation: |
1505 | // - confirmation is handled by javascript | 1513 | // - confirmation is handled by JavaScript |
1506 | // - we are protected from XSRF by the token. | 1514 | // - we are protected from XSRF by the token. |
1507 | $linkdate=$_POST['lf_linkdate']; | 1515 | $linkdate=$_POST['lf_linkdate']; |
1508 | unset($LINKSDB[$linkdate]); | 1516 | unset($LINKSDB[$linkdate]); |
@@ -1552,7 +1560,7 @@ function renderPage() | |||
1552 | $tags = (empty($_GET['tags']) ? '' : $_GET['tags'] ); // Get tags if it was provided in URL | 1560 | $tags = (empty($_GET['tags']) ? '' : $_GET['tags'] ); // Get tags if it was provided in URL |
1553 | $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0); // Get private if it was provided in URL | 1561 | $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0); // Get private if it was provided in URL |
1554 | if (($url!='') && parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url; | 1562 | if (($url!='') && parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url; |
1555 | // If this is an HTTP link, we try go get the page to extact the title (otherwise we will to straight to the edit form.) | 1563 | // If this is an HTTP link, we try go get the page to extract the title (otherwise we will to straight to the edit form.) |
1556 | if (empty($title) && parse_url($url,PHP_URL_SCHEME)=='http') | 1564 | if (empty($title) && parse_url($url,PHP_URL_SCHEME)=='http') |
1557 | { | 1565 | { |
1558 | list($status,$headers,$data) = getHTTP($url,4); // Short timeout to keep the application responsive. | 1566 | list($status,$headers,$data) = getHTTP($url,4); // Short timeout to keep the application responsive. |
@@ -1581,7 +1589,11 @@ function renderPage() | |||
1581 | } | 1589 | } |
1582 | } | 1590 | } |
1583 | } | 1591 | } |
1584 | if ($url=='') $url='?'.smallHash($linkdate); // In case of empty URL, this is just a text (with a link that point to itself) | 1592 | if ($url=='') // In case of empty URL, this is just a text (with a link that points to itself) |
1593 | { | ||
1594 | $url='?'.smallHash($linkdate); | ||
1595 | $title='Note: '; | ||
1596 | } | ||
1585 | $link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>$private); | 1597 | $link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>$private); |
1586 | } | 1598 | } |
1587 | 1599 | ||
@@ -1606,7 +1618,7 @@ function renderPage() | |||
1606 | exit; | 1618 | exit; |
1607 | } | 1619 | } |
1608 | $exportWhat=$_GET['what']; | 1620 | $exportWhat=$_GET['what']; |
1609 | if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export ???'); | 1621 | if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export???'); |
1610 | 1622 | ||
1611 | header('Content-Type: text/html; charset=utf-8'); | 1623 | header('Content-Type: text/html; charset=utf-8'); |
1612 | header('Content-disposition: attachment; filename=bookmarks_'.$exportWhat.'_'.strval(date('Ymd_His')).'.html'); | 1624 | header('Content-disposition: attachment; filename=bookmarks_'.$exportWhat.'_'.strval(date('Ymd_His')).'.html'); |
@@ -1679,8 +1691,8 @@ function importFile() | |||
1679 | $filename=$_FILES['filetoupload']['name']; | 1691 | $filename=$_FILES['filetoupload']['name']; |
1680 | $filesize=$_FILES['filetoupload']['size']; | 1692 | $filesize=$_FILES['filetoupload']['size']; |
1681 | $data=file_get_contents($_FILES['filetoupload']['tmp_name']); | 1693 | $data=file_get_contents($_FILES['filetoupload']['tmp_name']); |
1682 | $private = (empty($_POST['private']) ? 0 : 1); // Should the links be imported as private ? | 1694 | $private = (empty($_POST['private']) ? 0 : 1); // Should the links be imported as private? |
1683 | $overwrite = !empty($_POST['overwrite']) ; // Should the imported links overwrite existing ones ? | 1695 | $overwrite = !empty($_POST['overwrite']) ; // Should the imported links overwrite existing ones? |
1684 | $import_count=0; | 1696 | $import_count=0; |
1685 | 1697 | ||
1686 | // Sniff file type: | 1698 | // Sniff file type: |
@@ -1691,7 +1703,7 @@ function importFile() | |||
1691 | if ($type=='netscape') | 1703 | if ($type=='netscape') |
1692 | { | 1704 | { |
1693 | // This is a standard Netscape-style bookmark file. | 1705 | // This is a standard Netscape-style bookmark file. |
1694 | // This format is supported by all browsers (except IE, of course), also delicious, diigo and others. | 1706 | // This format is supported by all browsers (except IE, of course), also Delicious, Diigo and others. |
1695 | foreach(explode('<DT>',$data) as $html) // explode is very fast | 1707 | foreach(explode('<DT>',$data) as $html) // explode is very fast |
1696 | { | 1708 | { |
1697 | $link = array('linkdate'=>'','title'=>'','url'=>'','description'=>'','tags'=>'','private'=>0); | 1709 | $link = array('linkdate'=>'','title'=>'','url'=>'','description'=>'','tags'=>'','private'=>0); |
@@ -1725,14 +1737,14 @@ function importFile() | |||
1725 | 1737 | ||
1726 | // Make sure date/time is not already used by another link. | 1738 | // Make sure date/time is not already used by another link. |
1727 | // (Some bookmark files have several different links with the same ADD_DATE) | 1739 | // (Some bookmark files have several different links with the same ADD_DATE) |
1728 | // We increment date by 1 second until we find a date which is not used in db. | 1740 | // We increment date by 1 second until we find a date which is not used in DB. |
1729 | // (so that links that have the same date/time are more or less kept grouped by date, but do not conflict.) | 1741 | // (so that links that have the same date/time are more or less kept grouped by date, but do not conflict.) |
1730 | while (!empty($LINKSDB[date('Ymd_His',$raw_add_date)])) { $raw_add_date++; }// Yes, I know it's ugly. | 1742 | while (!empty($LINKSDB[date('Ymd_His',$raw_add_date)])) { $raw_add_date++; }// Yes, I know it's ugly. |
1731 | $link['linkdate']=date('Ymd_His',$raw_add_date); | 1743 | $link['linkdate']=date('Ymd_His',$raw_add_date); |
1732 | $LINKSDB[$link['linkdate']] = $link; | 1744 | $LINKSDB[$link['linkdate']] = $link; |
1733 | $import_count++; | 1745 | $import_count++; |
1734 | } | 1746 | } |
1735 | else // link already present in database. | 1747 | else // Link already present in database. |
1736 | { | 1748 | { |
1737 | if ($overwrite) | 1749 | if ($overwrite) |
1738 | { // If overwrite is required, we import link data, except date/time. | 1750 | { // If overwrite is required, we import link data, except date/time. |
@@ -1747,11 +1759,11 @@ function importFile() | |||
1747 | } | 1759 | } |
1748 | $LINKSDB->savedb(); | 1760 | $LINKSDB->savedb(); |
1749 | 1761 | ||
1750 | echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>'; | 1762 | echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>'; |
1751 | } | 1763 | } |
1752 | else | 1764 | else |
1753 | { | 1765 | { |
1754 | echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>'; | 1766 | echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>'; |
1755 | } | 1767 | } |
1756 | } | 1768 | } |
1757 | 1769 | ||
@@ -1783,13 +1795,13 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1783 | { | 1795 | { |
1784 | header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); | 1796 | header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); |
1785 | echo '<h1>404 Not found.</h1>Oh crap. The link you are trying to reach does not exist or has been deleted.'; | 1797 | echo '<h1>404 Not found.</h1>Oh crap. The link you are trying to reach does not exist or has been deleted.'; |
1786 | echo '<br>You would mind <a href="?">clicking here</a> ?'; | 1798 | echo '<br>You would mind <a href="?">clicking here</a>?'; |
1787 | exit; | 1799 | exit; |
1788 | } | 1800 | } |
1789 | $search_type='permalink'; | 1801 | $search_type='permalink'; |
1790 | } | 1802 | } |
1791 | else | 1803 | else |
1792 | $linksToDisplay = $LINKSDB; // otherwise, display without filtering. | 1804 | $linksToDisplay = $LINKSDB; // Otherwise, display without filtering. |
1793 | 1805 | ||
1794 | // Option: Show only private links | 1806 | // Option: Show only private links |
1795 | if (!empty($_SESSION['privateonly'])) | 1807 | if (!empty($_SESSION['privateonly'])) |
@@ -1803,11 +1815,11 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1803 | } | 1815 | } |
1804 | 1816 | ||
1805 | // ---- Handle paging. | 1817 | // ---- Handle paging. |
1806 | /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess ??? | 1818 | /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess??? |
1807 | "Warning: array_keys() expects parameter 1 to be array, object given in ... " | 1819 | "Warning: array_keys() expects parameter 1 to be array, object given in ... " |
1808 | If my class implements ArrayAccess, why won't array_keys() accept it ? ( $keys=array_keys($linksToDisplay); ) | 1820 | If my class implements ArrayAccess, why won't array_keys() accept it ? ( $keys=array_keys($linksToDisplay); ) |
1809 | */ | 1821 | */ |
1810 | $keys=array(); foreach($linksToDisplay as $key=>$value) { $keys[]=$key; } // Stupid and ugly. Thanks php. | 1822 | $keys=array(); foreach($linksToDisplay as $key=>$value) { $keys[]=$key; } // Stupid and ugly. Thanks PHP. |
1811 | 1823 | ||
1812 | // If there is only a single link, we change on-the-fly the title of the page. | 1824 | // If there is only a single link, we change on-the-fly the title of the page. |
1813 | if (count($linksToDisplay)==1) $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title']; | 1825 | if (count($linksToDisplay)==1) $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title']; |
@@ -1854,7 +1866,7 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1854 | $PAGE->assign('result_count',count($linksToDisplay)); | 1866 | $PAGE->assign('result_count',count($linksToDisplay)); |
1855 | $PAGE->assign('search_type',$search_type); | 1867 | $PAGE->assign('search_type',$search_type); |
1856 | $PAGE->assign('search_crits',$search_crits); | 1868 | $PAGE->assign('search_crits',$search_crits); |
1857 | $PAGE->assign('redirector',empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); // optional redirector URL | 1869 | $PAGE->assign('redirector',empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); // Optional redirector URL. |
1858 | $PAGE->assign('token',$token); | 1870 | $PAGE->assign('token',$token); |
1859 | $PAGE->assign('links',$linkDisp); | 1871 | $PAGE->assign('links',$linkDisp); |
1860 | return; | 1872 | return; |
@@ -1862,9 +1874,9 @@ function buildLinkList($PAGE,$LINKSDB) | |||
1862 | 1874 | ||
1863 | // Compute the thumbnail for a link. | 1875 | // Compute the thumbnail for a link. |
1864 | // | 1876 | // |
1865 | // with a link to the original URL. | 1877 | // With a link to the original URL. |
1866 | // Understands various services (youtube.com...) | 1878 | // Understands various services (youtube.com...) |
1867 | // Input: $url = url for which the thumbnail must be found. | 1879 | // Input: $url = URL for which the thumbnail must be found. |
1868 | // $href = if provided, this URL will be followed instead of $url | 1880 | // $href = if provided, this URL will be followed instead of $url |
1869 | // Returns an associative array with thumbnail attributes (src,href,width,height,style,alt) | 1881 | // Returns an associative array with thumbnail attributes (src,href,width,height,style,alt) |
1870 | // Some of them may be missing. | 1882 | // Some of them may be missing. |
@@ -1875,19 +1887,19 @@ function computeThumbnail($url,$href=false) | |||
1875 | if ($href==false) $href=$url; | 1887 | if ($href==false) $href=$url; |
1876 | 1888 | ||
1877 | // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link. | 1889 | // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link. |
1878 | // (eg. http://www.youtube.com/watch?v=spVypYk4kto ---> http://img.youtube.com/vi/spVypYk4kto/default.jpg ) | 1890 | // (e.g. http://www.youtube.com/watch?v=spVypYk4kto ---> http://img.youtube.com/vi/spVypYk4kto/default.jpg ) |
1879 | // ^^^^^^^^^^^ ^^^^^^^^^^^ | 1891 | // ^^^^^^^^^^^ ^^^^^^^^^^^ |
1880 | $domain = parse_url($url,PHP_URL_HOST); | 1892 | $domain = parse_url($url,PHP_URL_HOST); |
1881 | if ($domain=='youtube.com' || $domain=='www.youtube.com') | 1893 | if ($domain=='youtube.com' || $domain=='www.youtube.com') |
1882 | { | 1894 | { |
1883 | parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract video ID and get thumbnail | 1895 | parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract video ID and get thumbnail |
1884 | if (!empty($params['v'])) return array('src'=>'http://img.youtube.com/vi/'.$params['v'].'/default.jpg', | 1896 | if (!empty($params['v'])) return array('src'=>'https://img.youtube.com/vi/'.$params['v'].'/default.jpg', |
1885 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); | 1897 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); |
1886 | } | 1898 | } |
1887 | if ($domain=='youtu.be') // Youtube short links | 1899 | if ($domain=='youtu.be') // Youtube short links |
1888 | { | 1900 | { |
1889 | $path = parse_url($url,PHP_URL_PATH); | 1901 | $path = parse_url($url,PHP_URL_PATH); |
1890 | return array('src'=>'http://img.youtube.com/vi'.$path.'/default.jpg', | 1902 | return array('src'=>'https://img.youtube.com/vi'.$path.'/default.jpg', |
1891 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); | 1903 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); |
1892 | } | 1904 | } |
1893 | if ($domain=='pix.toile-libre.org') // pix.toile-libre.org image hosting | 1905 | if ($domain=='pix.toile-libre.org') // pix.toile-libre.org image hosting |
@@ -1901,18 +1913,18 @@ function computeThumbnail($url,$href=false) | |||
1901 | { | 1913 | { |
1902 | $path = parse_url($url,PHP_URL_PATH); | 1914 | $path = parse_url($url,PHP_URL_PATH); |
1903 | if (startsWith($path,'/a/')) return array(); // Thumbnails for albums are not available. | 1915 | if (startsWith($path,'/a/')) return array(); // Thumbnails for albums are not available. |
1904 | if (startsWith($path,'/r/')) return array('src'=>'http://i.imgur.com/'.basename($path).'s.jpg', | 1916 | if (startsWith($path,'/r/')) return array('src'=>'https://i.imgur.com/'.basename($path).'s.jpg', |
1905 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | 1917 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); |
1906 | if (startsWith($path,'/gallery/')) return array('src'=>'http://i.imgur.com'.substr($path,8).'s.jpg', | 1918 | if (startsWith($path,'/gallery/')) return array('src'=>'https://i.imgur.com'.substr($path,8).'s.jpg', |
1907 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | 1919 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); |
1908 | 1920 | ||
1909 | if (substr_count($path,'/')==1) return array('src'=>'http://i.imgur.com/'.substr($path,1).'s.jpg', | 1921 | if (substr_count($path,'/')==1) return array('src'=>'https://i.imgur.com/'.substr($path,1).'s.jpg', |
1910 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | 1922 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); |
1911 | } | 1923 | } |
1912 | if ($domain=='i.imgur.com') | 1924 | if ($domain=='i.imgur.com') |
1913 | { | 1925 | { |
1914 | $pi = pathinfo(parse_url($url,PHP_URL_PATH)); | 1926 | $pi = pathinfo(parse_url($url,PHP_URL_PATH)); |
1915 | if (!empty($pi['filename'])) return array('src'=>'http://i.imgur.com/'.$pi['filename'].'s.jpg', | 1927 | if (!empty($pi['filename'])) return array('src'=>'https://i.imgur.com/'.$pi['filename'].'s.jpg', |
1916 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | 1928 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); |
1917 | } | 1929 | } |
1918 | if ($domain=='dailymotion.com' || $domain=='www.dailymotion.com') | 1930 | if ($domain=='dailymotion.com' || $domain=='www.dailymotion.com') |
@@ -1948,17 +1960,17 @@ function computeThumbnail($url,$href=false) | |||
1948 | ) | 1960 | ) |
1949 | { | 1961 | { |
1950 | if ($domain=='vimeo.com') | 1962 | if ($domain=='vimeo.com') |
1951 | { // Make sure this vimeo url points to a video (/xxx... where xxx is numeric) | 1963 | { // Make sure this vimeo URL points to a video (/xxx... where xxx is numeric) |
1952 | $path = parse_url($url,PHP_URL_PATH); | 1964 | $path = parse_url($url,PHP_URL_PATH); |
1953 | if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL. | 1965 | if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL. |
1954 | } | 1966 | } |
1955 | if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) | 1967 | if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) |
1956 | { // Make sure this url points to a single comic (/xxx... where xxx is numeric) | 1968 | { // Make sure this URL points to a single comic (/xxx... where xxx is numeric) |
1957 | $path = parse_url($url,PHP_URL_PATH); | 1969 | $path = parse_url($url,PHP_URL_PATH); |
1958 | if (!preg_match('!/\d+.+?!',$path)) return array(); | 1970 | if (!preg_match('!/\d+.+?!',$path)) return array(); |
1959 | } | 1971 | } |
1960 | if ($domain=='ted.com' || endsWith($domain,'.ted.com')) | 1972 | if ($domain=='ted.com' || endsWith($domain,'.ted.com')) |
1961 | { // Make sure this TED url points to a video (/talks/...) | 1973 | { // Make sure this TED URL points to a video (/talks/...) |
1962 | $path = parse_url($url,PHP_URL_PATH); | 1974 | $path = parse_url($url,PHP_URL_PATH); |
1963 | if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. | 1975 | if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. |
1964 | } | 1976 | } |
@@ -1985,7 +1997,7 @@ function computeThumbnail($url,$href=false) | |||
1985 | // Returns the HTML code to display a thumbnail for a link | 1997 | // Returns the HTML code to display a thumbnail for a link |
1986 | // with a link to the original URL. | 1998 | // with a link to the original URL. |
1987 | // Understands various services (youtube.com...) | 1999 | // Understands various services (youtube.com...) |
1988 | // Input: $url = url for which the thumbnail must be found. | 2000 | // Input: $url = URL for which the thumbnail must be found. |
1989 | // $href = if provided, this URL will be followed instead of $url | 2001 | // $href = if provided, this URL will be followed instead of $url |
1990 | // Returns '' if no thumbnail available. | 2002 | // Returns '' if no thumbnail available. |
1991 | function thumbnail($url,$href=false) | 2003 | function thumbnail($url,$href=false) |
@@ -2006,7 +2018,7 @@ function thumbnail($url,$href=false) | |||
2006 | // Returns the HTML code to display a thumbnail for a link | 2018 | // Returns the HTML code to display a thumbnail for a link |
2007 | // for the picture wall (using lazy image loading) | 2019 | // for the picture wall (using lazy image loading) |
2008 | // Understands various services (youtube.com...) | 2020 | // Understands various services (youtube.com...) |
2009 | // Input: $url = url for which the thumbnail must be found. | 2021 | // Input: $url = URL for which the thumbnail must be found. |
2010 | // $href = if provided, this URL will be followed instead of $url | 2022 | // $href = if provided, this URL will be followed instead of $url |
2011 | // Returns '' if no thumbnail available. | 2023 | // Returns '' if no thumbnail available. |
2012 | function lazyThumbnail($url,$href=false) | 2024 | function lazyThumbnail($url,$href=false) |
@@ -2016,7 +2028,7 @@ function lazyThumbnail($url,$href=false) | |||
2016 | 2028 | ||
2017 | $html='<a href="'.htmlspecialchars($t['href']).'">'; | 2029 | $html='<a href="'.htmlspecialchars($t['href']).'">'; |
2018 | 2030 | ||
2019 | // Lazy image (only loaded by javascript when in the viewport). | 2031 | // Lazy image (only loaded by JavaScript when in the viewport). |
2020 | if (!empty($GLOBALS['disablejquery'])) // (except if jQuery is disabled) | 2032 | if (!empty($GLOBALS['disablejquery'])) // (except if jQuery is disabled) |
2021 | $html.='<img class="lazyimage" src="'.htmlspecialchars($t['src']).'"'; | 2033 | $html.='<img class="lazyimage" src="'.htmlspecialchars($t['src']).'"'; |
2022 | else | 2034 | else |
@@ -2028,7 +2040,7 @@ function lazyThumbnail($url,$href=false) | |||
2028 | if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"'; | 2040 | if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"'; |
2029 | $html.='>'; | 2041 | $html.='>'; |
2030 | 2042 | ||
2031 | // No-javascript fallback. | 2043 | // No-JavaScript fallback. |
2032 | $html.='<noscript><img src="'.htmlspecialchars($t['src']).'"'; | 2044 | $html.='<noscript><img src="'.htmlspecialchars($t['src']).'"'; |
2033 | if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"'; | 2045 | if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"'; |
2034 | if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"'; | 2046 | if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"'; |
@@ -2056,7 +2068,9 @@ function install() | |||
2056 | { // Step 2: Check if data in session is correct. | 2068 | { // Step 2: Check if data in session is correct. |
2057 | echo '<pre>Sessions do not seem to work correctly on your server.<br>'; | 2069 | echo '<pre>Sessions do not seem to work correctly on your server.<br>'; |
2058 | echo 'Make sure the variable session.save_path is set correctly in your php config, and that you have write access to it.<br>'; | 2070 | echo 'Make sure the variable session.save_path is set correctly in your php config, and that you have write access to it.<br>'; |
2059 | echo 'It currently points to '.session_save_path().'<br><br><a href="?">Click to try again.</a></pre>'; | 2071 | echo 'It currently points to '.session_save_path().'<br>'; |
2072 | echo 'Check that the hostname used to access Shaarli contains a dot. On some browsers, accessing your server via a hostname like \'localhost\' or any custom hostname without a dot causes cookie storage to fail. We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>'; | ||
2073 | echo '<br><a href="?">Click to try again.</a></pre>'; | ||
2060 | die; | 2074 | die; |
2061 | } | 2075 | } |
2062 | if (!isset($_SESSION['session_tested'])) | 2076 | if (!isset($_SESSION['session_tested'])) |
@@ -2065,7 +2079,7 @@ function install() | |||
2065 | header('Location: '.indexUrl().'?test_session'); // Redirect to check stored data. | 2079 | header('Location: '.indexUrl().'?test_session'); // Redirect to check stored data. |
2066 | } | 2080 | } |
2067 | if (isset($_GET['test_session'])) | 2081 | if (isset($_GET['test_session'])) |
2068 | { // Step 3: Sessions are ok. Remove test parameter from URL. | 2082 | { // Step 3: Sessions are OK. Remove test parameter from URL. |
2069 | header('Location: '.indexUrl()); | 2083 | header('Location: '.indexUrl()); |
2070 | } | 2084 | } |
2071 | 2085 | ||
@@ -2083,7 +2097,7 @@ function install() | |||
2083 | $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']); | 2097 | $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']); |
2084 | $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.htmlspecialchars(indexUrl()) : $_POST['title'] ); | 2098 | $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.htmlspecialchars(indexUrl()) : $_POST['title'] ); |
2085 | writeConfig(); | 2099 | writeConfig(); |
2086 | echo '<script language="JavaScript">alert("Shaarli is now configured. Please enter your login/password and start shaaring your links !");document.location=\'?do=login\';</script>'; | 2100 | echo '<script language="JavaScript">alert("Shaarli is now configured. Please enter your login/password and start shaaring your links!");document.location=\'?do=login\';</script>'; |
2087 | exit; | 2101 | exit; |
2088 | } | 2102 | } |
2089 | 2103 | ||
@@ -2098,14 +2112,14 @@ function install() | |||
2098 | exit; | 2112 | exit; |
2099 | } | 2113 | } |
2100 | 2114 | ||
2101 | // Generates the timezone selection form and javascript. | 2115 | // Generates the timezone selection form and JavaScript. |
2102 | // Input: (optional) current timezone (can be 'UTC/UTC'). It will be pre-selected. | 2116 | // Input: (optional) current timezone (can be 'UTC/UTC'). It will be pre-selected. |
2103 | // Output: array(html,js) | 2117 | // Output: array(html,js) |
2104 | // Example: list($htmlform,$js) = templateTZform('Europe/Paris'); // Europe/Paris pre-selected. | 2118 | // Example: list($htmlform,$js) = templateTZform('Europe/Paris'); // Europe/Paris pre-selected. |
2105 | // Returns array('','') if server does not support timezones list. (eg. php 5.1 on free.fr) | 2119 | // Returns array('','') if server does not support timezones list. (e.g. PHP 5.1 on free.fr) |
2106 | function templateTZform($ptz=false) | 2120 | function templateTZform($ptz=false) |
2107 | { | 2121 | { |
2108 | if (function_exists('timezone_identifiers_list')) // because of old php version (5.1) which can be found on free.fr | 2122 | if (function_exists('timezone_identifiers_list')) // because of old PHP version (5.1) which can be found on free.fr |
2109 | { | 2123 | { |
2110 | // Try to split the provided timezone. | 2124 | // Try to split the provided timezone. |
2111 | if ($ptz==false) { $l=timezone_identifiers_list(); $ptz=$l[0]; } | 2125 | if ($ptz==false) { $l=timezone_identifiers_list(); $ptz=$l[0]; } |
@@ -2114,7 +2128,7 @@ function templateTZform($ptz=false) | |||
2114 | // Display config form: | 2128 | // Display config form: |
2115 | $timezone_form = ''; | 2129 | $timezone_form = ''; |
2116 | $timezone_js = ''; | 2130 | $timezone_js = ''; |
2117 | // The list is in the forme "Europe/Paris", "America/Argentina/Buenos_Aires"... | 2131 | // The list is in the form "Europe/Paris", "America/Argentina/Buenos_Aires"... |
2118 | // We split the list in continents/cities. | 2132 | // We split the list in continents/cities. |
2119 | $continents = array(); | 2133 | $continents = array(); |
2120 | $cities = array(); | 2134 | $cities = array(); |
@@ -2152,9 +2166,9 @@ function templateTZform($ptz=false) | |||
2152 | function isTZvalid($continent,$city) | 2166 | function isTZvalid($continent,$city) |
2153 | { | 2167 | { |
2154 | $tz = $continent.'/'.$city; | 2168 | $tz = $continent.'/'.$city; |
2155 | if (function_exists('timezone_identifiers_list')) // because of old php version (5.1) which can be found on free.fr | 2169 | if (function_exists('timezone_identifiers_list')) // because of old PHP version (5.1) which can be found on free.fr |
2156 | { | 2170 | { |
2157 | if (in_array($tz, timezone_identifiers_list())) // it's a valid timezone ? | 2171 | if (in_array($tz, timezone_identifiers_list())) // it's a valid timezone? |
2158 | return true; | 2172 | return true; |
2159 | } | 2173 | } |
2160 | return false; | 2174 | return false; |
@@ -2197,7 +2211,7 @@ if (!function_exists('json_encode')) { | |||
2197 | } | 2211 | } |
2198 | 2212 | ||
2199 | // Webservices (for use with jQuery/jQueryUI) | 2213 | // Webservices (for use with jQuery/jQueryUI) |
2200 | // eg. index.php?ws=tags&term=minecr | 2214 | // e.g. index.php?ws=tags&term=minecr |
2201 | function processWS() | 2215 | function processWS() |
2202 | { | 2216 | { |
2203 | if (empty($_GET['ws']) || empty($_GET['term'])) return; | 2217 | if (empty($_GET['ws']) || empty($_GET['term'])) return; |
@@ -2205,7 +2219,7 @@ function processWS() | |||
2205 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). | 2219 | $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). |
2206 | header('Content-Type: application/json; charset=utf-8'); | 2220 | header('Content-Type: application/json; charset=utf-8'); |
2207 | 2221 | ||
2208 | // Search in tags (case insentitive, cumulative search) | 2222 | // Search in tags (case insensitive, cumulative search) |
2209 | if ($_GET['ws']=='tags') | 2223 | if ($_GET['ws']=='tags') |
2210 | { | 2224 | { |
2211 | $tags=explode(' ',str_replace(',',' ',$term)); $last = array_pop($tags); // Get the last term ("a b c d" ==> "a b c", "d") | 2225 | $tags=explode(' ',str_replace(',',' ',$term)); $last = array_pop($tags); // Get the last term ("a b c d" ==> "a b c", "d") |
@@ -2221,7 +2235,7 @@ function processWS() | |||
2221 | exit; | 2235 | exit; |
2222 | } | 2236 | } |
2223 | 2237 | ||
2224 | // Search a single tag (case sentitive, single tag search) | 2238 | // Search a single tag (case sensitive, single tag search) |
2225 | if ($_GET['ws']=='singletag') | 2239 | if ($_GET['ws']=='singletag') |
2226 | { | 2240 | { |
2227 | /* To speed up things, we store list of tags in session */ | 2241 | /* To speed up things, we store list of tags in session */ |
@@ -2237,13 +2251,14 @@ function processWS() | |||
2237 | 2251 | ||
2238 | // Re-write configuration file according to globals. | 2252 | // Re-write configuration file according to globals. |
2239 | // Requires some $GLOBALS to be set (login,hash,salt,title). | 2253 | // Requires some $GLOBALS to be set (login,hash,salt,title). |
2240 | // If the config file cannot be saved, an error message is dislayed and the user is redirected to "Tools" menu. | 2254 | // If the config file cannot be saved, an error message is displayed and the user is redirected to "Tools" menu. |
2241 | // (otherwise, the function simply returns.) | 2255 | // (otherwise, the function simply returns.) |
2242 | function writeConfig() | 2256 | function writeConfig() |
2243 | { | 2257 | { |
2244 | if (is_file($GLOBALS['config']['CONFIG_FILE']) && !isLoggedIn()) die('You are not authorized to alter config.'); // Only logged in user can alter config. | 2258 | if (is_file($GLOBALS['config']['CONFIG_FILE']) && !isLoggedIn()) die('You are not authorized to alter config.'); // Only logged in user can alter config. |
2245 | $config='<?php $GLOBALS[\'login\']='.var_export($GLOBALS['login'],true).'; $GLOBALS[\'hash\']='.var_export($GLOBALS['hash'],true).'; $GLOBALS[\'salt\']='.var_export($GLOBALS['salt'],true).'; '; | 2259 | $config='<?php $GLOBALS[\'login\']='.var_export($GLOBALS['login'],true).'; $GLOBALS[\'hash\']='.var_export($GLOBALS['hash'],true).'; $GLOBALS[\'salt\']='.var_export($GLOBALS['salt'],true).'; '; |
2246 | $config .='$GLOBALS[\'timezone\']='.var_export($GLOBALS['timezone'],true).'; date_default_timezone_set('.var_export($GLOBALS['timezone'],true).'); $GLOBALS[\'title\']='.var_export($GLOBALS['title'],true).';'; | 2260 | $config .='$GLOBALS[\'timezone\']='.var_export($GLOBALS['timezone'],true).'; date_default_timezone_set('.var_export($GLOBALS['timezone'],true).'); $GLOBALS[\'title\']='.var_export($GLOBALS['title'],true).';'; |
2261 | $config .= '$GLOBALS[\'titleLink\']='.var_export($GLOBALS['titleLink'],true).'; '; | ||
2247 | $config .= '$GLOBALS[\'redirector\']='.var_export($GLOBALS['redirector'],true).'; '; | 2262 | $config .= '$GLOBALS[\'redirector\']='.var_export($GLOBALS['redirector'],true).'; '; |
2248 | $config .= '$GLOBALS[\'disablesessionprotection\']='.var_export($GLOBALS['disablesessionprotection'],true).'; '; | 2263 | $config .= '$GLOBALS[\'disablesessionprotection\']='.var_export($GLOBALS['disablesessionprotection'],true).'; '; |
2249 | $config .= '$GLOBALS[\'disablejquery\']='.var_export($GLOBALS['disablejquery'],true).'; '; | 2264 | $config .= '$GLOBALS[\'disablejquery\']='.var_export($GLOBALS['disablejquery'],true).'; '; |
@@ -2256,12 +2271,12 @@ function writeConfig() | |||
2256 | } | 2271 | } |
2257 | } | 2272 | } |
2258 | 2273 | ||
2259 | /* Because some f*cking services like Flickr require an extra HTTP request to get the thumbnail URL, | 2274 | /* Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL, |
2260 | I have deported the thumbnail URL code generation here, otherwise this would slow down page generation. | 2275 | I have deported the thumbnail URL code generation here, otherwise this would slow down page generation. |
2261 | The following function takes the URL a link (eg. a flickr page) and return the proper thumbnail. | 2276 | The following function takes the URL a link (e.g. a flickr page) and return the proper thumbnail. |
2262 | This function is called by passing the url: | 2277 | This function is called by passing the URL: |
2263 | http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL] | 2278 | http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL] |
2264 | [URL] is the URL of the link (eg. a flickr page) | 2279 | [URL] is the URL of the link (e.g. a flickr page) |
2265 | [HMAC] is the signature for the [URL] (so that these URL cannot be forged). | 2280 | [HMAC] is the signature for the [URL] (so that these URL cannot be forged). |
2266 | The function below will fetch the image from the webservice and store it in the cache. | 2281 | The function below will fetch the image from the webservice and store it in the cache. |
2267 | */ | 2282 | */ |
@@ -2269,7 +2284,7 @@ function genThumbnail() | |||
2269 | { | 2284 | { |
2270 | // Make sure the parameters in the URL were generated by us. | 2285 | // Make sure the parameters in the URL were generated by us. |
2271 | $sign = hash_hmac('sha256', $_GET['url'], $GLOBALS['salt']); | 2286 | $sign = hash_hmac('sha256', $_GET['url'], $GLOBALS['salt']); |
2272 | if ($sign!=$_GET['hmac']) die('Naughty boy !'); | 2287 | if ($sign!=$_GET['hmac']) die('Naughty boy!'); |
2273 | 2288 | ||
2274 | // Let's see if we don't already have the image for this URL in the cache. | 2289 | // Let's see if we don't already have the image for this URL in the cache. |
2275 | $thumbname=hash('sha1',$_GET['url']).'.jpg'; | 2290 | $thumbname=hash('sha1',$_GET['url']).'.jpg'; |
@@ -2294,22 +2309,22 @@ function genThumbnail() | |||
2294 | 2309 | ||
2295 | if ($domain=='flickr.com' || endsWith($domain,'.flickr.com')) | 2310 | if ($domain=='flickr.com' || endsWith($domain,'.flickr.com')) |
2296 | { | 2311 | { |
2297 | // Crude replacement to handle new Flickr domain policy (They prefer www. now) | 2312 | // Crude replacement to handle new flickr domain policy (They prefer www. now) |
2298 | $url = str_replace('http://flickr.com/','http://www.flickr.com/',$url); | 2313 | $url = str_replace('http://flickr.com/','http://www.flickr.com/',$url); |
2299 | 2314 | ||
2300 | // Is this a link to an image, or to a flickr page ? | 2315 | // Is this a link to an image, or to a flickr page ? |
2301 | $imageurl=''; | 2316 | $imageurl=''; |
2302 | if (endswith(parse_url($url,PHP_URL_PATH),'.jpg')) | 2317 | if (endswith(parse_url($url,PHP_URL_PATH),'.jpg')) |
2303 | { // This is a direct link to an image. eg. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg | 2318 | { // This is a direct link to an image. e.g. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg |
2304 | preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches); | 2319 | preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches); |
2305 | if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg'; | 2320 | if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg'; |
2306 | } | 2321 | } |
2307 | else // this is a flickr page (html) | 2322 | else // This is a flickr page (html) |
2308 | { | 2323 | { |
2309 | list($httpstatus,$headers,$data) = getHTTP($url,20); // Get the flickr html page. | 2324 | list($httpstatus,$headers,$data) = getHTTP($url,20); // Get the flickr html page. |
2310 | if (strpos($httpstatus,'200 OK')!==false) | 2325 | if (strpos($httpstatus,'200 OK')!==false) |
2311 | { | 2326 | { |
2312 | // Flickr now nicely provides the URL of the thumbnail in each flickr page. | 2327 | // flickr now nicely provides the URL of the thumbnail in each flickr page. |
2313 | preg_match('!<link rel=\"image_src\" href=\"(.+?)\"!',$data,$matches); | 2328 | preg_match('!<link rel=\"image_src\" href=\"(.+?)\"!',$data,$matches); |
2314 | if (!empty($matches[1])) $imageurl=$matches[1]; | 2329 | if (!empty($matches[1])) $imageurl=$matches[1]; |
2315 | 2330 | ||
@@ -2340,9 +2355,9 @@ function genThumbnail() | |||
2340 | elseif ($domain=='vimeo.com' ) | 2355 | elseif ($domain=='vimeo.com' ) |
2341 | { | 2356 | { |
2342 | // This is more complex: we have to perform a HTTP request, then parse the result. | 2357 | // This is more complex: we have to perform a HTTP request, then parse the result. |
2343 | // Maybe we should deport this to javascript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098 | 2358 | // Maybe we should deport this to JavaScript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098 |
2344 | $vid = substr(parse_url($url,PHP_URL_PATH),1); | 2359 | $vid = substr(parse_url($url,PHP_URL_PATH),1); |
2345 | list($httpstatus,$headers,$data) = getHTTP('http://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',5); | 2360 | list($httpstatus,$headers,$data) = getHTTP('https://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',5); |
2346 | if (strpos($httpstatus,'200 OK')!==false) | 2361 | if (strpos($httpstatus,'200 OK')!==false) |
2347 | { | 2362 | { |
2348 | $t = unserialize($data); | 2363 | $t = unserialize($data); |
@@ -2476,7 +2491,7 @@ function resizeImage($filepath) | |||
2476 | } | 2491 | } |
2477 | 2492 | ||
2478 | // Invalidate caches when the database is changed or the user logs out. | 2493 | // Invalidate caches when the database is changed or the user logs out. |
2479 | // (eg. tags cache). | 2494 | // (e.g. tags cache). |
2480 | function invalidateCaches() | 2495 | function invalidateCaches() |
2481 | { | 2496 | { |
2482 | unset($_SESSION['tags']); // Purge cache attached to session. | 2497 | unset($_SESSION['tags']); // Purge cache attached to session. |
diff --git a/pagecache/.htaccess b/pagecache/.htaccess new file mode 100644 index 00000000..b584d98c --- /dev/null +++ b/pagecache/.htaccess | |||
@@ -0,0 +1,2 @@ | |||
1 | Allow from none | ||
2 | Deny from all | ||
diff --git a/shaarli_version.txt b/shaarli_version.txt new file mode 100644 index 00000000..54049091 --- /dev/null +++ b/shaarli_version.txt | |||
@@ -0,0 +1 @@ | |||
0.0.42 beta | |||
diff --git a/tmp/.htaccess b/tmp/.htaccess new file mode 100644 index 00000000..b584d98c --- /dev/null +++ b/tmp/.htaccess | |||
@@ -0,0 +1,2 @@ | |||
1 | Allow from none | ||
2 | Deny from all | ||
diff --git a/tpl/configure.html b/tpl/configure.html index 62296cb2..645107ae 100644 --- a/tpl/configure.html +++ b/tpl/configure.html | |||
@@ -11,6 +11,7 @@ | |||
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><td><b>Page title:</b></td><td><input type="text" name="title" id="title" size="50" value="{$title}"></td></tr> |
13 | 13 | ||
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> | ||
14 | <tr><td valign="top"><b>Timezone:</b></td><td valign="top">{$timezone_form}</td></tr> | 15 | <tr><td valign="top"><b>Timezone:</b></td><td valign="top">{$timezone_form}</td></tr> |
15 | 16 | ||
16 | <tr><td valign="top"><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> | 17 | <tr><td valign="top"><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> |
@@ -18,7 +19,7 @@ | |||
18 | <tr><td valign="top"><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> | 19 | <tr><td valign="top"><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> |
19 | 20 | ||
20 | <tr><td valign="top"><b>Features:</b></td><td> | 21 | <tr><td valign="top"><b>Features:</b></td><td> |
21 | <input type="checkbox" name="disablejquery" id="disablejquery" {if="!empty($GLOBALS['disablejquery'])"}checked{/if}><label for="disablejquery"> Disable jQuery and all heavy javascript (for example: Autocomplete in tags. Useful for slow computers.)</label> | 22 | <input type="checkbox" name="disablejquery" id="disablejquery" {if="!empty($GLOBALS['disablejquery'])"}checked{/if}><label for="disablejquery"> Disable jQuery and all heavy JavaScript (for example: Autocomplete in tags. Useful for slow computers.)</label> |
22 | </td></tr> | 23 | </td></tr> |
23 | <tr><td valign="top"><b>New link:</b></td><td> | 24 | <tr><td valign="top"><b>New link:</b></td><td> |
24 | <input type="checkbox" name="privateLinkByDefault" id="privateLinkByDefault" {if="!empty($GLOBALS['privateLinkByDefault'])"}checked{/if}/><label for="privateLinkByDefault"> All new link are private by default</label></td> | 25 | <input type="checkbox" name="privateLinkByDefault" id="privateLinkByDefault" {if="!empty($GLOBALS['privateLinkByDefault'])"}checked{/if}/><label for="privateLinkByDefault"> All new link are private by default</label></td> |
diff --git a/tpl/import.html b/tpl/import.html index 9e581fc9..259e56ee 100644 --- a/tpl/import.html +++ b/tpl/import.html | |||
@@ -5,7 +5,7 @@ | |||
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|htmlspecialchars} bytes). | 8 | Import Netscape HTML bookmarks (as exported from Firefox/Chrome/Opera/Delicious/Diigo...) (Max: {$maxfilesize|htmlspecialchars} bytes). |
9 | <form method="POST" action="?do=upload" enctype="multipart/form-data" name="uploadform" id="uploadform"> | 9 | <form method="POST" action="?do=upload" enctype="multipart/form-data" name="uploadform" id="uploadform"> |
10 | <input type="hidden" name="token" value="{$token}"> | 10 | <input type="hidden" name="token" value="{$token}"> |
11 | <input type="file" name="filetoupload" size="80"> | 11 | <input type="file" name="filetoupload" size="80"> |
diff --git a/tpl/linklist.html b/tpl/linklist.html index 5a742737..acb4bab0 100644 --- a/tpl/linklist.html +++ b/tpl/linklist.html | |||
@@ -48,6 +48,9 @@ | |||
48 | {else} | 48 | {else} |
49 | <span class="linkdate" title="Short link here"><a href="?{$value.linkdate|smallHash}">permalink</a> - </span> | 49 | <span class="linkdate" title="Short link here"><a href="?{$value.linkdate|smallHash}">permalink</a> - </span> |
50 | {/if} | 50 | {/if} |
51 | {if="$GLOBALS['config']['ARCHIVE_ORG']"} | ||
52 | <span class="linkarchive"><a href="https://web.archive.org/web/{$value.url|htmlspecialchars}">archive</a> - </span> | ||
53 | {/if} | ||
51 | <div style="position:relative;display:inline;"><a href="http://qrfree.kaywa.com/?l=1&s=8&d={$scripturl|urlencode}%3F{$value.linkdate|smallHash}" | 54 | <div style="position:relative;display:inline;"><a href="http://qrfree.kaywa.com/?l=1&s=8&d={$scripturl|urlencode}%3F{$value.linkdate|smallHash}" |
52 | onclick="showQrCode(this); return false;" class="qrcode" data-permalink="{$scripturl}?{$value.linkdate|smallHash}"><img src="images/qrcode.png#" width="13" height="13" title="QR-Code"></a></div> - | 55 | onclick="showQrCode(this); return false;" class="qrcode" data-permalink="{$scripturl}?{$value.linkdate|smallHash}"><img src="images/qrcode.png#" width="13" height="13" title="QR-Code"></a></div> - |
53 | <span class="linkurl" title="Short link">{$value.url|htmlspecialchars}</span><br> | 56 | <span class="linkurl" title="Short link">{$value.url|htmlspecialchars}</span><br> |
diff --git a/tpl/page.header.html b/tpl/page.header.html index 125b365b..654a551d 100644 --- a/tpl/page.header.html +++ b/tpl/page.header.html | |||
@@ -2,7 +2,7 @@ | |||
2 | <div id="logo" title="Share your links !" onclick="document.location='?';"></div> | 2 | <div id="logo" title="Share your links !" onclick="document.location='?';"></div> |
3 | <div style="float:right; font-style:italic; color:#bbb; text-align:right; padding:0 5 0 0;" class="nomobile">Shaare your links...<br> | 3 | <div style="float:right; font-style:italic; color:#bbb; text-align:right; padding:0 5 0 0;" class="nomobile">Shaare your links...<br> |
4 | {if="!empty($linkcount)"}{$linkcount} links{/if}</div> | 4 | {if="!empty($linkcount)"}{$linkcount} links{/if}</div> |
5 | <span id="shaarli_title"><a href="?">{$shaarlititle|htmlspecialchars}</a></span> | 5 | <span id="shaarli_title"><a href="{$titleLink}">{$shaarlititle|htmlspecialchars}</a></span> |
6 | 6 | ||
7 | {if="!empty($_GET['source']) && $_GET['source']=='bookmarklet'"} | 7 | {if="!empty($_GET['source']) && $_GET['source']=='bookmarklet'"} |
8 | {ignore} When called as a popup from bookmarklet, do not display menu. {/ignore} | 8 | {ignore} When called as a popup from bookmarklet, do not display menu. {/ignore} |
@@ -16,7 +16,9 @@ | |||
16 | <a href="?do=login">Login</a> | 16 | <a href="?do=login">Login</a> |
17 | {/if} | 17 | {/if} |
18 | <a href="{$feedurl}?do=rss{$searchcrits}" class="nomobile">RSS Feed</a> | 18 | <a href="{$feedurl}?do=rss{$searchcrits}" class="nomobile">RSS Feed</a> |
19 | <a href="{$feedurl}?do=atom{$searchcrits}" style="padding-left:10px;" class="nomobile">ATOM Feed</a> | 19 | {if="$GLOBALS['config']['SHOW_ATOM']"} |
20 | <a href="{$feedurl}?do=atom{$searchcrits}" style="padding-left:10px;" class="nomobile">ATOM Feed</a> | ||
21 | {/if} | ||
20 | <a href="?do=tagcloud">Tag cloud</a> | 22 | <a href="?do=tagcloud">Tag cloud</a> |
21 | <a href="?do=picwall{$searchcrits}">Picture wall</a> | 23 | <a href="?do=picwall{$searchcrits}">Picture wall</a> |
22 | <a href="?do=daily">Daily</a> | 24 | <a href="?do=daily">Daily</a> |
diff --git a/tpl/tagcloud.html b/tpl/tagcloud.html index 6918c7be..0dd2c0d3 100644 --- a/tpl/tagcloud.html +++ b/tpl/tagcloud.html | |||
@@ -6,7 +6,7 @@ | |||
6 | <center> | 6 | <center> |
7 | <div id="cloudtag"> | 7 | <div id="cloudtag"> |
8 | {loop="tags"} | 8 | {loop="tags"} |
9 | <span style="color:#99f; font-size:9pt; padding-left:5px; padding-right:2px;">{$value.count}</span><a href="?searchtags={$key|htmlspecialchars}" style="font-size:{$value.size}pt; font-weight:bold; color:black; text-decoration:none">{$key|htmlspecialchars}</a> | 9 | <span style="color:#99f; font-size:9pt; padding-left:5px; padding-right:2px;">{$value.count}</span><a href="?searchtags={$key|urlencode}" style="font-size:{$value.size}pt; font-weight:bold; color:black; text-decoration:none">{$key|htmlspecialchars}</a> |
10 | {/loop} | 10 | {/loop} |
11 | </div> | 11 | </div> |
12 | </center> | 12 | </center> |
diff --git a/tpl/tools.html b/tpl/tools.html index 48ecc97e..ba1c1e8e 100644 --- a/tpl/tools.html +++ b/tpl/tools.html | |||
@@ -10,7 +10,7 @@ | |||
10 | <a href="?do=changetag"><b>Rename/delete tags</b> <span>: Rename or delete a tag in all links</span></a><br><br> | 10 | <a href="?do=changetag"><b>Rename/delete tags</b> <span>: Rename or delete a tag in all links</span></a><br><br> |
11 | <a href="?do=import"><b>Import</b> <span>: Import Netscape html bookmarks (as exported from Firefox, Chrome, Opera, delicious...)</span></a> <br><br> | 11 | <a href="?do=import"><b>Import</b> <span>: Import Netscape html bookmarks (as exported from Firefox, Chrome, Opera, delicious...)</span></a> <br><br> |
12 | <a href="?do=export"><b>Export</b> <span>: Export Netscape html bookmarks (which can be imported in Firefox, Chrome, Opera, delicious...)</span></a><br><br> | 12 | <a href="?do=export"><b>Export</b> <span>: Export Netscape html bookmarks (which can be imported in Firefox, Chrome, Opera, delicious...)</span></a><br><br> |
13 | <a class="smallbutton" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="javascript:javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;window.open('{$pageabsaddr}?post='%20+%20encodeURIComponent(url)+'&title='%20+%20encodeURIComponent(title)+'&source=bookmarklet','_blank','menubar=no,height=390,width=600,toolbar=no,scrollbars=no,status=no,dialog=1');})();"><b>Shaare link</b></a> <a href="#" style="clear:none;"><span>⇐ Drag this link to your bookmarks toolbar (or right-click it and choose Bookmark This Link....).<br> Then click "Shaare link" button in any page you want to share.</span></a><br><br> | 13 | <a class="smallbutton" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="javascript:javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;window.open('{$pageabsaddr}?post='%20+%20encodeURIComponent(url)+'&title='%20+%20encodeURIComponent(title)+'&description='%20+%20encodeURIComponent(document.getSelection())+'&source=bookmarklet','_blank','menubar=no,height=390,width=600,toolbar=no,scrollbars=no,status=no,dialog=1');})();"><b>Shaare link</b></a> <a href="#" style="clear:none;"><span>⇐ Drag this link to your bookmarks toolbar (or right-click it and choose Bookmark This Link....).<br> Then click "Shaare link" button in any page you want to share.</span></a><br><br> |
14 | <div class="clear"></div> | 14 | <div class="clear"></div> |
15 | </div> | 15 | </div> |
16 | </div> | 16 | </div> |