Compare commits

..

22 Commits

Author SHA1 Message Date
6a9e4bfc90 version number 2013-08-11 21:04:04 +02:00
15493df62d fixed #114 : minimum & maximum scale removed 2013-08-11 19:42:52 +02:00
82051a6a6a fixed #128: back to home after mark a link as read is fixed 2013-08-11 19:10:48 +02:00
5bf30b0060 layout 2013-08-10 22:17:00 +02:00
baea9b6de5 new logo 2013-08-10 21:39:11 +02:00
fa0483b361 closed #120: top link 2013-08-10 12:21:36 +02:00
b58e261db9 bug in downloading pictures : article content wasn't updated anymore 2013-08-09 23:30:20 +02:00
d8d1542e52 typo 2013-08-09 22:15:40 +02:00
56ab22d02c only display latest dev version if DEBUG is on 2013-08-09 13:05:35 +02:00
6c15854448 Closed #111 : test of install folder moved before 2013-08-09 11:47:37 +02:00
d91787589b fix #113 - reading time 2013-08-09 08:25:16 +02:00
3d8bded89e debug false 2013-08-08 21:28:37 +02:00
5f9bff0f71 some urls weren't well parsed 2013-08-08 21:13:37 +02:00
301e4c5acb installation 2013-08-08 18:41:40 +02:00
572e758bf2 empty phpunit file 2013-08-08 14:07:43 +02:00
64f8970215 travis img 2013-08-08 13:50:53 +02:00
a3436d4cba travis 2013-08-08 13:49:57 +02:00
07ee09f49a comments 2013-08-08 12:33:02 +02:00
25b5caeed1 ignore doc output 2013-08-08 09:57:18 +02:00
313f4ca785 ignore doc output 2013-08-08 09:55:04 +02:00
ed06f04077 test if /install exists 2013-08-08 09:11:12 +02:00
7aa8ccc47d preparing travis 2013-08-08 07:16:07 +02:00
23 changed files with 152 additions and 26 deletions

4
.gitignore vendored
View File

@ -1,3 +1,5 @@
vendor vendor
composer.phar composer.phar
db/poche.sqlite db/poche.sqlite
output
phpdoc*

15
.travis.yml Normal file
View File

@ -0,0 +1,15 @@
language: php
php:
- 5.4
branches:
only:
- dev
before_script:
- composer install
notifications:
email:
- nicolas.loeuillet@gmail.com

View File

@ -1,5 +1,5 @@
poche is based on : poche is based on :
* PHP Readability http://www.keyvan.net/2010/08/php-readability/ * PHP Readability https://bitbucket.org/fivefilters/php-readability
* Encoding https://github.com/neitanod/forceutf8 * Encoding https://github.com/neitanod/forceutf8
* logo by Brightmix http://www.iconfinder.com/icondetails/43256/128/jeans_monotone_pocket_icon * logo by Brightmix http://www.iconfinder.com/icondetails/43256/128/jeans_monotone_pocket_icon
* icons http://icomoon.io * icons http://icomoon.io

View File

