diff options
-rw-r--r-- | application/ApplicationUtils.php | 20 | ||||
-rw-r--r-- | application/FileUtils.php | 26 | ||||
-rw-r--r-- | application/LinkUtils.php | 2 | ||||
-rw-r--r-- | application/PageBuilder.php | 9 | ||||
-rw-r--r-- | application/ThemeUtils.php | 1 | ||||
-rw-r--r-- | application/Updater.php | 4 | ||||
-rw-r--r-- | application/config/ConfigManager.php | 1 | ||||
-rw-r--r-- | doc/md/Shaarli-configuration.md | 1 | ||||
-rw-r--r-- | doc/md/docker/docker-101.md | 78 | ||||
-rw-r--r-- | doc/md/index.md | 11 | ||||
-rw-r--r-- | index.php | 4 | ||||
-rw-r--r-- | tests/LinkUtilsTest.php | 10 | ||||
-rw-r--r-- | tpl/default/includes.html | 14 | ||||
-rw-r--r-- | tpl/default/js/shaarli.js | 6 | ||||
-rw-r--r-- | tpl/default/page.footer.html | 6 |
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 | */ |
110 | function text2clickable($text, $redirector = '') | 110 | function 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 | |||
60 | Digest: sha256:c584131da2ac1948aa3e66468a4424b6aea2f33acba7cec0b631bdb56254c4fe | 60 | Digest: sha256:c584131da2ac1948aa3e66468a4424b6aea2f33acba7cec0b631bdb56254c4fe |
61 | Status: Downloaded newer image for debian:wheezy | 61 | Status: Downloaded newer image for debian:wheezy |
62 | ``` | 62 | ``` |
63 | |||
64 | Docker 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 | ||
67 | A 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 | |||
69 | The 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 | |||
71 | Stopped 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 | |||
76 | Some 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 | ||
79 | A 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 | ``` | ||
83 | Note 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 | |||
85 | Access can also be through one or more network ports, or disk volumes. Both are specified on and fixed on ``docker create`` or ``run``. | ||
86 | |||
87 | You 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 | ||
93 | Trying out different images can fill some gigabytes of disk quickly. Besides images, the docker volumes usually take up most disk space. | ||
94 | |||
95 | If 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 | ||
103 | Systemd 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 | ||
106 | systemctl enable /etc/systemd/system/docker.shaarli.service | ||
107 | systemctl start docker.shaarli | ||
108 | systemctl status docker.* | ||
109 | journalctl -f # inspect system log if needed | ||
110 | ``` | ||
111 | |||
112 | You 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] | ||
115 | Description=Shaarli Bookmark Manager Container | ||
116 | After=docker.service | ||
117 | Requires=docker.service | ||
118 | |||
119 | |||
120 | [Service] | ||
121 | Restart=always | ||
122 | |||
123 | # Put any environment you want in an included file, like $host- or $domainname in this example | ||
124 | EnvironmentFile=/etc/sysconfig/box-environment | ||
125 | |||
126 | # It's just an example.. | ||
127 | ExecStart=/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 | |||
135 | ExecStop=/usr/bin/docker rm -f ${hostname}-shaarli | ||
136 | |||
137 | |||
138 | [Install] | ||
139 | WantedBy=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 | ||
23 | Login: `demo`; Password: `demo` | 23 | Login: `demo`; Password: `demo` |
24 | 24 | ||
25 | Docker 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 | ``` | ||
27 | MY_SHAARLI_VOLUME=$(cd /path/to/shaarli/data/ && pwd -P) | ||
28 | docker run -ti --rm \ | ||
29 | -p 8000:80 \ | ||
30 | -v $MY_SHAARLI_VOLUME:/var/www/shaarli/data \ | ||
31 | shaarli/shaarli | ||
32 | ``` | ||
33 | |||
34 | A brief guide on getting starting using docker is given in [Docker 101](docker/docker-101). | ||
35 | To 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 | ||
@@ -88,7 +88,7 @@ try { | |||
88 | exit; | 88 | exit; |
89 | } | 89 | } |
90 | 90 | ||
91 | define('shaarli_version', ApplicationUtils::getVersion(__DIR__ .'/'. ApplicationUtils::$VERSION_FILE)); | 91 | define('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> |