aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md11
-rw-r--r--application/ApplicationUtils.php45
-rw-r--r--application/PageBuilder.php3
-rw-r--r--application/Updater.php60
-rw-r--r--application/config/ConfigJson.php10
-rw-r--r--application/config/ConfigManager.php8
-rw-r--r--index.php7
-rw-r--r--shaarli_version.php2
-rw-r--r--tests/ApplicationUtilsTest.php47
-rw-r--r--tests/Updater/UpdaterTest.php45
-rw-r--r--tests/config/ConfigJsonTest.php2
-rw-r--r--tpl/default/page.header.html4
12 files changed, 211 insertions, 33 deletions
diff --git a/README.md b/README.md
index db1b901f..cfdefc66 100644
--- a/README.md
+++ b/README.md
@@ -6,13 +6,18 @@ _Do you want to share the links you discover?_
6_Shaarli is a minimalist delicious clone that you can install on your own server._ 6_Shaarli is a minimalist delicious clone that you can install on your own server._
7_It is designed to be personal (single-user), fast and handy._ 7_It is designed to be personal (single-user), fast and handy._
8 8
9[![](https://img.shields.io/travis/shaarli/Shaarli.svg?label=master)](https://travis-ci.org/shaarli/Shaarli) 9[![](https://img.shields.io/badge/stable-v0.7.1-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.7.1)
10[![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli) 10[![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli)
11[![](https://img.shields.io/github/release/shaarli/shaarli.svg)](https://github.com/shaarli/Shaarli/releases/latest/) 11•
12[![Docker repository](https://img.shields.io/docker/pulls/shaarli/shaarli.svg)](https://hub.docker.com/r/shaarli/shaarli/) 12[![](https://img.shields.io/badge/latest-v0.8.4-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.8.4)
13[![](https://img.shields.io/travis/shaarli/Shaarli/latest.svg?label=latest)](https://travis-ci.org/shaarli/Shaarli)
14•
15[![](https://img.shields.io/badge/master-v0.9.x-blue.svg)](https://github.com/shaarli/Shaarli)
16[![](https://img.shields.io/travis/shaarli/Shaarli.svg?label=master)](https://travis-ci.org/shaarli/Shaarli)
13 17
14[![Join the chat at https://gitter.im/shaarli/Shaarli](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/shaarli/Shaarli) 18[![Join the chat at https://gitter.im/shaarli/Shaarli](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/shaarli/Shaarli)
15[![Bountysource](https://www.bountysource.com/badge/team?team_id=19583&style=bounties_received)](https://www.bountysource.com/teams/shaarli/issues) 19[![Bountysource](https://www.bountysource.com/badge/team?team_id=19583&style=bounties_received)](https://www.bountysource.com/teams/shaarli/issues)
20[![Docker repository](https://img.shields.io/docker/pulls/shaarli/shaarli.svg)](https://hub.docker.com/r/shaarli/shaarli/)
16 21
17## Quickstart 22## Quickstart
18- [Wiki/documentation](https://github.com/shaarli/Shaarli/wiki) 23- [Wiki/documentation](https://github.com/shaarli/Shaarli/wiki)
diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php
index a0f482b0..85dcbeeb 100644
--- a/application/ApplicationUtils.php
+++ b/application/ApplicationUtils.php
@@ -4,9 +4,13 @@
4 */ 4 */
5class ApplicationUtils 5class ApplicationUtils
6{ 6{
7 /**
8 * @var string File containing the current version
9 */
10 public static $VERSION_FILE = 'shaarli_version.php';
11
7 private static $GIT_URL = 'https://raw.githubusercontent.com/shaarli/Shaarli'; 12 private static $GIT_URL = 'https://raw.githubusercontent.com/shaarli/Shaarli';
8 private static $GIT_BRANCHES = array('master', 'stable'); 13 private static $GIT_BRANCHES = array('latest', 'stable');
9 private static $VERSION_FILE = 'shaarli_version.php';
10 private static $VERSION_START_TAG = '<?php /* '; 14 private static $VERSION_START_TAG = '<?php /* ';
11 private static $VERSION_END_TAG = ' */ ?>'; 15 private static $VERSION_END_TAG = ' */ ?>';
12 16
@@ -29,6 +33,30 @@ class ApplicationUtils
29 return false; 33 return false;
30 } 34 }
31 35
36 return $data;
37 }
38
39 /**
40 * Retrieve the version from a remote URL or a file.
41 *
42 * @param string $remote URL or file to fetch.
43 * @param int $timeout For URLs fetching.
44 *
45 * @return bool|string The version or false if it couldn't be retrieved.
46 */
47 public static function getVersion($remote, $timeout = 2)
48 {
49 if (startsWith($remote, 'http')) {
50 if (($data = static::getLatestGitVersionCode($remote, $timeout)) === false) {
51 return false;
52 }
53 } else {
54 if (! is_file($remote)) {
55 return false;
56 }
57 $data = file_get_contents($remote);
58 }
59
32 return str_replace( 60 return str_replace(
33 array(self::$VERSION_START_TAG, self::$VERSION_END_TAG, PHP_EOL), 61 array(self::$VERSION_START_TAG, self::$VERSION_END_TAG, PHP_EOL),
34 array('', '', ''), 62 array('', '', ''),
@@ -65,13 +93,10 @@ class ApplicationUtils
65 $isLoggedIn, 93 $isLoggedIn,
66 $branch='stable') 94 $branch='stable')
67 { 95 {
68 if (! $isLoggedIn) { 96 // Do not check versions for visitors
69 // Do not check versions for visitors 97 // Do not check if the user doesn't want to
70 return false; 98 // Do not check with dev version
71 } 99 if (! $isLoggedIn || empty($enableCheck) || $currentVersion === 'dev') {
72
73 if (empty($enableCheck)) {
74 // Do not check if the user doesn't want to
75 return false; 100 return false;
76 } 101 }
77 102
@@ -93,7 +118,7 @@ class ApplicationUtils
93 118
94 // Late Static Binding allows overriding within tests 119 // Late Static Binding allows overriding within tests
95 // See http://php.net/manual/en/language.oop5.late-static-bindings.php 120 // See http://php.net/manual/en/language.oop5.late-static-bindings.php
96 $latestVersion = static::getLatestGitVersionCode( 121 $latestVersion = static::getVersion(
97 self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE 122 self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE
98 ); 123 );
99 124
diff --git a/application/PageBuilder.php b/application/PageBuilder.php
index 544aba7c..b133dee8 100644
--- a/application/PageBuilder.php
+++ b/application/PageBuilder.php
@@ -75,7 +75,8 @@ class PageBuilder
75 } 75 }
76 $this->tpl->assign('shaarlititle', $this->conf->get('general.title', 'Shaarli')); 76 $this->tpl->assign('shaarlititle', $this->conf->get('general.title', 'Shaarli'));
77 $this->tpl->assign('openshaarli', $this->conf->get('security.open_shaarli', false)); 77 $this->tpl->assign('openshaarli', $this->conf->get('security.open_shaarli', false));
78 $this->tpl->assign('showatom', $this->conf->get('feed.show_atom', false)); 78 $this->tpl->assign('showatom', $this->conf->get('feed.show_atom', true));
79 $this->tpl->assign('feed_type', $this->conf->get('feed.show_atom', true) !== false ? 'atom' : 'rss');
79 $this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false)); 80 $this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false));
80 $this->tpl->assign('token', getToken($this->conf)); 81 $this->tpl->assign('token', getToken($this->conf));
81 // To be removed with a proper theme configuration. 82 // To be removed with a proper theme configuration.
diff --git a/application/Updater.php b/application/Updater.php
index fd7e2073..0fb68c5a 100644
--- a/application/Updater.php
+++ b/application/Updater.php
@@ -378,6 +378,66 @@ class Updater
378 378
379 $this->conf->set('plugins.PIWIK_URL', 'http://'. $this->conf->get('plugins.PIWIK_URL')); 379 $this->conf->set('plugins.PIWIK_URL', 'http://'. $this->conf->get('plugins.PIWIK_URL'));
380 $this->conf->write($this->isLoggedIn); 380 $this->conf->write($this->isLoggedIn);
381
382 return true;
383 }
384
385 /**
386 * Use ATOM feed as default.
387 */
388 public function updateMethodAtomDefault()
389 {
390 if (!$this->conf->exists('feed.show_atom') || $this->conf->get('feed.show_atom') === true) {
391 return true;
392 }
393
394 $this->conf->set('feed.show_atom', true);
395 $this->conf->write($this->isLoggedIn);
396
397 return true;
398 }
399
400 /**
401 * Update updates.check_updates_branch setting.
402 *
403 * If the current major version digit matches the latest branch
404 * major version digit, we set the branch to `latest`,
405 * otherwise we'll check updates on the `stable` branch.
406 *
407 * No update required for the dev version.
408 *
409 * Note: due to hardcoded URL and lack of dependency injection, this is not unit testable.
410 *
411 * FIXME! This needs to be removed when we switch to first digit major version
412 * instead of the second one since the versionning process will change.
413 */
414 public function updateMethodCheckUpdateRemoteBranch()
415 {
416 if (shaarli_version === 'dev' || $this->conf->get('updates.check_updates_branch') === 'latest') {
417 return true;
418 }
419
420 // Get latest branch major version digit
421 $latestVersion = ApplicationUtils::getLatestGitVersionCode(
422 'https://raw.githubusercontent.com/shaarli/Shaarli/latest/shaarli_version.php',
423 5
424 );
425 if (preg_match('/(\d+)\.\d+$/', $latestVersion, $matches) === false) {
426 return false;
427 }
428 $latestMajor = $matches[1];
429
430 // Get current major version digit
431 preg_match('/(\d+)\.\d+$/', shaarli_version, $matches);
432 $currentMajor = $matches[1];
433
434 if ($currentMajor === $latestMajor) {
435 $branch = 'latest';
436 } else {
437 $branch = 'stable';
438 }
439 $this->conf->set('updates.check_updates_branch', $branch);
440 $this->conf->write($this->isLoggedIn);
381 return true; 441 return true;
382 } 442 }
383} 443}
diff --git a/application/config/ConfigJson.php b/application/config/ConfigJson.php
index 30908d90..9ef2ef56 100644
--- a/application/config/ConfigJson.php
+++ b/application/config/ConfigJson.php
@@ -21,8 +21,14 @@ class ConfigJson implements ConfigIO
21 $data = str_replace(self::getPhpSuffix(), '', $data); 21 $data = str_replace(self::getPhpSuffix(), '', $data);
22 $data = json_decode($data, true); 22 $data = json_decode($data, true);
23 if ($data === null) { 23 if ($data === null) {
24 $error = json_last_error(); 24 $errorCode = json_last_error();
25 throw new \Exception('An error occurred while parsing JSON file: error code #'. $error); 25 $error = 'An error occurred while parsing JSON configuration file ('. $filepath .'): error code #';
26 $error .= $errorCode. '<br>➜ <code>' . json_last_error_msg() .'</code>';
27 if ($errorCode === JSON_ERROR_SYNTAX) {
28 $error .= '<br>Please check your JSON syntax (without PHP comment tags) using a JSON lint tool such as ';
29 $error .= '<a href="http://jsonlint.com/">jsonlint.com</a>.';
30 }
31 throw new \Exception($error);
26 } 32 }
27 return $data; 33 return $data;
28 } 34 }
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php
index f2097410..7bfbfc72 100644
--- a/application/config/ConfigManager.php
+++ b/application/config/ConfigManager.php
@@ -81,7 +81,11 @@ class ConfigManager
81 */ 81 */
82 protected function load() 82 protected function load()
83 { 83 {
84 $this->loadedConfig = $this->configIO->read($this->getConfigFileExt()); 84 try {
85 $this->loadedConfig = $this->configIO->read($this->getConfigFileExt());
86 } catch (\Exception $e) {
87 die($e->getMessage());
88 }
85 $this->setDefaultValues(); 89 $this->setDefaultValues();
86 } 90 }
87 91
@@ -317,7 +321,7 @@ class ConfigManager
317 $this->setEmpty('updates.check_updates_interval', 86400); 321 $this->setEmpty('updates.check_updates_interval', 86400);
318 322
319 $this->setEmpty('feed.rss_permalinks', true); 323 $this->setEmpty('feed.rss_permalinks', true);
320 $this->setEmpty('feed.show_atom', false); 324 $this->setEmpty('feed.show_atom', true);
321 325
322 $this->setEmpty('privacy.default_private_links', false); 326 $this->setEmpty('privacy.default_private_links', false);
323 $this->setEmpty('privacy.hide_public_links', false); 327 $this->setEmpty('privacy.hide_public_links', false);
diff --git a/index.php b/index.php
index cc7f3ca3..cf85197a 100644
--- a/index.php
+++ b/index.php
@@ -1,8 +1,6 @@
1<?php 1<?php
2/** 2/**
3 * Shaarli v0.8.3 - Shaare your links... 3 * Shaarli - The personal, minimalist, super-fast, database free, bookmarking service.
4 *
5 * The personal, minimalist, super-fast, database free, bookmarking service.
6 * 4 *
7 * Friendly fork by the Shaarli community: 5 * Friendly fork by the Shaarli community:
8 * - https://github.com/shaarli/Shaarli 6 * - https://github.com/shaarli/Shaarli
@@ -25,7 +23,6 @@ if (date_default_timezone_get() == '') {
25/* 23/*
26 * PHP configuration 24 * PHP configuration
27 */ 25 */
28define('shaarli_version', '0.8.2');
29 26
30// http://server.com/x/shaarli --> /shaarli/ 27// http://server.com/x/shaarli --> /shaarli/
31define('WEB_PATH', substr($_SERVER['REQUEST_URI'], 0, 1+strrpos($_SERVER['REQUEST_URI'], '/', 0))); 28define('WEB_PATH', substr($_SERVER['REQUEST_URI'], 0, 1+strrpos($_SERVER['REQUEST_URI'], '/', 0)));
@@ -90,6 +87,8 @@ try {
90 exit; 87 exit;
91} 88}
92 89
90define('shaarli_version', ApplicationUtils::getVersion(__DIR__ .'/'. ApplicationUtils::$VERSION_FILE));
91
93// Force cookie path (but do not change lifetime) 92// Force cookie path (but do not change lifetime)
94$cookie = session_get_cookie_params(); 93$cookie = session_get_cookie_params();
95$cookiedir = ''; 94$cookiedir = '';
diff --git a/shaarli_version.php b/shaarli_version.php
index 3f79a4c6..9167b43e 100644
--- a/shaarli_version.php
+++ b/shaarli_version.php
@@ -1 +1 @@
<?php /* 0.8.3 */ ?> <?php /* dev */ ?>
diff --git a/tests/ApplicationUtilsTest.php b/tests/ApplicationUtilsTest.php
index ad86e21c..ebdc365c 100644
--- a/tests/ApplicationUtilsTest.php
+++ b/tests/ApplicationUtilsTest.php
@@ -17,7 +17,7 @@ class FakeApplicationUtils extends ApplicationUtils
17 /** 17 /**
18 * Toggle HTTP requests, allow overriding the version code 18 * Toggle HTTP requests, allow overriding the version code
19 */ 19 */
20 public static function getLatestGitVersionCode($url, $timeout=0) 20 public static function getVersion($url, $timeout=0)
21 { 21 {
22 return self::$VERSION_CODE; 22 return self::$VERSION_CODE;
23 } 23 }
@@ -45,17 +45,27 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase
45 } 45 }
46 46
47 /** 47 /**
48 * Remove test version file if it exists
49 */
50 public function tearDown()
51 {
52 if (is_file('sandbox/version.php')) {
53 unlink('sandbox/version.php');
54 }
55 }
56
57 /**
48 * Retrieve the latest version code available on Git 58 * Retrieve the latest version code available on Git
49 * 59 *
50 * Expected format: Semantic Versioning - major.minor.patch 60 * Expected format: Semantic Versioning - major.minor.patch
51 */ 61 */
52 public function testGetLatestGitVersionCode() 62 public function testGetVersionCode()
53 { 63 {
54 $testTimeout = 10; 64 $testTimeout = 10;
55 65
56 $this->assertEquals( 66 $this->assertEquals(
57 '0.5.4', 67 '0.5.4',
58 ApplicationUtils::getLatestGitVersionCode( 68 ApplicationUtils::getVersion(
59 'https://raw.githubusercontent.com/shaarli/Shaarli/' 69 'https://raw.githubusercontent.com/shaarli/Shaarli/'
60 .'v0.5.4/shaarli_version.php', 70 .'v0.5.4/shaarli_version.php',
61 $testTimeout 71 $testTimeout
@@ -63,7 +73,7 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase
63 ); 73 );
64 $this->assertRegExp( 74 $this->assertRegExp(
65 self::$versionPattern, 75 self::$versionPattern,
66 ApplicationUtils::getLatestGitVersionCode( 76 ApplicationUtils::getVersion(
67 'https://raw.githubusercontent.com/shaarli/Shaarli/' 77 'https://raw.githubusercontent.com/shaarli/Shaarli/'
68 .'master/shaarli_version.php', 78 .'master/shaarli_version.php',
69 $testTimeout 79 $testTimeout
@@ -72,14 +82,26 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase
72 } 82 }
73 83
74 /** 84 /**
75 * Attempt to retrieve the latest version from an invalid URL 85 * Attempt to retrieve the latest version from an invalid File
86 */
87 public function testGetVersionCodeFromFile()
88 {
89 file_put_contents('sandbox/version.php', '<?php /* 1.2.3 */ ?>'. PHP_EOL);
90 $this->assertEquals(
91 '1.2.3',
92 ApplicationUtils::getVersion('sandbox/version.php', 1)
93 );
94 }
95
96 /**
97 * Attempt to retrieve the latest version from an invalid File
76 */ 98 */
77 public function testGetLatestGitVersionCodeInvalidUrl() 99 public function testGetVersionCodeInvalidFile()
78 { 100 {
79 $oldlog = ini_get('error_log'); 101 $oldlog = ini_get('error_log');
80 ini_set('error_log', '/dev/null'); 102 ini_set('error_log', '/dev/null');
81 $this->assertFalse( 103 $this->assertFalse(
82 ApplicationUtils::getLatestGitVersionCode('htttp://null.io', 1) 104 ApplicationUtils::getVersion('idontexist', 1)
83 ); 105 );
84 ini_set('error_log', $oldlog); 106 ini_set('error_log', $oldlog);
85 } 107 }
@@ -332,4 +354,15 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase
332 ApplicationUtils::checkResourcePermissions($conf) 354 ApplicationUtils::checkResourcePermissions($conf)
333 ); 355 );
334 } 356 }
357
358 /**
359 * Check update with 'dev' as curent version (master branch).
360 * It should always return false.
361 */
362 public function testCheckUpdateDev()
363 {
364 $this->assertFalse(
365 ApplicationUtils::checkUpdate('dev', self::$testUpdateFile, 100, true, true)
366 );
367 }
335} 368}
diff --git a/tests/Updater/UpdaterTest.php b/tests/Updater/UpdaterTest.php
index b522d616..11b6444a 100644
--- a/tests/Updater/UpdaterTest.php
+++ b/tests/Updater/UpdaterTest.php
@@ -615,4 +615,49 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
615 $this->assertTrue($updater->updateMethodPiwikUrl()); 615 $this->assertTrue($updater->updateMethodPiwikUrl());
616 $this->assertEquals($url, $this->conf->get('plugins.PIWIK_URL')); 616 $this->assertEquals($url, $this->conf->get('plugins.PIWIK_URL'));
617 } 617 }
618
619 /**
620 * Test updateMethodAtomDefault with show_atom set to false
621 * => update to true.
622 */
623 public function testUpdateMethodAtomDefault()
624 {
625 $sandboxConf = 'sandbox/config';
626 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
627 $this->conf = new ConfigManager($sandboxConf);
628 $this->conf->set('feed.show_atom', false);
629 $updater = new Updater([], [], $this->conf, true);
630 $this->assertTrue($updater->updateMethodAtomDefault());
631 $this->assertTrue($this->conf->get('feed.show_atom'));
632 // reload from file
633 $this->conf = new ConfigManager($sandboxConf);
634 $this->assertTrue($this->conf->get('feed.show_atom'));
635 }
636 /**
637 * Test updateMethodAtomDefault with show_atom not set.
638 * => nothing to do
639 */
640 public function testUpdateMethodAtomDefaultNoExist()
641 {
642 $sandboxConf = 'sandbox/config';
643 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
644 $this->conf = new ConfigManager($sandboxConf);
645 $updater = new Updater([], [], $this->conf, true);
646 $this->assertTrue($updater->updateMethodAtomDefault());
647 $this->assertTrue($this->conf->get('feed.show_atom'));
648 }
649 /**
650 * Test updateMethodAtomDefault with show_atom set to true.
651 * => nothing to do
652 */
653 public function testUpdateMethodAtomDefaultAlreadyTrue()
654 {
655 $sandboxConf = 'sandbox/config';
656 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
657 $this->conf = new ConfigManager($sandboxConf);
658 $this->conf->set('feed.show_atom', true);
659 $updater = new Updater([], [], $this->conf, true);
660 $this->assertTrue($updater->updateMethodAtomDefault());
661 $this->assertTrue($this->conf->get('feed.show_atom'));
662 }
618} 663}
diff --git a/tests/config/ConfigJsonTest.php b/tests/config/ConfigJsonTest.php
index 3527f83d..d237bc80 100644
--- a/tests/config/ConfigJsonTest.php
+++ b/tests/config/ConfigJsonTest.php
@@ -40,7 +40,7 @@ class ConfigJsonTest extends \PHPUnit_Framework_TestCase
40 * Read a non existent config file -> empty array. 40 * Read a non existent config file -> empty array.
41 * 41 *
42 * @expectedException \Exception 42 * @expectedException \Exception
43 * @expectedExceptionMessage An error occurred while parsing JSON file: error code #4 43 * @expectedExceptionMessageRegExp /An error occurred while parsing JSON configuration file \([\w\/\.]+\): error code #4/
44 */ 44 */
45 public function testReadInvalidJson() 45 public function testReadInvalidJson()
46 { 46 {
diff --git a/tpl/default/page.header.html b/tpl/default/page.header.html
index 03ca6843..9388ef79 100644
--- a/tpl/default/page.header.html
+++ b/tpl/default/page.header.html
@@ -48,7 +48,7 @@
48 </li> 48 </li>
49 {/loop} 49 {/loop}
50 <li class="pure-menu-item pure-u-lg-0"> 50 <li class="pure-menu-item pure-u-lg-0">
51 <a href="?do=atom{$searchcrits}" class="pure-menu-link">{'RSS Feed'|t}</a> 51 <a href="?do={$feed_type}{$searchcrits}" class="pure-menu-link">{'RSS Feed'|t}</a>
52 </li> 52 </li>
53 {if="isLoggedIn()"} 53 {if="isLoggedIn()"}
54 <li class="pure-menu-item pure-u-lg-0"> 54 <li class="pure-menu-item pure-u-lg-0">
@@ -70,7 +70,7 @@
70 </a> 70 </a>
71 </li> 71 </li>
72 <li class="pure-menu-item"> 72 <li class="pure-menu-item">
73 <a href="?do=atom{$searchcrits}" class="pure-menu-link" title="{'RSS Feed'|t}"> 73 <a href="?do={$feed_type}{$searchcrits}" class="pure-menu-link" title="{'RSS Feed'|t}">
74 <i class="fa fa-rss"></i> 74 <i class="fa fa-rss"></i>
75 </a> 75 </a>
76 </li> 76 </li>