aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--application/ApplicationUtils.php20
-rw-r--r--application/FileUtils.php26
-rw-r--r--application/LinkUtils.php2
-rw-r--r--application/PageBuilder.php9
-rw-r--r--application/ThemeUtils.php1
-rw-r--r--application/Updater.php4
-rw-r--r--application/config/ConfigManager.php1
-rw-r--r--doc/md/Shaarli-configuration.md1
-rw-r--r--doc/md/docker/docker-101.md78
-rw-r--r--doc/md/index.md11
-rw-r--r--index.php4
-rw-r--r--tests/LinkUtilsTest.php10
-rw-r--r--tpl/default/includes.html14
-rw-r--r--tpl/default/js/shaarli.js6
-rw-r--r--tpl/default/page.footer.html6
15 files changed, 164 insertions, 29 deletions
diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php
index 85dcbeeb..5643f4a0 100644
--- a/application/ApplicationUtils.php
+++ b/application/ApplicationUtils.php
@@ -168,14 +168,15 @@ class ApplicationUtils
168 public static function checkResourcePermissions($conf) 168 public static function checkResourcePermissions($conf)
169 { 169 {
170 $errors = array(); 170 $errors = array();
171 $rainTplDir = rtrim($conf->get('resource.raintpl_tpl'), '/');
171 172
172 // Check script and template directories are readable 173 // Check script and template directories are readable
173 foreach (array( 174 foreach (array(
174 'application', 175 'application',
175 'inc', 176 'inc',
176 'plugins', 177 'plugins',
177 $conf->get('resource.raintpl_tpl'), 178 $rainTplDir,
178 $conf->get('resource.raintpl_tpl').'/'.$conf->get('resource.theme'), 179 $rainTplDir.'/'.$conf->get('resource.theme'),
179 ) as $path) { 180 ) as $path) {
180 if (! is_readable(realpath($path))) { 181 if (! is_readable(realpath($path))) {
181 $errors[] = '"'.$path.'" directory is not readable'; 182 $errors[] = '"'.$path.'" directory is not readable';
@@ -220,4 +221,19 @@ class ApplicationUtils
220 221
221 return $errors; 222 return $errors;
222 } 223 }
224
225 /**
226 * Returns a salted hash representing the current Shaarli version.
227 *
228 * Useful for assets browser cache.
229 *
230 * @param string $currentVersion of Shaarli
231 * @param string $salt User personal salt, also used for the authentication
232 *
233 * @return string version hash
234 */
235 public static function getVersionHash($currentVersion, $salt)
236 {
237 return hash_hmac('sha256', $currentVersion, $salt);
238 }
223} 239}
diff --git a/application/FileUtils.php b/application/FileUtils.php
index a167f642..918cb83b 100644
--- a/application/FileUtils.php
+++ b/application/FileUtils.php
@@ -50,7 +50,8 @@ class FileUtils
50 50
51 /** 51 /**
52 * Read data from a file containing Shaarli database format content. 52 * Read data from a file containing Shaarli database format content.
53 * If the file isn't readable or doesn't exists, default data will be returned. 53 *
54 * If the file isn't readable or doesn't exist, default data will be returned.
54 * 55 *
55 * @param string $file File path. 56 * @param string $file File path.
56 * @param mixed $default The default value to return if the file isn't readable. 57 * @param mixed $default The default value to return if the file isn't readable.
@@ -61,16 +62,21 @@ class FileUtils
61 { 62 {
62 // Note that gzinflate is faster than gzuncompress. 63 // Note that gzinflate is faster than gzuncompress.
63 // See: http://www.php.net/manual/en/function.gzdeflate.php#96439 64 // See: http://www.php.net/manual/en/function.gzdeflate.php#96439
64 if (is_readable($file)) { 65 if (! is_readable($file)) {
65 return unserialize( 66 return $default;
66 gzinflate( 67 }
67 base64_decode( 68
68 substr(file_get_contents($file), strlen(self::$phpPrefix), -strlen(self::$phpSuffix)) 69 $data = file_get_contents($file);
69 ) 70 if ($data == '') {
70 ) 71 return $default;
71 );
72 } 72 }
73 73
74 return $default; 74 return unserialize(
75 gzinflate(
76 base64_decode(
77 substr($data, strlen(self::$phpPrefix), -strlen(self::$phpSuffix))
78 )
79 )
80 );
75 } 81 }
76} 82}
diff --git a/application/LinkUtils.php b/application/LinkUtils.php
index 976474de..267e62cd 100644
--- a/application/LinkUtils.php
+++ b/application/LinkUtils.php
@@ -109,7 +109,7 @@ function count_private($links)
109 */ 109 */
110function text2clickable($text, $redirector = '') 110function text2clickable($text, $redirector = '')
111{ 111{
112 $regex = '!(((?:https?|ftp|file)://|apt:|magnet:)\S+[[:alnum:]]/?)!si'; 112 $regex = '!(((?:https?|ftp|file)://|apt:|magnet:)\S+[a-z0-9\(\)]/?)!si';
113 113
114 if (empty($redirector)) { 114 if (empty($redirector)) {
115 return preg_replace($regex, '<a href="$1">$1</a>', $text); 115 return preg_replace($regex, '<a href="$1">$1</a>', $text);
diff --git a/application/PageBuilder.php b/application/PageBuilder.php
index 7a42400d..291860ad 100644
--- a/application/PageBuilder.php
+++ b/application/PageBuilder.php
@@ -49,7 +49,7 @@ class PageBuilder
49 49
50 try { 50 try {
51 $version = ApplicationUtils::checkUpdate( 51 $version = ApplicationUtils::checkUpdate(
52 shaarli_version, 52 SHAARLI_VERSION,
53 $this->conf->get('resource.update_check'), 53 $this->conf->get('resource.update_check'),
54 $this->conf->get('updates.check_updates_interval'), 54 $this->conf->get('updates.check_updates_interval'),
55 $this->conf->get('updates.check_updates'), 55 $this->conf->get('updates.check_updates'),
@@ -75,7 +75,11 @@ class PageBuilder
75 } 75 }
76 $this->tpl->assign('searchcrits', $searchcrits); 76 $this->tpl->assign('searchcrits', $searchcrits);
77 $this->tpl->assign('source', index_url($_SERVER)); 77 $this->tpl->assign('source', index_url($_SERVER));
78 $this->tpl->assign('version', shaarli_version); 78 $this->tpl->assign('version', SHAARLI_VERSION);
79 $this->tpl->assign(
80 'version_hash',
81 ApplicationUtils::getVersionHash(SHAARLI_VERSION, $this->conf->get('credentials.salt'))
82 );
79 $this->tpl->assign('scripturl', index_url($_SERVER)); 83 $this->tpl->assign('scripturl', index_url($_SERVER));
80 $this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links? 84 $this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links?
81 $this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly'])); 85 $this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly']));
@@ -89,6 +93,7 @@ class PageBuilder
89 $this->tpl->assign('feed_type', $this->conf->get('feed.show_atom', true) !== false ? 'atom' : 'rss'); 93 $this->tpl->assign('feed_type', $this->conf->get('feed.show_atom', true) !== false ? 'atom' : 'rss');
90 $this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false)); 94 $this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false));
91 $this->tpl->assign('token', getToken($this->conf)); 95 $this->tpl->assign('token', getToken($this->conf));
96
92 if ($this->linkDB !== null) { 97 if ($this->linkDB !== null) {
93 $this->tpl->assign('tags', $this->linkDB->linksCountPerTag()); 98 $this->tpl->assign('tags', $this->linkDB->linksCountPerTag());
94 } 99 }
diff --git a/application/ThemeUtils.php b/application/ThemeUtils.php
index 2718ed13..16f2f6a2 100644
--- a/application/ThemeUtils.php
+++ b/application/ThemeUtils.php
@@ -22,6 +22,7 @@ class ThemeUtils
22 */ 22 */
23 public static function getThemes($tplDir) 23 public static function getThemes($tplDir)
24 { 24 {
25 $tplDir = rtrim($tplDir, '/');
25 $allTheme = glob($tplDir.'/*', GLOB_ONLYDIR); 26 $allTheme = glob($tplDir.'/*', GLOB_ONLYDIR);
26 $themes = []; 27 $themes = [];
27 foreach ($allTheme as $value) { 28 foreach ($allTheme as $value) {
diff --git a/application/Updater.php b/application/Updater.php
index 40a15906..72b2def0 100644
--- a/application/Updater.php
+++ b/application/Updater.php
@@ -398,7 +398,7 @@ class Updater
398 */ 398 */
399 public function updateMethodCheckUpdateRemoteBranch() 399 public function updateMethodCheckUpdateRemoteBranch()
400 { 400 {
401 if (shaarli_version === 'dev' || $this->conf->get('updates.check_updates_branch') === 'latest') { 401 if (SHAARLI_VERSION === 'dev' || $this->conf->get('updates.check_updates_branch') === 'latest') {
402 return true; 402 return true;
403 } 403 }
404 404
@@ -413,7 +413,7 @@ class Updater
413 $latestMajor = $matches[1]; 413 $latestMajor = $matches[1];
414 414
415 // Get current major version digit 415 // Get current major version digit
416 preg_match('/(\d+)\.\d+$/', shaarli_version, $matches); 416 preg_match('/(\d+)\.\d+$/', SHAARLI_VERSION, $matches);
417 $currentMajor = $matches[1]; 417 $currentMajor = $matches[1];
418 418
419 if ($currentMajor === $latestMajor) { 419 if ($currentMajor === $latestMajor) {
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php
index 32f6ef6d..7ff2fe67 100644
--- a/application/config/ConfigManager.php
+++ b/application/config/ConfigManager.php
@@ -317,6 +317,7 @@ class ConfigManager
317 $this->setEmpty('general.header_link', '?'); 317 $this->setEmpty('general.header_link', '?');
318 $this->setEmpty('general.links_per_page', 20); 318 $this->setEmpty('general.links_per_page', 20);
319 $this->setEmpty('general.enabled_plugins', self::$DEFAULT_PLUGINS); 319 $this->setEmpty('general.enabled_plugins', self::$DEFAULT_PLUGINS);
320 $this->setEmpty('general.default_note_title', 'Note: ');
320 321
321 $this->setEmpty('updates.check_updates', false); 322 $this->setEmpty('updates.check_updates', false);
322 $this->setEmpty('updates.check_updates_branch', 'stable'); 323 $this->setEmpty('updates.check_updates_branch', 'stable');
diff --git a/doc/md/Shaarli-configuration.md b/doc/md/Shaarli-configuration.md
index 37486414..99b25ba7 100644
--- a/doc/md/Shaarli-configuration.md
+++ b/doc/md/Shaarli-configuration.md
@@ -55,6 +55,7 @@ _These settings should not be edited_
55- **links_per_page**: Number of shaares displayed per page. 55- **links_per_page**: Number of shaares displayed per page.
56- **timezone**: See [the list of supported timezones](http://php.net/manual/en/timezones.php). 56- **timezone**: See [the list of supported timezones](http://php.net/manual/en/timezones.php).
57- **enabled_plugins**: List of enabled plugins. 57- **enabled_plugins**: List of enabled plugins.
58- **default_note_title**: Default title of a new note.
58 59
59### Security 60### Security
60 61
diff --git a/doc/md/docker/docker-101.md b/doc/md/docker/docker-101.md
index b02dd149..a9c00b85 100644
--- a/doc/md/docker/docker-101.md
+++ b/doc/md/docker/docker-101.md
@@ -60,3 +60,81 @@ wheezy: Pulling from debian
60Digest: sha256:c584131da2ac1948aa3e66468a4424b6aea2f33acba7cec0b631bdb56254c4fe 60Digest: sha256:c584131da2ac1948aa3e66468a4424b6aea2f33acba7cec0b631bdb56254c4fe
61Status: Downloaded newer image for debian:wheezy 61Status: Downloaded newer image for debian:wheezy
62``` 62```
63
64Docker re-uses layers already downloaded. In other words if you have images based on Alpine or some Ubuntu version for example, those can share disk space.
65
66### Start a container
67A container is an instance created from an image, that can be run and that keeps running until its main process exits. Or until the user stops the container.
68
69The simplest way to start a container from image is ``docker run``. It also pulls the image for you if it is not locally available. For more advanced use, refer to ``docker create``.
70
71Stopped containers are not destroyed, unless you specify ``--rm``. To view all created, running and stopped containers, enter:
72```bash
73$ docker ps -a
74```
75
76Some containers may be designed or configured to be restarted, others are not. Also remember both network ports and volumes of a container are created on start, and not editable later.
77
78### Access a running container
79A running container is accessible using ``docker exec``, or ``docker copy``. You can use ``exec`` to start a root shell in the Shaarli container:
80```bash
81$ docker exec -ti <container-name-or-id> bash
82```
83Note the names and ID's of containers are listed in ``docker ps``. You can even type only one or two letters of the ID, given they are unique.
84
85Access can also be through one or more network ports, or disk volumes. Both are specified on and fixed on ``docker create`` or ``run``.
86
87You can view the console output of the main container process too:
88```bash
89$ docker logs -f <container-name-or-id>
90```
91
92### Docker disk use
93Trying out different images can fill some gigabytes of disk quickly. Besides images, the docker volumes usually take up most disk space.
94
95If you care only about trying out docker and not about what is running or saved, the following commands should help you out quickly if you run low on disk space:
96
97```bash
98$ docker rmi -f $(docker images -aq) # remove or mark all images for disposal
99$ docker volume rm $(docker volume ls -q) # remove all volumes
100```
101
102### Systemd config
103Systemd is the process manager of choice on Debian-based distributions. Once you have a ``docker`` service installed, you can use the following steps to set up Shaarli to run on system start.
104
105```bash
106systemctl enable /etc/systemd/system/docker.shaarli.service
107systemctl start docker.shaarli
108systemctl status docker.*
109journalctl -f # inspect system log if needed
110```
111
112You will need sudo or a root terminal to perform some or all of the steps above. Here are the contents for the service file:
113```
114[Unit]
115Description=Shaarli Bookmark Manager Container
116After=docker.service
117Requires=docker.service
118
119
120[Service]
121Restart=always
122
123# Put any environment you want in an included file, like $host- or $domainname in this example
124EnvironmentFile=/etc/sysconfig/box-environment
125
126# It's just an example..
127ExecStart=/usr/bin/docker run \
128 -p 28010:80 \
129 --name ${hostname}-shaarli \
130 --hostname shaarli.${domainname} \
131 -v /srv/docker-volumes-local/shaarli-data:/var/www/shaarli/data:rw \
132 -v /etc/localtime:/etc/localtime:ro \
133 shaarli/shaarli:latest
134
135ExecStop=/usr/bin/docker rm -f ${hostname}-shaarli
136
137
138[Install]
139WantedBy=multi-user.target
140```
diff --git a/doc/md/index.md b/doc/md/index.md
index 24ada6c7..2b7d0f00 100644
--- a/doc/md/index.md
+++ b/doc/md/index.md
@@ -22,6 +22,17 @@ It runs the latest development version of Shaarli and is updated/reset daily.
22 22
23Login: `demo`; Password: `demo` 23Login: `demo`; Password: `demo`
24 24
25Docker users can start a personal instance from an [autobuild image](https://hub.docker.com/r/shaarli/shaarli/). For example to start a temporary Shaarli at ``localhost:8000``, and keep session data (config, storage):
26```
27MY_SHAARLI_VOLUME=$(cd /path/to/shaarli/data/ && pwd -P)
28docker run -ti --rm \
29 -p 8000:80 \
30 -v $MY_SHAARLI_VOLUME:/var/www/shaarli/data \
31 shaarli/shaarli
32```
33
34A brief guide on getting starting using docker is given in [Docker 101](docker/docker-101).
35To learn more about user data and how to keep it across versions, please see [Upgrade and Migration](Upgrade-and-migration) documentation.
25 36
26## Features 37## Features
27 38
diff --git a/index.php b/index.php
index 8f0179e5..4068a828 100644
--- a/index.php
+++ b/index.php
@@ -88,7 +88,7 @@ try {
88 exit; 88 exit;
89} 89}
90 90
91define('shaarli_version', ApplicationUtils::getVersion(__DIR__ .'/'. ApplicationUtils::$VERSION_FILE)); 91define('SHAARLI_VERSION', ApplicationUtils::getVersion(__DIR__ .'/'. ApplicationUtils::$VERSION_FILE));
92 92
93// Force cookie path (but do not change lifetime) 93// Force cookie path (but do not change lifetime)
94$cookie = session_get_cookie_params(); 94$cookie = session_get_cookie_params();
@@ -1443,7 +1443,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1443 1443
1444 if ($url == '') { 1444 if ($url == '') {
1445 $url = '?' . smallHash($linkdate . $LINKSDB->getNextId()); 1445 $url = '?' . smallHash($linkdate . $LINKSDB->getNextId());
1446 $title = 'Note: '; 1446 $title = $conf->get('general.default_note_title', 'Note: ');
1447 } 1447 }
1448 $url = escape($url); 1448 $url = escape($url);
1449 $title = escape($title); 1449 $title = escape($title);
diff --git a/tests/LinkUtilsTest.php b/tests/LinkUtilsTest.php
index 7c0d4b0b..c77922ec 100644
--- a/tests/LinkUtilsTest.php
+++ b/tests/LinkUtilsTest.php
@@ -103,6 +103,16 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
103 $expectedText = 'stuff <a href="http://hello.there/is=someone#here">http://hello.there/is=someone#here</a> otherstuff'; 103 $expectedText = 'stuff <a href="http://hello.there/is=someone#here">http://hello.there/is=someone#here</a> otherstuff';
104 $processedText = text2clickable($text, ''); 104 $processedText = text2clickable($text, '');
105 $this->assertEquals($expectedText, $processedText); 105 $this->assertEquals($expectedText, $processedText);
106
107 $text = 'stuff http://hello.there/is=someone#here(please) otherstuff';
108 $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)">http://hello.there/is=someone#here(please)</a> otherstuff';
109 $processedText = text2clickable($text, '');
110 $this->assertEquals($expectedText, $processedText);
111
112 $text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff';
113 $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)&no">http://hello.there/is=someone#here(please)&no</a> otherstuff';
114 $processedText = text2clickable($text, '');
115 $this->assertEquals($expectedText, $processedText);
106 } 116 }
107 117
108 /** 118 /**
diff --git a/tpl/default/includes.html b/tpl/default/includes.html
index 0350ef66..80c08333 100644
--- a/tpl/default/includes.html
+++ b/tpl/default/includes.html
@@ -5,16 +5,16 @@
5<link rel="alternate" type="application/atom+xml" href="{$feedurl}?do=atom{$searchcrits}#" title="ATOM Feed" /> 5<link rel="alternate" type="application/atom+xml" href="{$feedurl}?do=atom{$searchcrits}#" title="ATOM Feed" />
6<link rel="alternate" type="application/rss+xml" href="{$feedurl}?do=rss{$searchcrits}#" title="RSS Feed" /> 6<link rel="alternate" type="application/rss+xml" href="{$feedurl}?do=rss{$searchcrits}#" title="RSS Feed" />
7<link href="img/favicon.png" rel="shortcut icon" type="image/png" /> 7<link href="img/favicon.png" rel="shortcut icon" type="image/png" />
8<link type="text/css" rel="stylesheet" href="css/pure.min.css" /> 8<link type="text/css" rel="stylesheet" href="css/pure.min.css?v={$version_hash}" />
9<link type="text/css" rel="stylesheet" href="css/grids-responsive.min.css"> 9<link type="text/css" rel="stylesheet" href="css/grids-responsive.min.css?v={$version_hash}">
10<link type="text/css" rel="stylesheet" href="css/pure-extras.css"> 10<link type="text/css" rel="stylesheet" href="css/pure-extras.css?v={$version_hash}">
11<link type="text/css" rel="stylesheet" href="css/font-awesome.min.css" /> 11<link type="text/css" rel="stylesheet" href="css/font-awesome.min.css?v={$version_hash}" />
12<link type="text/css" rel="stylesheet" href="inc/awesomplete.css#" /> 12<link type="text/css" rel="stylesheet" href="inc/awesomplete.css?v={$version_hash}#" />
13<link type="text/css" rel="stylesheet" href="css/shaarli.css" /> 13<link type="text/css" rel="stylesheet" href="css/shaarli.css?v={$version_hash}" />
14{if="is_file('data/user.css')"} 14{if="is_file('data/user.css')"}
15 <link type="text/css" rel="stylesheet" href="data/user.css#" /> 15 <link type="text/css" rel="stylesheet" href="data/user.css#" />
16{/if} 16{/if}
17{loop="$plugins_includes.css_files"} 17{loop="$plugins_includes.css_files"}
18 <link type="text/css" rel="stylesheet" href="{$value}#"/> 18 <link type="text/css" rel="stylesheet" href="{$value}?v={$version_hash}#"/>
19{/loop} 19{/loop}
20<link rel="search" type="application/opensearchdescription+xml" href="?do=opensearch#" title="Shaarli search - {$shaarlititle}"/> \ No newline at end of file 20<link rel="search" type="application/opensearchdescription+xml" href="?do=opensearch#" title="Shaarli search - {$shaarlititle}"/> \ No newline at end of file
diff --git a/tpl/default/js/shaarli.js b/tpl/default/js/shaarli.js
index 1c66ebbd..55656f80 100644
--- a/tpl/default/js/shaarli.js
+++ b/tpl/default/js/shaarli.js
@@ -275,8 +275,14 @@ window.onload = function () {
275 }; 275 };
276 function init () { 276 function init () {
277 function resize () { 277 function resize () {
278 /* Fix jumpy resizing: https://stackoverflow.com/a/18262927/1484919 */
279 var scrollTop = window.pageYOffset ||
280 (document.documentElement || document.body.parentNode || document.body).scrollTop;
281
278 description.style.height = 'auto'; 282 description.style.height = 'auto';
279 description.style.height = description.scrollHeight+10+'px'; 283 description.style.height = description.scrollHeight+10+'px';
284
285 window.scrollTo(0, scrollTop);
280 } 286 }
281 /* 0-timeout to get the already changed text */ 287 /* 0-timeout to get the already changed text */
282 function delayedResize () { 288 function delayedResize () {
diff --git a/tpl/default/page.footer.html b/tpl/default/page.footer.html
index 94f771a2..54b16e8a 100644
--- a/tpl/default/page.footer.html
+++ b/tpl/default/page.footer.html
@@ -27,6 +27,6 @@
27 <script src="{$value}#"></script> 27 <script src="{$value}#"></script>
28{/loop} 28{/loop}
29 29
30<script src="js/shaarli.js"></script> 30<script src="js/shaarli.js?v={$version_hash}"></script>
31<script src="inc/awesomplete.js#"></script> 31<script src="inc/awesomplete.js?v={$version_hash}#"></script>
32<script src="inc/awesomplete-multiple-tags.js#"></script> 32<script src="inc/awesomplete-multiple-tags.js?v={$version_hash}#"></script>