aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--Makefile2
-rw-r--r--application/ApplicationUtils.php92
-rw-r--r--index.php39
-rw-r--r--tests/ApplicationUtilsTest.php218
-rw-r--r--tests/CacheTest.php6
-rw-r--r--tests/CachedPageTest.php2
-rw-r--r--tests/LinkDBTest.php2
8 files changed, 331 insertions, 35 deletions
diff --git a/.gitignore b/.gitignore
index b98c38b9..75cd3a6b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,9 +19,8 @@ composer.lock
19# Ignore development and test resources 19# Ignore development and test resources
20coverage 20coverage
21doxygen 21doxygen
22tests/datastore.php 22sandbox
23tests/dummycache/
24phpmd.html 23phpmd.html
25 24
26# Ignore user plugin configuration 25# Ignore user plugin configuration
27plugins/*/config.php \ No newline at end of file 26plugins/*/config.php
diff --git a/Makefile b/Makefile
index c560d8d1..a86f9aa8 100644
--- a/Makefile
+++ b/Makefile
@@ -110,6 +110,7 @@ test:
110 @echo "-------" 110 @echo "-------"
111 @echo "PHPUNIT" 111 @echo "PHPUNIT"
112 @echo "-------" 112 @echo "-------"
113 @mkdir -p sandbox
113 @$(BIN)/phpunit tests 114 @$(BIN)/phpunit tests
114 115
115## 116##
@@ -119,6 +120,7 @@ test:
119### remove all unversioned files 120### remove all unversioned files
120clean: 121clean:
121 @git clean -df 122 @git clean -df
123 @rm -rf sandbox
122 124
123### generate Doxygen documentation 125### generate Doxygen documentation
124doxygen: clean 126doxygen: clean
diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php
index b0e94e24..c7414b77 100644
--- a/application/ApplicationUtils.php
+++ b/application/ApplicationUtils.php
@@ -4,6 +4,98 @@
4 */ 4 */
5class ApplicationUtils 5class ApplicationUtils
6{ 6{
7 private static $GIT_URL = 'https://raw.githubusercontent.com/shaarli/Shaarli';
8 private static $GIT_BRANCH = 'master';
9 private static $VERSION_FILE = 'shaarli_version.php';
10 private static $VERSION_START_TAG = '<?php /* ';
11 private static $VERSION_END_TAG = ' */ ?>';
12
13 /**
14 * Gets the latest version code from the Git repository
15 *
16 * The code is read from the raw content of the version file on the Git server.
17 *
18 * @return mixed the version code from the repository if available, else 'false'
19 */
20 public static function getLatestGitVersionCode($url, $timeout=2)
21 {
22 list($headers, $data) = get_http_url($url, $timeout);
23
24 if (strpos($headers[0], '200 OK') === false) {
25 error_log('Failed to retrieve ' . $url);
26 return false;
27 }
28
29 return str_replace(
30 array(self::$VERSION_START_TAG, self::$VERSION_END_TAG, PHP_EOL),
31 array('', '', ''),
32 $data
33 );
34 }
35
36 /**
37 * Checks if a new Shaarli version has been published on the Git repository
38 *
39 * Updates checks are run periodically, according to the following criteria:
40 * - the update checks are enabled (install, global config);
41 * - the user is logged in (or this is an open instance);
42 * - the last check is older than a given interval;
43 * - the check is non-blocking if the HTTPS connection to Git fails;
44 * - in case of failure, the update file's modification date is updated,
45 * to avoid intempestive connection attempts.
46 *
47 * @param string $currentVersion the current version code
48 * @param string $updateFile the file where to store the latest version code
49 * @param int $checkInterval the minimum interval between update checks (in seconds
50 * @param bool $enableCheck whether to check for new versions
51 * @param bool $isLoggedIn whether the user is logged in
52 *
53 * @return mixed the new version code if available and greater, else 'false'
54 */
55 public static function checkUpdate(
56 $currentVersion, $updateFile, $checkInterval, $enableCheck, $isLoggedIn)
57 {
58 if (! $isLoggedIn) {
59 // Do not check versions for visitors
60 return false;
61 }
62
63 if (empty($enableCheck)) {
64 // Do not check if the user doesn't want to
65 return false;
66 }
67
68 if (is_file($updateFile) && (filemtime($updateFile) > time() - $checkInterval)) {
69 // Shaarli has checked for updates recently - skip HTTP query
70 $latestKnownVersion = file_get_contents($updateFile);
71
72 if (version_compare($latestKnownVersion, $currentVersion) == 1) {
73 return $latestKnownVersion;
74 }
75 return false;
76 }
77
78 // Late Static Binding allows overriding within tests
79 // See http://php.net/manual/en/language.oop5.late-static-bindings.php
80 $latestVersion = static::getLatestGitVersionCode(
81 self::$GIT_URL . '/' . self::$GIT_BRANCH . '/' . self::$VERSION_FILE
82 );
83
84 if (! $latestVersion) {
85 // Only update the file's modification date
86 file_put_contents($updateFile, $currentVersion);
87 return false;
88 }
89
90 // Update the file's content and modification date
91 file_put_contents($updateFile, $latestVersion);
92
93 if (version_compare($latestVersion, $currentVersion) == 1) {
94 return $latestVersion;
95 }
96
97 return false;
98 }
7 99
8 /** 100 /**
9 * Checks the PHP version to ensure Shaarli can run 101 * Checks the PHP version to ensure Shaarli can run
diff --git a/index.php b/index.php
index 3954be97..90045d60 100644
--- a/index.php
+++ b/index.php
@@ -305,32 +305,6 @@ function setup_login_state() {
305} 305}
306$userIsLoggedIn = setup_login_state(); 306$userIsLoggedIn = setup_login_state();
307 307
308// Checks if an update is available for Shaarli.
309// (at most once a day, and only for registered user.)
310// Output: '' = no new version.
311// other= the available version.
312function checkUpdate()
313{
314 if (!isLoggedIn()) return ''; // Do not check versions for visitors.
315 if (empty($GLOBALS['config']['ENABLE_UPDATECHECK'])) return ''; // Do not check if the user doesn't want to.
316
317 // Get latest version number at most once a day.
318 if (!is_file($GLOBALS['config']['UPDATECHECK_FILENAME']) || (filemtime($GLOBALS['config']['UPDATECHECK_FILENAME'])<time()-($GLOBALS['config']['UPDATECHECK_INTERVAL'])))
319 {
320 $version = shaarli_version;
321 list($headers, $data) = get_http_url('https://raw.githubusercontent.com/shaarli/Shaarli/master/shaarli_version.php', 2);
322 if (strpos($headers[0], '200 OK') !== false) {
323 $version = str_replace(' */ ?>', '', str_replace('<?php /* ', '', $data));
324 }
325 // If failed, never mind. We don't want to bother the user with that.
326 file_put_contents($GLOBALS['config']['UPDATECHECK_FILENAME'],$version); // touch file date
327 }
328 // Compare versions:
329 $newestversion=file_get_contents($GLOBALS['config']['UPDATECHECK_FILENAME']);
330 if (version_compare($newestversion,shaarli_version)==1) return $newestversion;
331 return '';
332}
333
334 308
335// ----------------------------------------------------------------------------------------------- 309// -----------------------------------------------------------------------------------------------
336// Log to text file 310// Log to text file
@@ -657,7 +631,18 @@ class pageBuilder
657 private function initialize() 631 private function initialize()
658 { 632 {
659 $this->tpl = new RainTPL; 633 $this->tpl = new RainTPL;
660 $this->tpl->assign('newversion', escape(checkUpdate())); 634 $this->tpl->assign(
635 'newversion',
636 escape(
637 ApplicationUtils::checkUpdate(
638 shaarli_version,
639 $GLOBALS['config']['UPDATECHECK_FILENAME'],
640 $GLOBALS['config']['UPDATECHECK_INTERVAL'],
641 $GLOBALS['config']['ENABLE_UPDATECHECK'],
642 isLoggedIn()
643 )
644 )
645 );
661 $this->tpl->assign('feedurl', escape(index_url($_SERVER))); 646 $this->tpl->assign('feedurl', escape(index_url($_SERVER)));
662 $searchcrits = ''; // Search criteria 647 $searchcrits = ''; // Search criteria
663 if (!empty($_GET['searchtags'])) { 648 if (!empty($_GET['searchtags'])) {
diff --git a/tests/ApplicationUtilsTest.php b/tests/ApplicationUtilsTest.php
index 01301e68..437c21fd 100644
--- a/tests/ApplicationUtilsTest.php
+++ b/tests/ApplicationUtilsTest.php
@@ -5,12 +5,230 @@
5 5
6require_once 'application/ApplicationUtils.php'; 6require_once 'application/ApplicationUtils.php';
7 7
8/**
9 * Fake ApplicationUtils class to avoid HTTP requests
10 */
11class FakeApplicationUtils extends ApplicationUtils
12{
13 public static $VERSION_CODE = '';
14
15 /**
16 * Toggle HTTP requests, allow overriding the version code
17 */
18 public static function getLatestGitVersionCode($url, $timeout=0)
19 {
20 return self::$VERSION_CODE;
21 }
22}
23
8 24
9/** 25/**
10 * Unitary tests for Shaarli utilities 26 * Unitary tests for Shaarli utilities
11 */ 27 */
12class ApplicationUtilsTest extends PHPUnit_Framework_TestCase 28class ApplicationUtilsTest extends PHPUnit_Framework_TestCase
13{ 29{
30 protected static $testUpdateFile = 'sandbox/update.txt';
31 protected static $testVersion = '0.5.0';
32 protected static $versionPattern = '/^\d+\.\d+\.\d+$/';
33
34 /**
35 * Reset test data for each test
36 */
37 public function setUp()
38 {
39 FakeApplicationUtils::$VERSION_CODE = '';
40 if (file_exists(self::$testUpdateFile)) {
41 unlink(self::$testUpdateFile);
42 }
43 }
44
45 /**
46 * Retrieve the latest version code available on Git
47 *
48 * Expected format: Semantic Versioning - major.minor.patch
49 */
50 public function testGetLatestGitVersionCode()
51 {
52 $testTimeout = 10;
53
54 $this->assertEquals(
55 '0.5.4',
56 ApplicationUtils::getLatestGitVersionCode(
57 'https://raw.githubusercontent.com/shaarli/Shaarli/'
58 .'v0.5.4/shaarli_version.php',
59 $testTimeout
60 )
61 );
62 $this->assertRegexp(
63 self::$versionPattern,
64 ApplicationUtils::getLatestGitVersionCode(
65 'https://raw.githubusercontent.com/shaarli/Shaarli/'
66 .'master/shaarli_version.php',
67 $testTimeout
68 )
69 );
70 }
71
72 /**
73 * Attempt to retrieve the latest version from an invalid URL
74 */
75 public function testGetLatestGitVersionCodeInvalidUrl()
76 {
77 $this->assertFalse(
78 ApplicationUtils::getLatestGitVersionCode('htttp://null.io', 0)
79 );
80 }
81
82 /**
83 * Test update checks - the user is logged off
84 */
85 public function testCheckUpdateLoggedOff()
86 {
87 $this->assertFalse(
88 ApplicationUtils::checkUpdate(self::$testVersion, 'null', 0, false, false)
89 );
90 }
91
92 /**
93 * Test update checks - the user has disabled updates
94 */
95 public function testCheckUpdateUserDisabled()
96 {
97 $this->assertFalse(
98 ApplicationUtils::checkUpdate(self::$testVersion, 'null', 0, false, true)
99 );
100 }
101
102 /**
103 * A newer version is available
104 */
105 public function testCheckUpdateNewVersionNew()
106 {
107 $newVersion = '1.8.3';
108 FakeApplicationUtils::$VERSION_CODE = $newVersion;
109
110 $version = FakeApplicationUtils::checkUpdate(
111 self::$testVersion,
112 self::$testUpdateFile,
113 100,
114 true,
115 true
116 );
117
118 $this->assertEquals($newVersion, $version);
119 }
120
121 /**
122 * No available information about versions
123 */
124 public function testCheckUpdateNewVersionUnavailable()
125 {
126 $version = FakeApplicationUtils::checkUpdate(
127 self::$testVersion,
128 self::$testUpdateFile,
129 100,
130 true,
131 true
132 );
133
134 $this->assertFalse($version);
135 }
136
137 /**
138 * Shaarli is up-to-date
139 */
140 public function testCheckUpdateNewVersionUpToDate()
141 {
142 FakeApplicationUtils::$VERSION_CODE = self::$testVersion;
143
144 $version = FakeApplicationUtils::checkUpdate(
145 self::$testVersion,
146 self::$testUpdateFile,
147 100,
148 true,
149 true
150 );
151
152 $this->assertFalse($version);
153 }
154
155 /**
156 * Time-traveller's Shaarli
157 */
158 public function testCheckUpdateNewVersionMaartiMcFly()
159 {
160 FakeApplicationUtils::$VERSION_CODE = '0.4.1';
161
162 $version = FakeApplicationUtils::checkUpdate(
163 self::$testVersion,
164 self::$testUpdateFile,
165 100,
166 true,
167 true
168 );
169
170 $this->assertFalse($version);
171 }
172
173 /**
174 * The version has been checked recently and Shaarli is up-to-date
175 */
176 public function testCheckUpdateNewVersionTwiceUpToDate()
177 {
178 FakeApplicationUtils::$VERSION_CODE = self::$testVersion;
179
180 // Create the update file
181 $version = FakeApplicationUtils::checkUpdate(
182 self::$testVersion,
183 self::$testUpdateFile,
184 100,
185 true,
186 true
187 );
188
189 $this->assertFalse($version);
190
191 // Reuse the update file
192 $version = FakeApplicationUtils::checkUpdate(
193 self::$testVersion,
194 self::$testUpdateFile,
195 100,
196 true,
197 true
198 );
199
200 $this->assertFalse($version);
201 }
202
203 /**
204 * The version has been checked recently and Shaarli is outdated
205 */
206 public function testCheckUpdateNewVersionTwiceOutdated()
207 {
208 $newVersion = '1.8.3';
209 FakeApplicationUtils::$VERSION_CODE = $newVersion;
210
211 // Create the update file
212 $version = FakeApplicationUtils::checkUpdate(
213 self::$testVersion,
214 self::$testUpdateFile,
215 100,
216 true,
217 true
218 );
219 $this->assertEquals($newVersion, $version);
220
221 // Reuse the update file
222 $version = FakeApplicationUtils::checkUpdate(
223 self::$testVersion,
224 self::$testUpdateFile,
225 100,
226 true,
227 true
228 );
229 $this->assertEquals($newVersion, $version);
230 }
231
14 /** 232 /**
15 * Check supported PHP versions 233 * Check supported PHP versions
16 */ 234 */
diff --git a/tests/CacheTest.php b/tests/CacheTest.php
index aa5395b0..eacd4487 100644
--- a/tests/CacheTest.php
+++ b/tests/CacheTest.php
@@ -11,10 +11,10 @@ require_once 'application/Cache.php';
11/** 11/**
12 * Unitary tests for cached pages 12 * Unitary tests for cached pages
13 */ 13 */
14class CachedTest extends PHPUnit_Framework_TestCase 14class CacheTest extends PHPUnit_Framework_TestCase
15{ 15{
16 // test cache directory 16 // test cache directory
17 protected static $testCacheDir = 'tests/dummycache'; 17 protected static $testCacheDir = 'sandbox/dummycache';
18 18
19 // dummy cached file names / content 19 // dummy cached file names / content
20 protected static $pages = array('a', 'toto', 'd7b59c'); 20 protected static $pages = array('a', 'toto', 'd7b59c');
@@ -56,7 +56,7 @@ class CachedTest extends PHPUnit_Framework_TestCase
56 public function testPurgeCachedPagesMissingDir() 56 public function testPurgeCachedPagesMissingDir()
57 { 57 {
58 $this->assertEquals( 58 $this->assertEquals(
59 'Cannot purge tests/dummycache_missing: no directory', 59 'Cannot purge sandbox/dummycache_missing: no directory',
60 purgeCachedPages(self::$testCacheDir.'_missing') 60 purgeCachedPages(self::$testCacheDir.'_missing')
61 ); 61 );
62 } 62 }
diff --git a/tests/CachedPageTest.php b/tests/CachedPageTest.php
index e97af030..51565cd6 100644
--- a/tests/CachedPageTest.php
+++ b/tests/CachedPageTest.php
@@ -11,7 +11,7 @@ require_once 'application/CachedPage.php';
11class CachedPageTest extends PHPUnit_Framework_TestCase 11class CachedPageTest extends PHPUnit_Framework_TestCase
12{ 12{
13 // test cache directory 13 // test cache directory
14 protected static $testCacheDir = 'tests/pagecache'; 14 protected static $testCacheDir = 'sandbox/pagecache';
15 protected static $url = 'http://shaar.li/?do=atom'; 15 protected static $url = 'http://shaar.li/?do=atom';
16 protected static $filename; 16 protected static $filename;
17 17
diff --git a/tests/LinkDBTest.php b/tests/LinkDBTest.php
index ff917f6d..7b22b270 100644
--- a/tests/LinkDBTest.php
+++ b/tests/LinkDBTest.php
@@ -16,7 +16,7 @@ require_once 'tests/utils/ReferenceLinkDB.php';
16class LinkDBTest extends PHPUnit_Framework_TestCase 16class LinkDBTest extends PHPUnit_Framework_TestCase
17{ 17{
18 // datastore to test write operations 18 // datastore to test write operations
19 protected static $testDatastore = 'tests/datastore.php'; 19 protected static $testDatastore = 'sandbox/datastore.php';
20 protected static $refDB = null; 20 protected static $refDB = null;
21 protected static $publicLinkDB = null; 21 protected static $publicLinkDB = null;
22 protected static $privateLinkDB = null; 22 protected static $privateLinkDB = null;