# Ignore development and test resources
coverage
doxygen
-tests/datastore.php
-tests/dummycache/
+sandbox
phpmd.html
# Ignore user plugin configuration
-plugins/*/config.php
\ No newline at end of file
+plugins/*/config.php
@echo "-------"
@echo "PHPUNIT"
@echo "-------"
+ @mkdir -p sandbox
@$(BIN)/phpunit tests
##
### remove all unversioned files
clean:
@git clean -df
+ @rm -rf sandbox
### generate Doxygen documentation
doxygen: clean
*/
class ApplicationUtils
{
+ private static $GIT_URL = 'https://raw.githubusercontent.com/shaarli/Shaarli';
+ private static $GIT_BRANCHES = array('master', 'stable');
+ private static $VERSION_FILE = 'shaarli_version.php';
+ private static $VERSION_START_TAG = '<?php /* ';
+ private static $VERSION_END_TAG = ' */ ?>';
+
+ /**
+ * Gets the latest version code from the Git repository
+ *
+ * The code is read from the raw content of the version file on the Git server.
+ *
+ * @return mixed the version code from the repository if available, else 'false'
+ */
+ public static function getLatestGitVersionCode($url, $timeout=2)
+ {
+ list($headers, $data) = get_http_url($url, $timeout);
+
+ if (strpos($headers[0], '200 OK') === false) {
+ error_log('Failed to retrieve ' . $url);
+ return false;
+ }
+
+ return str_replace(
+ array(self::$VERSION_START_TAG, self::$VERSION_END_TAG, PHP_EOL),
+ array('', '', ''),
+ $data
+ );
+ }
+
+ /**
+ * Checks if a new Shaarli version has been published on the Git repository
+ *
+ * Updates checks are run periodically, according to the following criteria:
+ * - the update checks are enabled (install, global config);
+ * - the user is logged in (or this is an open instance);
+ * - the last check is older than a given interval;
+ * - the check is non-blocking if the HTTPS connection to Git fails;
+ * - in case of failure, the update file's modification date is updated,
+ * to avoid intempestive connection attempts.
+ *
+ * @param string $currentVersion the current version code
+ * @param string $updateFile the file where to store the latest version code
+ * @param int $checkInterval the minimum interval between update checks (in seconds
+ * @param bool $enableCheck whether to check for new versions
+ * @param bool $isLoggedIn whether the user is logged in
+ *
+ * @throws Exception an invalid branch has been set for update checks
+ *
+ * @return mixed the new version code if available and greater, else 'false'
+ */
+ public static function checkUpdate($currentVersion,
+ $updateFile,
+ $checkInterval,
+ $enableCheck,
+ $isLoggedIn,
+ $branch='stable')
+ {
+ if (! $isLoggedIn) {
+ // Do not check versions for visitors
+ return false;
+ }
+
+ if (empty($enableCheck)) {
+ // Do not check if the user doesn't want to
+ return false;
+ }
+
+ if (is_file($updateFile) && (filemtime($updateFile) > time() - $checkInterval)) {
+ // Shaarli has checked for updates recently - skip HTTP query
+ $latestKnownVersion = file_get_contents($updateFile);
+
+ if (version_compare($latestKnownVersion, $currentVersion) == 1) {
+ return $latestKnownVersion;
+ }
+ return false;
+ }
+
+ if (! in_array($branch, self::$GIT_BRANCHES)) {
+ throw new Exception(
+ 'Invalid branch selected for updates: "' . $branch . '"'
+ );
+ }
+
+ // Late Static Binding allows overriding within tests
+ // See http://php.net/manual/en/language.oop5.late-static-bindings.php
+ $latestVersion = static::getLatestGitVersionCode(
+ self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE
+ );
+
+ if (! $latestVersion) {
+ // Only update the file's modification date
+ file_put_contents($updateFile, $currentVersion);
+ return false;
+ }
+
+ // Update the file's content and modification date
+ file_put_contents($updateFile, $latestVersion);
+
+ if (version_compare($latestVersion, $currentVersion) == 1) {
+ return $latestVersion;
+ }
+
+ return false;
+ }
/**
* Checks the PHP version to ensure Shaarli can run
// Hide public links
private $_hidePublicLinks;
+ // link redirector set in user settings.
+ private $_redirector;
+
/**
* Creates a new LinkDB
*
* Checks if the datastore exists; else, attempts to create a dummy one.
*
- * @param $isLoggedIn is the user logged in?
+ * @param string $datastore datastore file path.
+ * @param boolean $isLoggedIn is the user logged in?
+ * @param boolean $hidePublicLinks if true all links are private.
+ * @param string $redirector link redirector set in user settings.
*/
- function __construct($datastore, $isLoggedIn, $hidePublicLinks)
+ function __construct($datastore, $isLoggedIn, $hidePublicLinks, $redirector = '')
{
$this->_datastore = $datastore;
$this->_loggedIn = $isLoggedIn;
$this->_hidePublicLinks = $hidePublicLinks;
+ $this->_redirector = $redirector;
$this->_checkDB();
$this->_readDB();
}
// Escape links data
foreach($this->_links as &$link) {
- sanitizeLink($link);
+ sanitizeLink($link);
+ // Do not use the redirector for internal links (Shaarli note URL starting with a '?').
+ if (!empty($this->_redirector) && !startsWith($link['url'], '?')) {
+ $link['real_url'] = $this->_redirector . urlencode($link['url']);
+ }
+ else {
+ $link['real_url'] = $link['url'];
+ }
}
}
return true;
}
+
+/**
+ * In a string, converts URLs to clickable links.
+ *
+ * @param string $text input string.
+ * @param string $redirector if a redirector is set, use it to gerenate links.
+ *
+ * @return string returns $text with all links converted to HTML links.
+ *
+ * @see Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722
+ */
+function text2clickable($text, $redirector)
+{
+ $regex = '!(((?:https?|ftp|file)://|apt:|magnet:)\S+[[:alnum:]]/?)!si';
+
+ if (empty($redirector)) {
+ return preg_replace($regex, '<a href="$1">$1</a>', $text);
+ }
+ // Redirector is set, urlencode the final URL.
+ return preg_replace_callback(
+ $regex,
+ function ($matches) use ($redirector) {
+ return '<a href="' . $redirector . urlencode($matches[1]) .'">'. $matches[1] .'</a>';
+ },
+ $text
+ );
+}
+
+/**
+ * This function inserts where relevant so that multiple spaces are properly displayed in HTML
+ * even in the absence of <pre> (This is used in description to keep text formatting).
+ *
+ * @param string $text input text.
+ *
+ * @return string formatted text.
+ */
+function space2nbsp($text)
+{
+ return preg_replace('/(^| ) /m', '$1 ', $text);
+}
+
+/**
+ * Format Shaarli's description
+ * TODO: Move me to ApplicationUtils when it's ready.
+ *
+ * @param string $description shaare's description.
+ * @param string $redirector if a redirector is set, use it to gerenate links.
+ *
+ * @return string formatted description.
+ */
+function format_description($description, $redirector) {
+ return nl2br(space2nbsp(text2clickable($description, $redirector)));
+}
<?php
/**
- * Shaarli v0.6.0 - Shaare your links...
+ * Shaarli v0.6.1 - Shaare your links...
*
* The personal, minimalist, super-fast, no-database Delicious clone.
*
$GLOBALS['config']['ENABLE_LOCALCACHE'] = true;
// Update check frequency for Shaarli. 86400 seconds=24 hours
-$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ;
+$GLOBALS['config']['UPDATECHECK_BRANCH'] = 'stable';
+$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400;
/*
/*
* PHP configuration
*/
-define('shaarli_version', '0.6.0');
+define('shaarli_version', '0.6.1');
// http://server.com/x/shaarli --> /shaarli/
define('WEB_PATH', substr($_SERVER["REQUEST_URI"], 0, 1+strrpos($_SERVER["REQUEST_URI"], '/', 0)));
}
$userIsLoggedIn = setup_login_state();
-// Checks if an update is available for Shaarli.
-// (at most once a day, and only for registered user.)
-// Output: '' = no new version.
-// other= the available version.
-function checkUpdate()
-{
- if (!isLoggedIn()) return ''; // Do not check versions for visitors.
- if (empty($GLOBALS['config']['ENABLE_UPDATECHECK'])) return ''; // Do not check if the user doesn't want to.
-
- // Get latest version number at most once a day.
- if (!is_file($GLOBALS['config']['UPDATECHECK_FILENAME']) || (filemtime($GLOBALS['config']['UPDATECHECK_FILENAME'])<time()-($GLOBALS['config']['UPDATECHECK_INTERVAL'])))
- {
- $version = shaarli_version;
- list($headers, $data) = get_http_url('https://raw.githubusercontent.com/shaarli/Shaarli/master/shaarli_version.php', 2);
- if (strpos($headers[0], '200 OK') !== false) {
- $version = str_replace(' */ ?>', '', str_replace('<?php /* ', '', $data));
- }
- // If failed, never mind. We don't want to bother the user with that.
- file_put_contents($GLOBALS['config']['UPDATECHECK_FILENAME'],$version); // touch file date
- }
- // Compare versions:
- $newestversion=file_get_contents($GLOBALS['config']['UPDATECHECK_FILENAME']);
- if (version_compare($newestversion,shaarli_version)==1) return $newestversion;
- return '';
-}
-
// -----------------------------------------------------------------------------------------------
// Log to text file
function logm($message)
{
$t = strval(date('Y/m/d_H:i:s')).' - '.$_SERVER["REMOTE_ADDR"].' - '.strval($message)."\n";
- file_put_contents($GLOBAL['config']['LOG_FILE'], $t, FILE_APPEND);
-}
-
-// In a string, converts URLs to clickable links.
-// Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722
-function text2clickable($url)
-{
- $redir = empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'];
- return preg_replace('!(((?:https?|ftp|file)://|apt:|magnet:)\S+[[:alnum:]]/?)!si','<a href="'.$redir.'$1" rel="nofollow">$1</a>',$url);
+ file_put_contents($GLOBALS['config']['LOG_FILE'], $t, FILE_APPEND);
}
-// This function inserts where relevant so that multiple spaces are properly displayed in HTML
-// even in the absence of <pre> (This is used in description to keep text formatting)
-function keepMultipleSpaces($text)
-{
- return str_replace(' ',' ',$text);
-
-}
// ------------------------------------------------------------------------------------------
// Sniff browser language to display dates in the right format automatically.
// (Note that is may not work on your server if the corresponding local is not installed.)
private function initialize()
{
$this->tpl = new RainTPL;
- $this->tpl->assign('newversion', escape(checkUpdate()));
+
+ try {
+ $version = ApplicationUtils::checkUpdate(
+ shaarli_version,
+ $GLOBALS['config']['UPDATECHECK_FILENAME'],
+ $GLOBALS['config']['UPDATECHECK_INTERVAL'],
+ $GLOBALS['config']['ENABLE_UPDATECHECK'],
+ isLoggedIn(),
+ $GLOBALS['config']['UPDATECHECK_BRANCH']
+ );
+ $this->tpl->assign('newVersion', escape($version));
+ $this->tpl->assign('versionError', '');
+
+ } catch (Exception $exc) {
+ logm($exc->getMessage());
+ $this->tpl->assign('newVersion', '');
+ $this->tpl->assign('versionError', escape($exc->getMessage()));
+ }
+
$this->tpl->assign('feedurl', escape(index_url($_SERVER)));
$searchcrits = ''; // Search criteria
if (!empty($_GET['searchtags'])) {
$LINKSDB = new LinkDB(
$GLOBALS['config']['DATASTORE'],
isLoggedIn(),
- $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+ $GLOBALS['redirector']
);
// Read links from database (and filter private links if user it not logged in).
// If user wants permalinks first, put the final link in description
if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)';
if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink;
- echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable($link['description']))).$descriptionlink.']]></description>'."\n</item>\n";
+ echo '<description><![CDATA['.
+ format_description($link['description'], $GLOBALS['redirector']) .
+ $descriptionlink . ']]></description>' . "\n</item>\n";
$i++;
}
echo '</channel></rss><!-- Cached version of '.escape(page_url($_SERVER)).' -->';
$LINKSDB = new LinkDB(
$GLOBALS['config']['DATASTORE'],
isLoggedIn(),
- $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+ $GLOBALS['redirector']
);
// Optionally filter the results:
if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)';
if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink;
- $entries.='<content type="html"><![CDATA['.nl2br(keepMultipleSpaces(text2clickable($link['description']))).$descriptionlink."]]></content>\n";
+ $entries .= '<content type="html"><![CDATA['.
+ format_description($link['description'], $GLOBALS['redirector']) .
+ $descriptionlink . "]]></content>\n";
if ($link['tags']!='') // Adding tags to each ATOM entry (as mentioned in ATOM specification)
{
foreach(explode(' ',$link['tags']) as $tag)
$LINKSDB = new LinkDB(
$GLOBALS['config']['DATASTORE'],
isLoggedIn(),
- $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+ $GLOBALS['redirector']
);
/* Some Shaarlies may have very few links, so we need to look
// We pre-format some fields for proper output.
foreach ($linkdates as $linkdate) {
$l = $LINKSDB[$linkdate];
- $l['formatedDescription'] = nl2br(keepMultipleSpaces(text2clickable($l['description'])));
+ $l['formatedDescription'] = format_description($l['description'], $GLOBALS['redirector']);
$l['thumbnail'] = thumbnail($l['url']);
$l['timestamp'] = linkdate2timestamp($l['linkdate']);
if (startsWith($l['url'], '?')) {
$LINKSDB = new LinkDB(
$GLOBALS['config']['DATASTORE'],
isLoggedIn(),
- $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+ $GLOBALS['redirector']
);
$day=Date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD.
$taglist = explode(' ',$link['tags']);
uasort($taglist, 'strcasecmp');
$linksToDisplay[$key]['taglist']=$taglist;
- $linksToDisplay[$key]['formatedDescription']=nl2br(keepMultipleSpaces(text2clickable($link['description'])));
+ $linksToDisplay[$key]['formatedDescription'] = format_description($link['description'], $GLOBALS['redirector']);
$linksToDisplay[$key]['thumbnail'] = thumbnail($link['url']);
$linksToDisplay[$key]['timestamp'] = linkdate2timestamp($link['linkdate']);
}
$LINKSDB = new LinkDB(
$GLOBALS['config']['DATASTORE'],
isLoggedIn(),
- $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+ $GLOBALS['redirector']
);
$PAGE = new pageBuilder;
$LINKSDB = new LinkDB(
$GLOBALS['config']['DATASTORE'],
isLoggedIn(),
- $GLOBALS['config']['HIDE_PUBLIC_LINKS']
+ $GLOBALS['config']['HIDE_PUBLIC_LINKS'],
+ $GLOBALS['redirector']
);
$filename=$_FILES['filetoupload']['name'];
$filesize=$_FILES['filetoupload']['size'];
while ($i<$end && $i<count($keys))
{
$link = $linksToDisplay[$keys[$i]];
- $link['description']=nl2br(keepMultipleSpaces(text2clickable($link['description'])));
- $title=$link['title'];
+ $link['description'] = format_description($link['description'], $GLOBALS['redirector']);
$classLi = $i%2!=0 ? '' : 'publicLinkHightLight';
$link['class'] = ($link['private']==0 ? $classLi : 'private');
$link['timestamp']=linkdate2timestamp($link['linkdate']);
'links' => $linkDisp,
'tags' => $LINKSDB->allTags(),
);
+ // FIXME! temporary fix - see #399.
+ if (!empty($GLOBALS['pagetitle']) && count($linkDisp) == 1) {
+ $data['pagetitle'] = $GLOBALS['pagetitle'];
+ }
$pluginManager = PluginManager::getInstance();
$pluginManager->executeHooks('render_linklist', $data, array('loggedin' => isLoggedIn()));
### â–º Play Videos plugin for Shaarli
-This plugin adds a `â–º Play Videos` button to [Shaarli](https://github.com/shaarli/Shaarli)'s toolbar. Click this button to play all videos on the page in an overlay HTML5 player. Nice for continuous stream of music, documentaries, talks...
-This uses code from https://zaius.github.io/youtube_playlist/ and is currently only compatible with Youtube videos.
+Adds a `â–º Play Videos` button to [Shaarli](https://github.com/shaarli/Shaarli)'s toolbar. Click this button to play all videos on the page in an overlay HTML5 player. Nice for continuous stream of music, documentaries, talks...
+
+<!-- TODO screenshot -->
-![](https://cdn.mediacru.sh/D_izf0zjAtxy.png)
+This uses code from https://zaius.github.io/youtube_playlist/ and is currently only compatible with Youtube videos.
#### Installation and setup
-Place the files in the `tpl/plugins/playvideos/` directory of your Shaarli.
-This is a default Shaarli plugin, you just have to enable it.
-To enable the plugin, add `playvideos` to the `TOOLBAR_PLUGINS` config option in your `index.php` or `data/options.php`. Example:
+This is a default Shaarli plugin, you just have to enable it. See https://github.com/shaarli/Shaarli/wiki/Shaarli-configuration/
- $GLOBALS['config']['TOOLBAR_PLUGINS'] = array('aplugins', 'anotherone', 'playvideos');
#### Troubleshooting
+
If your server has [Content Security Policy](http://content-security-policy.com/) headers enabled, this may prevent the script from loading fully. You should relax the CSP in your server settings. Example CSP rule for apache2:
-`Header set Content-Security-Policy "script-src 'self' 'unsafe-inline' https://www.youtube.com https://s.ytimg.com 'unsafe-eval'"`
+
+In `/etc/apache2/conf-available/shaarli-csp.conf`:
+
+```apache
+<Directory /path/to/shaarli>
+ Header set Content-Security-Policy "script-src 'self' 'unsafe-inline' https://www.youtube.com https://s.ytimg.com 'unsafe-eval'"
+</Directory>
+```
+
+Then run `a2enconf shaarli-csp; service apache2 reload`
### License
```
THE SOFTWARE.
----------------------------------------------------
-```
\ No newline at end of file
+```
$qrcode_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/qrcode/qrcode.html');
foreach ($data['links'] as &$value) {
- $qrcode = sprintf($qrcode_html, $value['url'], $value['url'], PluginManager::$PLUGINS_PATH);
+ $qrcode = sprintf($qrcode_html, $value['real_url'], $value['real_url'], PluginManager::$PLUGINS_PATH);
$value['link_plugin'][] = $qrcode;
}
-<?php /* 0.6.0 */ ?>
+<?php /* 0.6.1 */ ?>
require_once 'application/ApplicationUtils.php';
+/**
+ * Fake ApplicationUtils class to avoid HTTP requests
+ */
+class FakeApplicationUtils extends ApplicationUtils
+{
+ public static $VERSION_CODE = '';
+
+ /**
+ * Toggle HTTP requests, allow overriding the version code
+ */
+ public static function getLatestGitVersionCode($url, $timeout=0)
+ {
+ return self::$VERSION_CODE;
+ }
+}
+
/**
* Unitary tests for Shaarli utilities
*/
class ApplicationUtilsTest extends PHPUnit_Framework_TestCase
{
+ protected static $testUpdateFile = 'sandbox/update.txt';
+ protected static $testVersion = '0.5.0';
+ protected static $versionPattern = '/^\d+\.\d+\.\d+$/';
+
+ /**
+ * Reset test data for each test
+ */
+ public function setUp()
+ {
+ FakeApplicationUtils::$VERSION_CODE = '';
+ if (file_exists(self::$testUpdateFile)) {
+ unlink(self::$testUpdateFile);
+ }
+ }
+
+ /**
+ * Retrieve the latest version code available on Git
+ *
+ * Expected format: Semantic Versioning - major.minor.patch
+ */
+ public function testGetLatestGitVersionCode()
+ {
+ $testTimeout = 10;
+
+ $this->assertEquals(
+ '0.5.4',
+ ApplicationUtils::getLatestGitVersionCode(
+ 'https://raw.githubusercontent.com/shaarli/Shaarli/'
+ .'v0.5.4/shaarli_version.php',
+ $testTimeout
+ )
+ );
+ $this->assertRegexp(
+ self::$versionPattern,
+ ApplicationUtils::getLatestGitVersionCode(
+ 'https://raw.githubusercontent.com/shaarli/Shaarli/'
+ .'master/shaarli_version.php',
+ $testTimeout
+ )
+ );
+ }
+
+ /**
+ * Attempt to retrieve the latest version from an invalid URL
+ */
+ public function testGetLatestGitVersionCodeInvalidUrl()
+ {
+ $this->assertFalse(
+ ApplicationUtils::getLatestGitVersionCode('htttp://null.io', 1)
+ );
+ }
+
+ /**
+ * Test update checks - the user is logged off
+ */
+ public function testCheckUpdateLoggedOff()
+ {
+ $this->assertFalse(
+ ApplicationUtils::checkUpdate(self::$testVersion, 'null', 0, false, false)
+ );
+ }
+
+ /**
+ * Test update checks - the user has disabled updates
+ */
+ public function testCheckUpdateUserDisabled()
+ {
+ $this->assertFalse(
+ ApplicationUtils::checkUpdate(self::$testVersion, 'null', 0, false, true)
+ );
+ }
+
+ /**
+ * A newer version is available
+ */
+ public function testCheckUpdateNewVersionAvailable()
+ {
+ $newVersion = '1.8.3';
+ FakeApplicationUtils::$VERSION_CODE = $newVersion;
+
+ $version = FakeApplicationUtils::checkUpdate(
+ self::$testVersion,
+ self::$testUpdateFile,
+ 100,
+ true,
+ true
+ );
+
+ $this->assertEquals($newVersion, $version);
+ }
+
+ /**
+ * No available information about versions
+ */
+ public function testCheckUpdateNewVersionUnavailable()
+ {
+ $version = FakeApplicationUtils::checkUpdate(
+ self::$testVersion,
+ self::$testUpdateFile,
+ 100,
+ true,
+ true
+ );
+
+ $this->assertFalse($version);
+ }
+
+ /**
+ * Test update checks - invalid Git branch
+ * @expectedException Exception
+ * @expectedExceptionMessageRegExp /Invalid branch selected for updates/
+ */
+ public function testCheckUpdateInvalidGitBranch()
+ {
+ ApplicationUtils::checkUpdate('', 'null', 0, true, true, 'unstable');
+ }
+
+ /**
+ * Shaarli is up-to-date
+ */
+ public function testCheckUpdateNewVersionUpToDate()
+ {
+ FakeApplicationUtils::$VERSION_CODE = self::$testVersion;
+
+ $version = FakeApplicationUtils::checkUpdate(
+ self::$testVersion,
+ self::$testUpdateFile,
+ 100,
+ true,
+ true
+ );
+
+ $this->assertFalse($version);
+ }
+
+ /**
+ * Time-traveller's Shaarli
+ */
+ public function testCheckUpdateNewVersionMaartiMcFly()
+ {
+ FakeApplicationUtils::$VERSION_CODE = '0.4.1';
+
+ $version = FakeApplicationUtils::checkUpdate(
+ self::$testVersion,
+ self::$testUpdateFile,
+ 100,
+ true,
+ true
+ );
+
+ $this->assertFalse($version);
+ }
+
+ /**
+ * The version has been checked recently and Shaarli is up-to-date
+ */
+ public function testCheckUpdateNewVersionTwiceUpToDate()
+ {
+ FakeApplicationUtils::$VERSION_CODE = self::$testVersion;
+
+ // Create the update file
+ $version = FakeApplicationUtils::checkUpdate(
+ self::$testVersion,
+ self::$testUpdateFile,
+ 100,
+ true,
+ true
+ );
+
+ $this->assertFalse($version);
+
+ // Reuse the update file
+ $version = FakeApplicationUtils::checkUpdate(
+ self::$testVersion,
+ self::$testUpdateFile,
+ 100,
+ true,
+ true
+ );
+
+ $this->assertFalse($version);
+ }
+
+ /**
+ * The version has been checked recently and Shaarli is outdated
+ */
+ public function testCheckUpdateNewVersionTwiceOutdated()
+ {
+ $newVersion = '1.8.3';
+ FakeApplicationUtils::$VERSION_CODE = $newVersion;
+
+ // Create the update file
+ $version = FakeApplicationUtils::checkUpdate(
+ self::$testVersion,
+ self::$testUpdateFile,
+ 100,
+ true,
+ true
+ );
+ $this->assertEquals($newVersion, $version);
+
+ // Reuse the update file
+ $version = FakeApplicationUtils::checkUpdate(
+ self::$testVersion,
+ self::$testUpdateFile,
+ 100,
+ true,
+ true
+ );
+ $this->assertEquals($newVersion, $version);
+ }
+
/**
* Check supported PHP versions
*/
/**
* Unitary tests for cached pages
*/
-class CachedTest extends PHPUnit_Framework_TestCase
+class CacheTest extends PHPUnit_Framework_TestCase
{
// test cache directory
- protected static $testCacheDir = 'tests/dummycache';
+ protected static $testCacheDir = 'sandbox/dummycache';
// dummy cached file names / content
protected static $pages = array('a', 'toto', 'd7b59c');
} else {
array_map('unlink', glob(self::$testCacheDir.'/*'));
}
-
+
foreach (self::$pages as $page) {
file_put_contents(self::$testCacheDir.'/'.$page.'.cache', $page);
}
file_put_contents(self::$testCacheDir.'/intru.der', 'ShouldNotBeThere');
}
+ /**
+ * Remove dummycache folder after each tests.
+ */
+ public function tearDown()
+ {
+ array_map('unlink', glob(self::$testCacheDir.'/*'));
+ rmdir(self::$testCacheDir);
+ }
+
/**
* Purge cached pages
*/
public function testPurgeCachedPagesMissingDir()
{
$this->assertEquals(
- 'Cannot purge tests/dummycache_missing: no directory',
+ 'Cannot purge sandbox/dummycache_missing: no directory',
purgeCachedPages(self::$testCacheDir.'_missing')
);
}
class CachedPageTest extends PHPUnit_Framework_TestCase
{
// test cache directory
- protected static $testCacheDir = 'tests/pagecache';
+ protected static $testCacheDir = 'sandbox/pagecache';
protected static $url = 'http://shaar.li/?do=atom';
protected static $filename;
class LinkDBTest extends PHPUnit_Framework_TestCase
{
// datastore to test write operations
- protected static $testDatastore = 'tests/datastore.php';
+ protected static $testDatastore = 'sandbox/datastore.php';
protected static $refDB = null;
protected static $publicLinkDB = null;
protected static $privateLinkDB = null;
sizeof(self::$publicLinkDB->filterFullText('free software'))
);
}
+
+ /**
+ * Test real_url without redirector.
+ */
+ public function testLinkRealUrlWithoutRedirector()
+ {
+ $db = new LinkDB(self::$testDatastore, false, false);
+ foreach($db as $link) {
+ $this->assertEquals($link['url'], $link['real_url']);
+ }
+ }
+
+ /**
+ * Test real_url with redirector.
+ */
+ public function testLinkRealUrlWithRedirector()
+ {
+ $redirector = 'http://redirector.to?';
+ $db = new LinkDB(self::$testDatastore, false, false, $redirector);
+ foreach($db as $link) {
+ $this->assertStringStartsWith($redirector, $link['real_url']);
+ }
+ }
}
is_session_id_valid('c0ZqcWF3VFE2NmJBdm1HMVQ0ZHJ3UmZPbTFsNGhkNHI=')
);
}
+
+ /**
+ * Test text2clickable without a redirector being set.
+ */
+ public function testText2clickableWithoutRedirector()
+ {
+ $text = 'stuff http://hello.there/is=someone#here otherstuff';
+ $expectedText = 'stuff <a href="http://hello.there/is=someone#here">http://hello.there/is=someone#here</a> otherstuff';
+ $processedText = text2clickable($text, '');
+ $this->assertEquals($expectedText, $processedText);
+ }
+
+ /**
+ * Test text2clickable a redirector set.
+ */
+ public function testText2clickableWithRedirector()
+ {
+ $text = 'stuff http://hello.there/is=someone#here otherstuff';
+ $redirector = 'http://redirector.to';
+ $expectedText = 'stuff <a href="'.
+ $redirector .
+ urlencode('http://hello.there/is=someone#here') .
+ '">http://hello.there/is=someone#here</a> otherstuff';
+ $processedText = text2clickable($text, $redirector);
+ $this->assertEquals($expectedText, $processedText);
+ }
+
+ /**
+ * Test testSpace2nbsp.
+ */
+ public function testSpace2nbsp()
+ {
+ $text = ' Are you thrilled by flags ?'. PHP_EOL .' Really?';
+ $expectedText = ' Are you thrilled by flags ?'. PHP_EOL .' Really?';
+ $processedText = space2nbsp($text);
+ $this->assertEquals($expectedText, $processedText);
+ }
}
'title' => $str,
'links' => array(
array(
- 'url' => $str,
+ 'real_url' => $str,
)
)
);
$link = $data['links'][0];
// data shouldn't be altered
$this->assertEquals($str, $data['title']);
- $this->assertEquals($str, $link['url']);
+ $this->assertEquals($str, $link['real_url']);
// plugin data
$this->assertEquals(1, count($link['link_plugin']));
</div>
{/if}
<div class="dailyEntryTitle">
- <a href="{$link.url}">{$link.title}</a>
+ <a href="{$link.real_url}">{$link.title}</a>
</div>
{if="$link.thumbnail"}
<div class="dailyEntryThumbnail">{$link.thumbnail}</div>
</form>
</div>
{/if}
- <span class="linktitle"><a href="{$redirector}{$value.url}">{$value.title}</a></span>
+ <span class="linktitle">
+ <a href="{$value.real_url}">{$value.title}</a>
+ </span>
<br>
{if="$value.description"}<div class="linkdescription">{$value.description}</div>{/if}
{if="!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()"}
<span>{$value}</span> -
{/loop}
- <a href="{$value.url}"><span class="linkurl" title="Short link">{$value.url}</span></a><br>
+ <a href="{$value.real_url}"><span class="linkurl" title="Short link">{$value.url}</span></a><br>
{if="$value.tags"}
<div class="linktaglist">
{loop="value.taglist"}<span class="linktag" title="Add tag"><a href="?addtag={$value|urlencode}">{$value}</a></span> {/loop}
{$value}
{/loop}
</div>
-{if="$newversion"}
- <div id="newversion"><span id="version_id">●</span> Shaarli {$newversion} is <a href="https://github.com/shaarli/Shaarli/releases">available</a>.</div>
+{if="$newVersion"}
+<div id="newversion">
+ <span id="version_id">●</span> Shaarli {$newVersion} is
+ <a href="https://github.com/shaarli/Shaarli/releases">available</a>.
+</div>
+{/if}
+{if="$versionError"}
+<div id="newversion">
+ Error: {$versionError}
+</div>
{/if}
{if="isLoggedIn()"}
<script>function confirmDeleteLink() { var agree=confirm("Are you sure you want to delete this link ?"); if (agree) return true ; else return false ; }</script>
<div id="picwall_container">
{loop="linksToDisplay"}
<div class="picwall_pictureframe">
- {$value.thumbnail}<a href="{$value.url}"><span class="info">{$value.title}</span></a>
+ {$value.thumbnail}<a href="{$value.real_url}"><span class="info">{$value.title}</span></a>
{loop="$value.picwall_plugin"}
{$value}
{/loop}