@ -1,6 +1,6 @@
# Installing poche # Installing poche
Get the [latest dev version](https://github.com/inthepoche/poche/archive/dev.zip) of poche on github. Unzip it and upload it on your server. Get the [latest version](https://github.com/inthepoche/poche/archive/1.0-beta1.zip) of poche on github. Unzip it and upload it on your server.
your datas can be stored on sqlite, postgres or mysql databases. your datas can be stored on sqlite, postgres or mysql databases.

View File

@ -11,6 +11,10 @@ To get news from poche, [follow us on twitter](http://twitter.com/getpoche) or [
[![flattr](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/1265480/poche-a-read-it-later-open-source-system) [![flattr](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/1265480/poche-a-read-it-later-open-source-system)
## Installation
Read the INSTALL.md file.
## Security ## Security
You **have** to protect your db/poche.sqlite file. Modify the virtual host of your website to add this condition : You **have** to protect your db/poche.sqlite file. Modify the virtual host of your website to add this condition :
```apache ```apache
@ -31,6 +35,9 @@ location ~ /(db) {
## Usage ## Usage
See the documentation on our website : [inthepoche.com](http://inthepoche.com). See the documentation on our website : [inthepoche.com](http://inthepoche.com).
## Travis
[![Build Status](https://api.travis-ci.org/inthepoche/poche.png?branch=dev)](http://travis-ci.org/#!/inthepoche/poche)
## License ## License
Copyright © 2010-2013 Nicolas Lœuillet <nicolas.loeuillet@gmail.com> Copyright © 2010-2013 Nicolas Lœuillet <nicolas.loeuillet@gmail.com>
This work is free. You can redistribute it and/or modify it under the This work is free. You can redistribute it and/or modify it under the

11
TODO.md Normal file
View File

@ -0,0 +1,11 @@
# TODO
pouvoir annuler la suppression
conventions codage ? phing ? vérifier error_log qui trainent
phpDocumentor
minifier css
revoir tous les css
barre fixe d'admin sur la page d'un billet ?
revoir export (export vers pocket &cie ? )
raccourcis clavier
date d'ajout d'un lien

View File

@ -810,7 +810,8 @@ class Readability
return $this->grabArticle($this->body); return $this->grabArticle($this->body);
} }
else { else {
return false; # this line was commented by Nicolas Lœuillet 8/8/13 due to some urls not parsed
// return false;
} }
} }
return $articleContent; return $articleContent;

View File

@ -184,6 +184,13 @@ class Database {
return $entries; return $entries;
} }
public function updateContent($id, $content, $user_id) {
$sql_action = 'UPDATE entries SET content = ? WHERE id=? AND user_id=?';
$params_action = array($content, $id, $user_id);
$query = $this->executeQuery($sql_action, $params_action);
return $query;
}
public function add($url, $title, $content, $user_id) { public function add($url, $title, $content, $user_id) {
$sql_action = 'INSERT INTO entries ( url, title, content, user_id ) VALUES (?, ?, ?, ?)'; $sql_action = 'INSERT INTO entries ( url, title, content, user_id ) VALUES (?, ?, ?, ?)';
$params_action = array($url, $title, $content, $user_id); $params_action = array($url, $title, $content, $user_id);

View File

@ -18,6 +18,11 @@ class Poche
function __construct() function __construct()
{ {
if (file_exists('./install') && !DEBUG_POCHE) {
Tools::logm('folder /install exists');
die('To install your poche with sqlite, copy /install/poche.sqlite in /db and delete the folder /install. you have to delete the /install folder before using poche.');
}
$this->store = new Database(); $this->store = new Database();
$this->init(); $this->init();
$this->messages = new Messages(); $this->messages = new Messages();
@ -64,6 +69,10 @@ class Poche
$filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain'); $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain');
$this->tpl->addFilter($filter); $this->tpl->addFilter($filter);
# filter for reading time
$filter = new Twig_SimpleFilter('getReadingTime', 'Tools::getReadingTime');
$this->tpl->addFilter($filter);
# Pagination # Pagination
$this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p'); $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p');
} }
@ -117,6 +126,8 @@ class Poche
$last_id = $this->store->getLastId($sequence); $last_id = $this->store->getLastId($sequence);
if (DOWNLOAD_PICTURES) { if (DOWNLOAD_PICTURES) {
$content = filtre_picture($parametres_url['content'], $url->getUrl(), $last_id); $content = filtre_picture($parametres_url['content'], $url->getUrl(), $last_id);
Tools::logm('updating content article');
$this->store->updateContent($last_id, $content, $this->user->getId());
} }
if (!$import) { if (!$import) {
$this->messages->add('s', _('the link has been added successfully')); $this->messages->add('s', _('the link has been added successfully'));
@ -208,7 +219,7 @@ class Poche
); );
} }
else { else {
Tools::logm('error in view call : entry is NULL'); Tools::logm('error in view call : entry is null');
} }
break; break;
default: # home view default: # home view
@ -227,6 +238,13 @@ class Poche
return $tpl_vars; return $tpl_vars;
} }
/**
* update the password of the current user.
* if MODE_DEMO is TRUE, the password can't be updated.
* @todo add the return value
* @todo set the new password in function header like this updatePassword($newPassword)
* @return boolean
*/
public function updatePassword() public function updatePassword()
{ {
if (MODE_DEMO) { if (MODE_DEMO) {
@ -251,6 +269,13 @@ class Poche
} }
} }
/**
* checks if login & password are correct and save the user in session.
* it redirects the user to the $referer link
* @param string $referer the url to redirect after login
* @todo add the return value
* @return boolean
*/
public function login($referer) public function login($referer)
{ {
if (!empty($_POST['login']) && !empty($_POST['password'])) { if (!empty($_POST['login']) && !empty($_POST['password'])) {
@ -281,6 +306,11 @@ class Poche
} }
} }
/**
* log out the poche user. It cleans the session.
* @todo add the return value
* @return boolean
*/
public function logout() public function logout()
{ {
$this->user = array(); $this->user = array();
@ -290,6 +320,11 @@ class Poche
Tools::redirect(); Tools::redirect();
} }
/**
* import from Instapaper. poche needs a ./instapaper-export.html file
* @todo add the return value
* @return boolean
*/
private function importFromInstapaper() private function importFromInstapaper()
{ {
# TODO gestion des articles favs # TODO gestion des articles favs
@ -324,6 +359,11 @@ class Poche
Tools::redirect(); Tools::redirect();
} }
/**
* import from Pocket. poche needs a ./ril_export.html file
* @todo add the return value
* @return boolean
*/
private function importFromPocket() private function importFromPocket()
{ {
# TODO gestion des articles favs # TODO gestion des articles favs
@ -358,6 +398,11 @@ class Poche
Tools::redirect(); Tools::redirect();
} }
/**
* import from Readability. poche needs a ./readability file
* @todo add the return value
* @return boolean
*/
private function importFromReadability() private function importFromReadability()
{ {
# TODO gestion des articles lus / favs # TODO gestion des articles lus / favs
@ -393,19 +438,29 @@ class Poche
Tools::redirect(); Tools::redirect();
} }
/**
* import datas into your poche
* @param string $from name of the service to import : pocket, instapaper or readability
* @todo add the return value
* @return boolean
*/
public function import($from) public function import($from)
{ {
if ($from == 'pocket') { if ($from == 'pocket') {
$this->importFromPocket(); return $this->importFromPocket();
} }
else if ($from == 'readability') { else if ($from == 'readability') {
$this->importFromReadability(); return $this->importFromReadability();
} }
else if ($from == 'instapaper') { else if ($from == 'instapaper') {
$this->importFromInstapaper(); return $this->importFromInstapaper();
} }
} }
/**
* export poche entries in json
* @return json all poche entries
*/
public function export() public function export()
{ {
$entries = $this->store->retrieveAll($this->user->getId()); $entries = $this->store->retrieveAll($this->user->getId());
@ -415,9 +470,16 @@ class Poche
Tools::logm('export view'); Tools::logm('export view');
} }
/**
* Checks online the latest version of poche and cache it
* @param string $which 'prod' or 'dev'
* @return string latest $which version
*/
private function getPocheVersion($which = 'prod') private function getPocheVersion($which = 'prod')
{ {
$cache_file = CACHE . '/' . $which; $cache_file = CACHE . '/' . $which;
# checks if the cached version file exists
if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) { if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) {
$version = file_get_contents($cache_file); $version = file_get_contents($cache_file);
} else { } else {

View File

@ -170,6 +170,7 @@ class Tools
preg_match('#charset="?(.*)"#si', $meta[0], $encoding); preg_match('#charset="?(.*)"#si', $meta[0], $encoding);
# if charset is found set it otherwise, set it to utf-8 # if charset is found set it otherwise, set it to utf-8
$html_charset = (!empty($encoding[1])) ? strtolower($encoding[1]) : 'utf-8'; $html_charset = (!empty($encoding[1])) ? strtolower($encoding[1]) : 'utf-8';
if (empty($encoding[1])) $encoding[1] = 'utf-8';
} else { } else {
$html_charset = 'utf-8'; $html_charset = 'utf-8';
$encoding[1] = ''; $encoding[1] = '';
@ -223,4 +224,13 @@ class Tools
return FALSE; return FALSE;
} }
public static function getReadingTime($text) {
$word = str_word_count(strip_tags($text));
$minutes = floor($word / 200);
$seconds = floor($word % 200 / (200 / 60));
$time = array('minutes' => $minutes, 'seconds' => $seconds);
return $minutes;
}
} }

