From: VirtualTam Date: Sat, 21 Oct 2017 16:00:08 +0000 (+0200) Subject: Merge pull request #846 from virtualtam/docker/alpine X-Git-Tag: v0.9.4~43 X-Git-Url: https://git.immae.eu/?a=commitdiff_plain;h=72cfe44436f4316112fc4aabfe8940aa7b4adcab;hp=e3a3cc0da85925d08df29a2146b54b4159d5a14b;p=github%2Fshaarli%2FShaarli.git Merge pull request #846 from virtualtam/docker/alpine Docker: switch to Alpine Linux --- diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..4a6589a2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +# EditorConfig: http://EditorConfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +[*.{htaccess,html,xml}] +indent_size = 2 + +[*.php] +max_line_length = 100 + +[Dockerfile] +max_line_length = 80 + +[Makefile] +indent_style = tab diff --git a/.gitattributes b/.gitattributes index dd0e573c..93900602 100644 --- a/.gitattributes +++ b/.gitattributes @@ -24,6 +24,7 @@ Dockerfile text *.min.js binary # Exclude from Git archives +.editorconfig export-ignore .gitattributes export-ignore .github export-ignore .gitignore export-ignore diff --git a/.github/mailmap b/.github/mailmap index 41d91e47..bbdb7908 100644 --- a/.github/mailmap +++ b/.github/mailmap @@ -11,3 +11,5 @@ Timo Van Neerden lehollandaisvolant VirtualTam VirtualTam +Willi Eggeling +Willi Eggeling diff --git a/AUTHORS b/AUTHORS index 2181ec9d..105561c1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,11 +1,13 @@ - 518 ArthurHoaro - 231 VirtualTam - 147 nodiscc + 537 ArthurHoaro + 252 VirtualTam + 148 nodiscc 56 Sébastien Sauvage 15 Florian Eula 13 Emilien Klein 12 Nicolas Danelon + 9 Willi Eggeling 8 Christophe HENRY + 6 B. van Berkum 5 Lucas Cimon 4 Alexandre Alapetite 4 David Sferruzza @@ -37,6 +39,7 @@ 1 Kevin Canévet 1 Knah Tsaeb 1 Lionel Martin + 1 Mark Gerarts 1 Marsup 1 Sbgodin 1 TsT diff --git a/CHANGELOG.md b/CHANGELOG.md index 60262d56..120c5d22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,44 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [v0.9.2](https://github.com/shaarli/Shaarli/releases/tag/v0.9.2) - 2017-10-07 + +**Major security issue fixed. Please update.** + +### Added +- Tag search now supports wildcards `*` +- New setting `privacy.force_login` which can be used with `privacy.hide_public_links` to redirect anonymous users to the login page. +- New setting `general.default_note_title` used to override default `Note:` title prefix for notes. +- Add a version hash for asset loading to prevent browser's cache issue + +### Changed +- The "Remember me" checkbox is unchecked by default +- The default value of the "Remember me" checkbox can be configured under `data/config.json.php` + +### Removed +- Remove obsolete PHP magic quote support + +### Fixed +- Generates a permalink URL if the URL is set to blank +- Replace links to the old GitHub wiki with ReadTheDocs URIs +- Use single quotes in the note bookmarklet +- Daily page if there is no link +- Bulk link deletion with a single link +- HTTPS detection behind a reverse proxy +- Travis tests environment and localization +- Improve template paths robustness (trailing slash) +- Robustness: safer gzinflate/zlib usage +- Description links parsing with parenthesis (without Markdown) +- Templates: + - Sort the tag cloud alphabetically + - Firefox social title + - Improved visited link color + - Fix jumpy textarea with long content in post edit + +### Security + +- Vulnerability introduced in v0.9.1 fixed. + ## [v0.9.1](https://github.com/shaarli/Shaarli/releases/tag/v0.9.1) - 2017-08-23 The documentation has been migrated to ReadTheDocs: diff --git a/README.md b/README.md index 100ff46b..c1050027 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ _It is designed to be personal (single-user), fast and handy._ [![](https://img.shields.io/badge/stable-v0.8.4-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.8.4) [![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli) • -[![](https://img.shields.io/badge/latest-v0.9.1-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.1) +[![](https://img.shields.io/badge/latest-v0.9.2-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.2) [![](https://img.shields.io/travis/shaarli/Shaarli/latest.svg?label=latest)](https://travis-ci.org/shaarli/Shaarli) • [![](https://img.shields.io/badge/master-v0.9.x-blue.svg)](https://github.com/shaarli/Shaarli) diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php index 123cc0b3..5643f4a0 100644 --- a/application/ApplicationUtils.php +++ b/application/ApplicationUtils.php @@ -221,4 +221,19 @@ class ApplicationUtils return $errors; } + + /** + * Returns a salted hash representing the current Shaarli version. + * + * Useful for assets browser cache. + * + * @param string $currentVersion of Shaarli + * @param string $salt User personal salt, also used for the authentication + * + * @return string version hash + */ + public static function getVersionHash($currentVersion, $salt) + { + return hash_hmac('sha256', $currentVersion, $salt); + } } diff --git a/application/History.php b/application/History.php index 116b9264..5e3b1b72 100644 --- a/application/History.php +++ b/application/History.php @@ -16,6 +16,7 @@ * - UPDATED: link updated * - DELETED: link deleted * - SETTINGS: the settings have been updated through the UI. + * - IMPORT: bulk links import * * Note: new events are put at the beginning of the file and history array. */ @@ -41,6 +42,11 @@ class History */ const SETTINGS = 'SETTINGS'; + /** + * @var string Action key: a bulk import has been processed. + */ + const IMPORT = 'IMPORT'; + /** * @var string History file path. */ @@ -121,6 +127,16 @@ class History $this->addEvent(self::SETTINGS); } + /** + * Add Event: bulk import. + * + * Note: we don't store links add/update one by one since it can have a huge impact on performances. + */ + public function importLinks() + { + $this->addEvent(self::IMPORT); + } + /** * Save a new event and write it in the history file. * diff --git a/application/NetscapeBookmarkUtils.php b/application/NetscapeBookmarkUtils.php index 2a10ff22..31796367 100644 --- a/application/NetscapeBookmarkUtils.php +++ b/application/NetscapeBookmarkUtils.php @@ -66,6 +66,7 @@ class NetscapeBookmarkUtils * @param int $importCount how many links were imported * @param int $overwriteCount how many links were overwritten * @param int $skipCount how many links were skipped + * @param int $duration how many seconds did the import take * * @return string Summary of the bookmark import status */ @@ -74,14 +75,16 @@ class NetscapeBookmarkUtils $filesize, $importCount=0, $overwriteCount=0, - $skipCount=0 + $skipCount=0, + $duration=0 ) { $status = 'File '.$filename.' ('.$filesize.' bytes) '; if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) { $status .= 'has an unknown file format. Nothing was imported.'; } else { - $status .= 'was successfully processed: '.$importCount.' links imported, '; + $status .= 'was successfully processed in '. $duration .' seconds: '; + $status .= $importCount.' links imported, '; $status .= $overwriteCount.' links overwritten, '; $status .= $skipCount.' links skipped.'; } @@ -101,6 +104,7 @@ class NetscapeBookmarkUtils */ public static function import($post, $files, $linkDb, $conf, $history) { + $start = time(); $filename = $files['filetoupload']['name']; $filesize = $files['filetoupload']['size']; $data = file_get_contents($files['filetoupload']['tmp_name']); @@ -184,7 +188,6 @@ class NetscapeBookmarkUtils $linkDb[$existingLink['id']] = $newLink; $importCount++; $overwriteCount++; - $history->updateLink($newLink); continue; } @@ -196,16 +199,19 @@ class NetscapeBookmarkUtils $newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']); $linkDb[$newLink['id']] = $newLink; $importCount++; - $history->addLink($newLink); } $linkDb->save($conf->get('resource.page_cache')); + $history->importLinks(); + + $duration = time() - $start; return self::importStatus( $filename, $filesize, $importCount, $overwriteCount, - $skipCount + $skipCount, + $duration ); } } 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 try { $version = ApplicationUtils::checkUpdate( - shaarli_version, + SHAARLI_VERSION, $this->conf->get('resource.update_check'), $this->conf->get('updates.check_updates_interval'), $this->conf->get('updates.check_updates'), @@ -75,7 +75,11 @@ class PageBuilder } $this->tpl->assign('searchcrits', $searchcrits); $this->tpl->assign('source', index_url($_SERVER)); - $this->tpl->assign('version', shaarli_version); + $this->tpl->assign('version', SHAARLI_VERSION); + $this->tpl->assign( + 'version_hash', + ApplicationUtils::getVersionHash(SHAARLI_VERSION, $this->conf->get('credentials.salt')) + ); $this->tpl->assign('scripturl', index_url($_SERVER)); $this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links? $this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly'])); @@ -89,6 +93,7 @@ class PageBuilder $this->tpl->assign('feed_type', $this->conf->get('feed.show_atom', true) !== false ? 'atom' : 'rss'); $this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false)); $this->tpl->assign('token', getToken($this->conf)); + if ($this->linkDB !== null) { $this->tpl->assign('tags', $this->linkDB->linksCountPerTag()); } 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 */ public function updateMethodCheckUpdateRemoteBranch() { - if (shaarli_version === 'dev' || $this->conf->get('updates.check_updates_branch') === 'latest') { + if (SHAARLI_VERSION === 'dev' || $this->conf->get('updates.check_updates_branch') === 'latest') { return true; } @@ -413,7 +413,7 @@ class Updater $latestMajor = $matches[1]; // Get current major version digit - preg_match('/(\d+)\.\d+$/', shaarli_version, $matches); + preg_match('/(\d+)\.\d+$/', SHAARLI_VERSION, $matches); $currentMajor = $matches[1]; if ($currentMajor === $latestMajor) { diff --git a/data/.htaccess b/data/.htaccess index f601c1ee..1d49da37 100644 --- a/data/.htaccess +++ b/data/.htaccess @@ -1,10 +1,16 @@ = 2.4> - Require all denied + Require all denied + + Require all granted + - Allow from none - Deny from all + Allow from none + Deny from all + + Allow from all + diff --git a/doc/md/Upgrade-and-migration.md b/doc/md/Upgrade-and-migration.md index b3a08764..7033cd41 100644 --- a/doc/md/Upgrade-and-migration.md +++ b/doc/md/Upgrade-and-migration.md @@ -14,7 +14,7 @@ Shaarli stores all user data under the `data` directory: - `data/ipbans.php` - banned IP addresses - `data/updates.txt` - contains all automatic update to the configuration and datastore files already run -See [Shaarli configuration](Shaarli configuration) for more information about Shaarli resources. +See [Shaarli configuration](Shaarli-configuration) for more information about Shaarli resources. It is recommended to backup this repository _before_ starting updating/upgrading Shaarli: @@ -27,7 +27,7 @@ As all user data is kept under `data`, this is the only directory you need to wo - backup the `data` directory - install or update Shaarli: - - fresh installation - see [Download and installation](Download and installation) + - fresh installation - see [Download and installation](Download-and-installation) - update - see the following sections - check or restore the `data` directory @@ -35,11 +35,11 @@ As all user data is kept under `data`, this is the only directory you need to wo All tagged revisions can be downloaded as tarballs or ZIP archives from the [releases](https://github.com/shaarli/Shaarli/releases) page. -We recommend that you use the latest release tarball with the `-full` suffix. It contains the dependencies, please read [Download and installation](Download and installation) for `git` complete instructions. +We recommend that you use the latest release tarball with the `-full` suffix. It contains the dependencies, please read [Download and installation](Download-and-installation) for `git` complete instructions. Once downloaded, extract the archive locally and update your remote installation (e.g. via FTP) -be sure you keep the content of the `data` directory! -After upgrading, access your fresh Shaarli installation from a web browser; the configuration and data store will then be automatically updated, and new settings added to `data/config.json.php` (see [Shaarli configuration](Shaarli configuration) for more details). +After upgrading, access your fresh Shaarli installation from a web browser; the configuration and data store will then be automatically updated, and new settings added to `data/config.json.php` (see [Shaarli configuration](Shaarli-configuration) for more details). ## Upgrading with Git @@ -173,7 +173,7 @@ Total 3317 (delta 2050), reused 3301 (delta 2034)to #### Step 3: configuration -After migrating, access your fresh Shaarli installation from a web browser; the configuration will then be automatically updated, and new settings added to `data/config.php` (see [Shaarli configuration](Shaarli configuration) for more details). +After migrating, access your fresh Shaarli installation from a web browser; the configuration will then be automatically updated, and new settings added to `data/config.php` (see [Shaarli configuration](Shaarli-configuration) for more details). ## Troubleshooting 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 Digest: sha256:c584131da2ac1948aa3e66468a4424b6aea2f33acba7cec0b631bdb56254c4fe Status: Downloaded newer image for debian:wheezy ``` + +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. + +### Start a container +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. + +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``. + +Stopped containers are not destroyed, unless you specify ``--rm``. To view all created, running and stopped containers, enter: +```bash +$ docker ps -a +``` + +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. + +### Access a running container +A running container is accessible using ``docker exec``, or ``docker copy``. You can use ``exec`` to start a root shell in the Shaarli container: +```bash +$ docker exec -ti bash +``` +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. + +Access can also be through one or more network ports, or disk volumes. Both are specified on and fixed on ``docker create`` or ``run``. + +You can view the console output of the main container process too: +```bash +$ docker logs -f +``` + +### Docker disk use +Trying out different images can fill some gigabytes of disk quickly. Besides images, the docker volumes usually take up most disk space. + +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: + +```bash +$ docker rmi -f $(docker images -aq) # remove or mark all images for disposal +$ docker volume rm $(docker volume ls -q) # remove all volumes +``` + +### Systemd config +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. + +```bash +systemctl enable /etc/systemd/system/docker.shaarli.service +systemctl start docker.shaarli +systemctl status docker.* +journalctl -f # inspect system log if needed +``` + +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: +``` +[Unit] +Description=Shaarli Bookmark Manager Container +After=docker.service +Requires=docker.service + + +[Service] +Restart=always + +# Put any environment you want in an included file, like $host- or $domainname in this example +EnvironmentFile=/etc/sysconfig/box-environment + +# It's just an example.. +ExecStart=/usr/bin/docker run \ + -p 28010:80 \ + --name ${hostname}-shaarli \ + --hostname shaarli.${domainname} \ + -v /srv/docker-volumes-local/shaarli-data:/var/www/shaarli/data:rw \ + -v /etc/localtime:/etc/localtime:ro \ + shaarli/shaarli:latest + +ExecStop=/usr/bin/docker rm -f ${hostname}-shaarli + + +[Install] +WantedBy=multi-user.target +``` 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. Login: `demo`; Password: `demo` +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): +``` +MY_SHAARLI_VOLUME=$(cd /path/to/shaarli/data/ && pwd -P) +docker run -ti --rm \ + -p 8000:80 \ + -v $MY_SHAARLI_VOLUME:/var/www/shaarli/data \ + shaarli/shaarli +``` + +A brief guide on getting starting using docker is given in [Docker 101](docker/docker-101). +To learn more about user data and how to keep it across versions, please see [Upgrade and Migration](Upgrade-and-migration) documentation. ## Features diff --git a/index.php b/index.php index 43aab303..4068a828 100644 --- a/index.php +++ b/index.php @@ -88,7 +88,7 @@ try { exit; } -define('shaarli_version', ApplicationUtils::getVersion(__DIR__ .'/'. ApplicationUtils::$VERSION_FILE)); +define('SHAARLI_VERSION', ApplicationUtils::getVersion(__DIR__ .'/'. ApplicationUtils::$VERSION_FILE)); // Force cookie path (but do not change lifetime) $cookie = session_get_cookie_params(); @@ -840,7 +840,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history) } $data = array( - 'search_tags' => implode(' ', $filteringTags), + 'search_tags' => implode(' ', escape($filteringTags)), 'tags' => $tagList, ); $pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => isLoggedIn())); @@ -870,7 +870,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history) } $data = [ - 'search_tags' => implode(' ', $filteringTags), + 'search_tags' => implode(' ', escape($filteringTags)), 'tags' => $tags, ]; $pluginManager->executeHooks('render_taglist', $data, ['loggedin' => isLoggedIn()]); diff --git a/tests/NetscapeBookmarkUtils/BookmarkImportTest.php b/tests/NetscapeBookmarkUtils/BookmarkImportTest.php index 5fc1d1e8..4961aa2c 100644 --- a/tests/NetscapeBookmarkUtils/BookmarkImportTest.php +++ b/tests/NetscapeBookmarkUtils/BookmarkImportTest.php @@ -132,8 +132,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase public function testImportInternetExplorerEncoding() { $files = file2array('internet_explorer_encoding.htm'); - $this->assertEquals( - 'File internet_explorer_encoding.htm (356 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File internet_explorer_encoding.htm (356 bytes) was successfully processed in %d seconds:' .' 1 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history) ); @@ -161,8 +161,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase public function testImportNested() { $files = file2array('netscape_nested.htm'); - $this->assertEquals( - 'File netscape_nested.htm (1337 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_nested.htm (1337 bytes) was successfully processed in %d seconds:' .' 8 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history) ); @@ -283,8 +283,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase public function testImportDefaultPrivacyNoPost() { $files = file2array('netscape_basic.htm'); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 2 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history) ); @@ -328,8 +328,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase { $post = array('privacy' => 'default'); $files = file2array('netscape_basic.htm'); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 2 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); @@ -372,8 +372,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase { $post = array('privacy' => 'public'); $files = file2array('netscape_basic.htm'); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 2 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); @@ -396,8 +396,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase { $post = array('privacy' => 'private'); $files = file2array('netscape_basic.htm'); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 2 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); @@ -422,8 +422,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase // import links as private $post = array('privacy' => 'private'); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 2 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); @@ -442,8 +442,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase 'privacy' => 'public', 'overwrite' => 'true' ); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 2 links imported, 2 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); @@ -468,8 +468,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase // import links as public $post = array('privacy' => 'public'); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 2 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); @@ -489,8 +489,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase 'privacy' => 'private', 'overwrite' => 'true' ); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 2 links imported, 2 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); @@ -513,8 +513,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase { $post = array('privacy' => 'public'); $files = file2array('netscape_basic.htm'); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 2 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); @@ -523,8 +523,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase // re-import as private, DO NOT enable overwriting $post = array('privacy' => 'private'); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 0 links imported, 0 links overwritten, 2 links skipped.', NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); @@ -542,8 +542,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase 'default_tags' => 'tag1,tag2 tag3' ); $files = file2array('netscape_basic.htm'); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 2 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); @@ -569,8 +569,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase 'default_tags' => 'tag1&,tag2 "tag3"' ); $files = file2array('netscape_basic.htm'); - $this->assertEquals( - 'File netscape_basic.htm (482 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' .' 2 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) ); @@ -594,8 +594,8 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase public function testImportSameDate() { $files = file2array('same_date.htm'); - $this->assertEquals( - 'File same_date.htm (453 bytes) was successfully processed:' + $this->assertStringMatchesFormat( + 'File same_date.htm (453 bytes) was successfully processed in %d seconds:' .' 3 links imported, 0 links overwritten, 0 links skipped.', NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf, $this->history) ); @@ -622,24 +622,19 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase 'overwrite' => 'true', ]; $files = file2array('netscape_basic.htm'); - $nbLinks = 2; NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history); $history = $this->history->getHistory(); - $this->assertEquals($nbLinks, count($history)); - foreach ($history as $value) { - $this->assertEquals(History::CREATED, $value['event']); - $this->assertTrue(new DateTime('-5 seconds') < $value['datetime']); - $this->assertTrue(is_int($value['id'])); - } + $this->assertEquals(1, count($history)); + $this->assertEquals(History::IMPORT, $history[0]['event']); + $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']); // re-import as private, enable overwriting NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history); $history = $this->history->getHistory(); - $this->assertEquals($nbLinks * 2, count($history)); - for ($i = 0 ; $i < $nbLinks ; $i++) { - $this->assertEquals(History::UPDATED, $history[$i]['event']); - $this->assertTrue(new DateTime('-5 seconds') < $history[$i]['datetime']); - $this->assertTrue(is_int($history[$i]['id'])); - } + $this->assertEquals(2, count($history)); + $this->assertEquals(History::IMPORT, $history[0]['event']); + $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']); + $this->assertEquals(History::IMPORT, $history[1]['event']); + $this->assertTrue(new DateTime('-5 seconds') < $history[1]['datetime']); } } 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 @@ - - - - - - + + + + + + {if="is_file('data/user.css')"} {/if} {loop="$plugins_includes.css_files"} - + {/loop} \ 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 () { }; function init () { function resize () { + /* Fix jumpy resizing: https://stackoverflow.com/a/18262927/1484919 */ + var scrollTop = window.pageYOffset || + (document.documentElement || document.body.parentNode || document.body).scrollTop; + description.style.height = 'auto'; description.style.height = description.scrollHeight+10+'px'; + + window.scrollTo(0, scrollTop); } /* 0-timeout to get the already changed text */ 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 @@ {/loop} - - - + + + diff --git a/tpl/default/tag.cloud.html b/tpl/default/tag.cloud.html index 96b357a3..68335c70 100644 --- a/tpl/default/tag.cloud.html +++ b/tpl/default/tag.cloud.html @@ -26,7 +26,7 @@