View File

@ -16,7 +16,7 @@ define ('STORAGE_SQLITE', './db/poche.sqlite');
define ('STORAGE_USER', 'postgres'); # leave blank for sqlite define ('STORAGE_USER', 'postgres'); # leave blank for sqlite
define ('STORAGE_PASSWORD', 'postgres'); # leave blank for sqlite define ('STORAGE_PASSWORD', 'postgres'); # leave blank for sqlite
define ('POCHE_VERSION', '1.0-beta1'); define ('POCHE_VERSION', '1.0-beta2');
define ('MODE_DEMO', FALSE); define ('MODE_DEMO', FALSE);
define ('DEBUG_POCHE', FALSE); define ('DEBUG_POCHE', FALSE);
define ('CONVERT_LINKS_FOOTNOTES', FALSE); define ('CONVERT_LINKS_FOOTNOTES', FALSE);

View File

@ -10,6 +10,7 @@
include dirname(__FILE__).'/inc/poche/config.inc.php'; include dirname(__FILE__).'/inc/poche/config.inc.php';
# Parse GET & REFERER vars
$referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']; $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
$view = Tools::checkVar('view', 'home'); $view = Tools::checkVar('view', 'home');
$action = Tools::checkVar('action'); $action = Tools::checkVar('action');
@ -17,6 +18,7 @@ $id = Tools::checkVar('id');
$_SESSION['sort'] = Tools::checkVar('sort', 'id'); $_SESSION['sort'] = Tools::checkVar('sort', 'id');
$url = new Url((isset ($_GET['url'])) ? $_GET['url'] : ''); $url = new Url((isset ($_GET['url'])) ? $_GET['url'] : '');
# poche actions
if (isset($_GET['login'])) { if (isset($_GET['login'])) {
# hello you # hello you
$poche->login($referer); $poche->login($referer);
@ -30,12 +32,13 @@ elseif (isset($_GET['config'])) {
$poche->updatePassword(); $poche->updatePassword();
} }
elseif (isset($_GET['import'])) { elseif (isset($_GET['import'])) {
$poche->import($_GET['from']); $import = $poche->import($_GET['from']);
} }
elseif (isset($_GET['export'])) { elseif (isset($_GET['export'])) {
$poche->export(); $poche->export();
} }
# vars to send to templates
$tpl_vars = array( $tpl_vars = array(
'referer' => $referer, 'referer' => $referer,
'view' => $view, 'view' => $view,
@ -57,5 +60,5 @@ else {
$messages = $poche->messages->display('all', FALSE); $messages = $poche->messages->display('all', FALSE);
$tpl_vars = array_merge($tpl_vars, array('messages' => $messages)); $tpl_vars = array_merge($tpl_vars, array('messages' => $messages));
# Aaaaaaand action ! # display poche
echo $poche->tpl->render($tpl_file, $tpl_vars); echo $poche->tpl->render($tpl_file, $tpl_vars);

View File

@ -1,5 +1,5 @@
<?php <?php
# import script to upgrade from poche 0.3
$db_path = 'sqlite:../db/poche.sqlite'; $db_path = 'sqlite:../db/poche.sqlite';
$handle = new PDO($db_path); $handle = new PDO($db_path);
$handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

0
phpunit.xml.dist Normal file
View File

View File

@ -6,4 +6,6 @@
<link rel="stylesheet" href="./tpl/css/style.css" media="all"> <link rel="stylesheet" href="./tpl/css/style.css" media="all">
<link rel="stylesheet" href="./tpl/css/style-{{ constant('THEME') }}.css" media="all" title="{{ constant('THEME') }} theme"> <link rel="stylesheet" href="./tpl/css/style-{{ constant('THEME') }}.css" media="all" title="{{ constant('THEME') }} theme">
<link rel="stylesheet" href="./tpl/css/messages.css" media="all"> <link rel="stylesheet" href="./tpl/css/messages.css" media="all">
<link href='http://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'> <link href='http://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
<script src="./tpl/js/jquery-2.0.3.min.js"></script>
<script type="text/javascript">$(document).ready(function(){$("body").prepend('<a href="#top" class="top_link" title="{% trans "back to top" %}"><img src="./tpl/img/{{ constant("THEME") }}/backtotop.png" alt={% trans "back to top" %}"/></a>');$(".top_link").css({position:"fixed",right:"15px",bottom:"15px",display:"none",padding:"20px",background:"#ccc","-moz-border-radius":"40px","-webkit-border-radius":"40px","border-radius":"40px",opacity:"0.9","z-index":"2000"});$(window).scroll(function(){posScroll=$(document).scrollTop();if(posScroll>=400)$(".top_link").fadeIn(600);else $(".top_link").fadeOut(600)})})</script>

View File

@ -20,7 +20,7 @@
<p><ul> <p><ul>
<li>{% trans "your version" %} : <strong>{{ constant('POCHE_VERSION') }}</strong></li> <li>{% trans "your version" %} : <strong>{{ constant('POCHE_VERSION') }}</strong></li>
<li>{% trans "latest stable version" %} : {{ prod }}. {% if compare_prod == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent stable version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li> <li>{% trans "latest stable version" %} : {{ prod }}. {% if compare_prod == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent stable version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li>
<li>{% trans "latest dev version" %} : {{ dev }}. {% if compare_dev == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent development version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li> {% if constant('DEBUG_POCHE') == 1 %}<li>{% trans "latest dev version" %} : {{ dev }}. {% if compare_dev == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent development version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li>{% endif %}
</ul> </ul>
</p> </p>

View File

@ -120,11 +120,6 @@ header h1 {
height: 16px; height: 16px;
} }
#main .entrie .url {
font-size: 13px;
}
/*** ***/ /*** ***/
/*** ARTICLE PAGE ***/ /*** ARTICLE PAGE ***/
@ -241,4 +236,8 @@ a, a:hover, a:visited {
footer { footer {
clear: both; clear: both;
}
.reading-time {
font-size: 0.8em;
} }

View File

@ -19,6 +19,7 @@
<a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li> <a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li>
<li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li> <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li>
<li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li> <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li>
<li class="reading-time">{{ entry.content| getReadingTime }} min</li>
</li> </li>
</ul> </ul>
<p>{{ entry.content|striptags|slice(0, 300) }}...</p> <p>{{ entry.content|striptags|slice(0, 300) }}...</p>

BIN
tpl/img/light/backtotop.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 B

After

Width:  |  Height:  |  Size: 1.3 KiB

6
tpl/js/jquery-2.0.3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -5,12 +5,12 @@
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]--> <!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0"> <meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=10"> <meta http-equiv="X-UA-Compatible" content="IE=10">
<title>{% block title %}{% endblock %} - poche</title> <title>{% block title %}{% endblock %} - poche</title>
{% include '_head.twig' %} {% include '_head.twig' %}
{% include '_bookmarklet.twig' %} {% include '_bookmarklet.twig' %}
</head> </head>
<body> <body>
{% include '_top.twig' %} {% include '_top.twig' %}
@ -24,6 +24,6 @@
{% block content %}{% endblock %} {% block content %}{% endblock %}
</div> </div>
</div> </div>
{% include '_footer.twig' %} {% include '_footer.twig' %}
</body> </body>
</html> </html>

View File

@ -5,7 +5,7 @@
<div class="tools"> <div class="tools">
<ul class="tools"> <ul class="tools">
<li> <li>
<li><a href="{{ referer }}" title="{% trans "back to home" %}" class="tool back"><span></span></a></li> <li><a href="./" title="{% trans "back to home" %}" class="tool back"><span></span></a></li>
<a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li> <a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li>
<li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li> <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li>
<li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li> <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li>
@ -25,8 +25,8 @@
<div class="tools"> <div class="tools">
<ul class="tools"> <ul class="tools">
<li> <li>
<li><a href="{{ referer }}" title="{% trans "back to home" %}" class="tool back"><span></span></a></li> <li><a href="./?" title="{% trans "back to home" %}" class="tool back"><span></span></a></li>
<li><a href="#" title="{% trans "back to top" %}" class="tool top"><span></span></a></li> <li><a href="#top" title="{% trans "back to top" %}" class="tool top"><span></span></a></li>
<a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li> <a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li>
<li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li> <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li>
<li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li> <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li>