From a79211d47fe4c5cf90ad9e2045c09cfdcd4f2345 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Sat, 6 Jul 2013 10:50:37 -0400 Subject: [PATCH] Improve items paging, get config values, add option to set the number of items per page and add comments --- assets/js/app.js | 10 +-- common.php | 5 +- cronjob.php | 4 +- index.php | 115 ++++++++++++++++++++---------- locales/de_DE/translations.php | 1 + locales/fr_FR/translations.php | 1 + model.php | 62 +++++++++++++--- schema.php | 8 ++- templates/app_header.php | 2 +- templates/bookmarks.php | 8 +-- templates/config.php | 3 + templates/confirm_remove_feed.php | 2 +- templates/feeds.php | 2 +- templates/history.php | 12 ++-- templates/unread_items.php | 12 ++-- 15 files changed, 166 insertions(+), 81 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 8679fe9..9843190 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -58,9 +58,9 @@ if (item) { var item_id = item.getAttribute("data-item-id"); - var redirect = item.getAttribute("data-item-page"); + var link = document.getElementById("bookmark-" + item_id); - window.location = "?action=bookmark&value=1&id=" + item_id + "&redirect=" + redirect; + if (link) link.click(); } } @@ -354,11 +354,7 @@ function is_listing() { - if (document.getElementById("listing")) { - - return true; - } - + if (document.getElementById("listing")) return true; return false; } diff --git a/common.php b/common.php index 1d0a00b..3e9ad9e 100644 --- a/common.php +++ b/common.php @@ -10,12 +10,9 @@ require 'schema.php'; require 'model.php'; -const DB_VERSION = 8; +const DB_VERSION = 9; const APP_VERSION = 'master'; -const APP_USERAGENT = 'Miniflux - http://miniflux.net'; const HTTP_TIMEOUT = 10; -const LIMIT_ALL = -1; -const ITEMS_PER_PAGE = 80; function get_db_filename() diff --git a/cronjob.php b/cronjob.php index b30183e..88baaba 100644 --- a/cronjob.php +++ b/cronjob.php @@ -15,11 +15,11 @@ else { $options = $_GET; } -$limit = ! empty($options['limit']) && ctype_digit($options['limit']) ? (int) $options['limit'] : LIMIT_ALL; +$limit = ! empty($options['limit']) && ctype_digit($options['limit']) ? (int) $options['limit'] : Model\LIMIT_ALL; $update_interval = ! empty($options['update-interval']) && ctype_digit($options['update-interval']) ? (int) $options['update-interval'] : null; $call_interval = ! empty($options['call-interval']) && ctype_digit($options['call-interval']) ? (int) $options['call-interval'] : null; -if ($update_interval !== null && $call_interval !== null && $limit === LIMIT_ALL && $update_interval >= $call_interval) { +if ($update_interval !== null && $call_interval !== null && $limit === Model\LIMIT_ALL && $update_interval >= $call_interval) { $feeds_count = \PicoTools\singleton('db')->table('feeds')->count(); $limit = ceil($feeds_count / ($update_interval / $call_interval)); diff --git a/index.php b/index.php index d2a8285..c6aa77d 100644 --- a/index.php +++ b/index.php @@ -18,6 +18,7 @@ use PicoTools\Template; Session\open(dirname($_SERVER['PHP_SELF'])); +// Called before each action Router\before(function($action) { if ($action !== 'login' && ! isset($_SESSION['user'])) { @@ -54,6 +55,7 @@ Router\before(function($action) { }); +// Logout and destroy session Router\get_action('logout', function() { Session\close(); @@ -61,12 +63,10 @@ Router\get_action('logout', function() { }); +// Display form login Router\get_action('login', function() { - if (isset($_SESSION['user'])) { - - Response\redirect('./index.php'); - } + if (isset($_SESSION['user'])) Response\redirect('index.php'); Response\html(Template\load('login', array( 'errors' => array(), @@ -75,15 +75,13 @@ Router\get_action('login', function() { }); +// Check credentials and redirect to unread items Router\post_action('login', function() { $values = Request\values(); list($valid, $errors) = Model\validate_login($values); - if ($valid) { - - Response\redirect('?action=default'); - } + if ($valid) Response\redirect('?action=unread'); Response\html(Template\load('login', array( 'errors' => $errors, @@ -92,6 +90,7 @@ Router\post_action('login', function() { }); +// Show item without bottom nav Router\get_action('show', function() { $id = Model\decode_item_id(Request\param('id')); @@ -105,6 +104,7 @@ Router\get_action('show', function() { }); +// Show item with bottom nav Router\get_action('read', function() { $id = Model\decode_item_id(Request\param('id')); @@ -121,30 +121,46 @@ Router\get_action('read', function() { }); +// Mark item as read and redirect to the listing page Router\get_action('mark-item-read', function() { $id = Model\decode_item_id(Request\param('id')); + $redirect = Request\param('redirect', 'unread'); + $offset = Request\int_param('offset', 0); + Model\set_item_read($id); - Response\Redirect('?action='.Request\param('redirect', 'default')); + + Response\Redirect('?action='.$redirect.'&offset='.$offset); }); +// Mark item as unread and redirect to the listing page Router\get_action('mark-item-unread', function() { $id = Model\decode_item_id(Request\param('id')); + $redirect = Request\param('redirect', 'history'); + $offset = Request\int_param('offset', 0); + Model\set_item_unread($id); - Response\Redirect('?action='.Request\param('redirect', 'history')); + + Response\Redirect('?action='.$redirect.'&offset='.$offset); }); +// Mark item as removed and redirect to the listing page Router\get_action('mark-item-removed', function() { $id = Model\decode_item_id(Request\param('id')); + $redirect = Request\param('redirect', 'history'); + $offset = Request\int_param('offset', 0); + Model\set_item_removed($id); - Response\Redirect('?action='.Request\param('redirect', 'history')); + + Response\Redirect('?action='.$redirect.'&offset='.$offset); }); +// Ajax call to mark item read Router\post_action('mark-item-read', function() { $id = Model\decode_item_id(Request\param('id')); @@ -153,6 +169,7 @@ Router\post_action('mark-item-read', function() { }); +// Ajax call to mark item unread Router\post_action('mark-item-unread', function() { $id = Model\decode_item_id(Request\param('id')); @@ -161,6 +178,7 @@ Router\post_action('mark-item-unread', function() { }); +// Ajax call to bookmark an item Router\post_action('bookmark-item', function() { $id = Model\decode_item_id(Request\param('id')); @@ -169,11 +187,25 @@ Router\post_action('bookmark-item', function() { }); +// Ajax call change item status +Router\post_action('change-item-status', function() { + + $id = Model\decode_item_id(Request\param('id')); + + Response\json(array( + 'item_id' => Model\encode_item_id($id), + 'status' => Model\switch_item_status($id) + )); +}); + + +// Add new bookmark Router\get_action('bookmark', function() { $param_id = Request\param('id'); $id = Model\decode_item_id($param_id); $redirect = Request\param('redirect', 'unread'); + $offset = Request\int_param('offset', 0); Model\set_bookmark_value($id, Request\int_param('value')); @@ -186,52 +218,46 @@ Router\get_action('bookmark', function() { Response\Redirect('?action=read&id='.$param_id); } - Response\Redirect('?action='.$redirect); -}); - - -Router\post_action('change-item-status', function() { - - $id = Model\decode_item_id(Request\param('id')); - - Response\json(array( - 'item_id' => Model\encode_item_id($id), - 'status' => Model\switch_item_status($id) - )); + Response\Redirect('?action='.$redirect.'&offset='.$offset); }); +// Display history page Router\get_action('history', function() { $offset = Request\int_param('offset', 0); $nb_items = Model\count_items('read'); Response\html(Template\layout('history', array( - 'items' => Model\get_read_items($offset, ITEMS_PER_PAGE), + 'items' => Model\get_read_items($offset, Model\get_config_value('items_per_page')), 'nb_items' => $nb_items, 'offset' => $offset, + 'items_per_page' => Model\get_config_value('items_per_page'), 'menu' => 'history', 'title' => t('History').' ('.$nb_items.')' ))); }); +// Display bookmarks page Router\get_action('bookmarks', function() { $offset = Request\int_param('offset', 0); $nb_items = Model\count_bookmarks(); Response\html(Template\layout('bookmarks', array( - 'items' => Model\get_bookmarks($offset, ITEMS_PER_PAGE), + 'items' => Model\get_bookmarks($offset, Model\get_config_value('items_per_page')), 'nb_items' => $nb_items, 'offset' => $offset, + 'items_per_page' => Model\get_config_value('items_per_page'), 'menu' => 'bookmarks', 'title' => t('Bookmarks').' ('.$nb_items.')' ))); }); -Router\get_action('confirm-remove', function() { +// Confirmation box to remove a feed +Router\get_action('confirm-remove-feed', function() { $id = Request\int_param('feed_id'); @@ -243,7 +269,8 @@ Router\get_action('confirm-remove', function() { }); -Router\get_action('remove', function() { +// Remove a feed +Router\get_action('remove-feed', function() { $id = Request\int_param('feed_id'); @@ -260,25 +287,22 @@ Router\get_action('remove', function() { }); +// Refresh one feed and redirect to unread items Router\get_action('refresh-feed', function() { $id = Request\int_param('feed_id'); - - if ($id) { - - Model\update_feed($id); - } + if ($id) Model\update_feed($id); Response\redirect('?action=unread'); }); +// Ajax call to refresh one feed Router\post_action('refresh-feed', function() { $id = Request\int_param('feed_id'); if ($id) { - Response\json(array('feed_id' => $id, 'result' => Model\update_feed($id))); } @@ -286,6 +310,7 @@ Router\post_action('refresh-feed', function() { }); +// Mark all unread items as read Router\get_action('mark-as-read', function() { Model\mark_as_read(); @@ -293,6 +318,7 @@ Router\get_action('mark-as-read', function() { }); +// Confirmation box to flush history Router\get_action('confirm-flush-history', function() { Response\html(Template\layout('confirm_flush_items', array( @@ -302,6 +328,7 @@ Router\get_action('confirm-flush-history', function() { }); +// Flush history Router\get_action('flush-history', function() { Model\mark_as_removed(); @@ -309,6 +336,7 @@ Router\get_action('flush-history', function() { }); +// Refresh all feeds, used when Javascript is disabled Router\get_action('refresh-all', function() { Model\update_feeds(); @@ -317,6 +345,7 @@ Router\get_action('refresh-all', function() { }); +// Display all feeds Router\get_action('feeds', function() { Response\html(Template\layout('feeds', array( @@ -328,6 +357,7 @@ Router\get_action('feeds', function() { }); +// Display form to add one feed Router\get_action('add', function() { Response\html(Template\layout('add', array( @@ -339,6 +369,7 @@ Router\get_action('add', function() { }); +// Add the feed Router\post_action('add', function() { if (Model\import_feed($_POST['url'])) { @@ -359,6 +390,7 @@ Router\post_action('add', function() { }); +// Optimize the database manually Router\get_action('optimize-db', function() { \PicoTools\singleton('db')->getConnection()->exec('VACUUM'); @@ -366,6 +398,7 @@ Router\get_action('optimize-db', function() { }); +// Download the compressed database Router\get_action('download-db', function() { Response\force_download('db.sqlite.gz'); @@ -373,6 +406,7 @@ Router\get_action('download-db', function() { }); +// OPML export Router\get_action('export', function() { Response\force_download('feeds.opml'); @@ -380,6 +414,7 @@ Router\get_action('export', function() { }); +// OPML import form Router\get_action('import', function() { Response\html(Template\layout('import', array( @@ -390,6 +425,7 @@ Router\get_action('import', function() { }); +// OPML importation Router\post_action('import', function() { if (Model\import_feeds(Request\file_content('file'))) { @@ -405,6 +441,7 @@ Router\post_action('import', function() { }); +// Display preferences page Router\get_action('config', function() { Response\html(Template\layout('config', array( @@ -413,12 +450,14 @@ Router\get_action('config', function() { 'db_size' => filesize(get_db_filename()), 'languages' => Model\get_languages(), 'autoflush_options' => Model\get_autoflush_options(), + 'paging_options' => Model\get_paging_options(), 'menu' => 'config', 'title' => t('Preferences') ))); }); +// Update preferences Router\post_action('config', function() { $values = Request\values() + array('nocontent' => 0); @@ -444,30 +483,30 @@ Router\post_action('config', function() { 'db_size' => filesize(get_db_filename()), 'languages' => Model\get_languages(), 'autoflush_options' => Model\get_autoflush_options(), + 'paging_options' => Model\get_paging_options(), 'menu' => 'config', 'title' => t('Preferences') ))); }); +// Display unread items Router\notfound(function() { Model\autoflush(); $offset = Request\int_param('offset', 0); - $items = Model\get_unread_items($offset, ITEMS_PER_PAGE); + $items = Model\get_unread_items($offset, Model\get_config_value('items_per_page')); $nb_items = Model\count_items('unread');; - if ($nb_items === 0) { - - Response\redirect('?action=feeds¬hing_to_read=1'); - } + if ($nb_items === 0) Response\redirect('?action=feeds¬hing_to_read=1'); Response\html(Template\layout('unread_items', array( 'items' => $items, 'nb_items' => $nb_items, 'nb_unread_items' => $nb_items, 'offset' => $offset, + 'items_per_page' => Model\get_config_value('items_per_page'), 'title' => 'miniflux ('.$nb_items.')', 'menu' => 'unread' ))); diff --git a/locales/de_DE/translations.php b/locales/de_DE/translations.php index 784d43e..2c018fa 100644 --- a/locales/de_DE/translations.php +++ b/locales/de_DE/translations.php @@ -1,6 +1,7 @@ 'Artikel pro Seite', 'Previous page' => 'Vorheriger Seite', 'Next page' => 'Nächster Seite', 'Do not fetch the content of articles' => 'Lade die Inhalte der Feeds nicht', diff --git a/locales/fr_FR/translations.php b/locales/fr_FR/translations.php index 94dd944..bca384a 100644 --- a/locales/fr_FR/translations.php +++ b/locales/fr_FR/translations.php @@ -1,6 +1,7 @@ 'Nombre d\'éléments par page', 'Previous page' => 'Page précédente', 'Next page' => 'Page suivante', 'Do not fetch the content of articles' => 'Ne pas récupérer le contenu des articles', diff --git a/model.php b/model.php index 0e43785..c3ee14a 100644 --- a/model.php +++ b/model.php @@ -13,6 +13,7 @@ require_once 'vendor/SimpleValidator/Validators/MaxLength.php'; require_once 'vendor/SimpleValidator/Validators/MinLength.php'; require_once 'vendor/SimpleValidator/Validators/Integer.php'; require_once 'vendor/SimpleValidator/Validators/Equals.php'; +require_once 'vendor/SimpleValidator/Validators/Integer.php'; use SimpleValidator\Validator; use SimpleValidator\Validators; @@ -21,6 +22,10 @@ use PicoFeed\Reader; use PicoFeed\Export; +const HTTP_USERAGENT = 'Miniflux - http://miniflux.net'; +const LIMIT_ALL = -1; + + function get_languages() { return array( @@ -43,6 +48,18 @@ function get_autoflush_options() } +function get_paging_options() +{ + return array( + 50 => 50, + 100 => 100, + 150 => 150, + 200 => 200, + 250 => 250, + ); +} + + function encode_item_id($input) { return strtr(base64_encode($input), '+/=', '-_,'); @@ -97,7 +114,7 @@ function import_feeds($content) function import_feed($url) { $reader = new Reader; - $resource = $reader->download($url, '', '', HTTP_TIMEOUT, APP_USERAGENT); + $resource = $reader->download($url, '', '', HTTP_TIMEOUT, HTTP_USERAGENT); $parser = $reader->getParser(); @@ -157,16 +174,13 @@ function update_feed($feed_id) $feed['last_modified'], $feed['etag'], HTTP_TIMEOUT, - APP_USERAGENT + HTTP_USERAGENT ); // Update the `last_checked` column each time, HTTP cache or not update_feed_last_checked($feed_id); - if (! $resource->isModified()) { - - return true; - } + if (! $resource->isModified()) return true; $parser = $reader->getParser(); @@ -437,7 +451,7 @@ function mark_as_removed() function autoflush() { - $autoflush = \PicoTools\singleton('db')->table('config')->findOneColumn('autoflush'); + $autoflush = get_config_value('autoflush'); if ($autoflush) { @@ -453,7 +467,7 @@ function autoflush() function update_items($feed_id, array $items) { - $nocontent = (bool) \PicoTools\singleton('db')->table('config')->findOneColumn('nocontent'); + $nocontent = (bool) get_config_value('nocontent'); $items_in_feed = array(); $db = \PicoTools\singleton('db'); @@ -501,11 +515,32 @@ function update_items($feed_id, array $items) } +function get_config_value($name) +{ + if (! isset($_SESSION)) { + + return \PicoTools\singleton('db')->table('config')->findOneColumn($name); + } + else { + + if (! isset($_SESSION['config'])) { + $_SESSION['config'] = get_config(); + } + + if (isset($_SESSION['config'][$name])) { + return $_SESSION['config'][$name]; + } + } + + return null; +} + + function get_config() { return \PicoTools\singleton('db') ->table('config') - ->columns('username', 'language', 'autoflush', 'nocontent') + ->columns('username', 'language', 'autoflush', 'nocontent', 'items_per_page') ->findOne(); } @@ -538,7 +573,9 @@ function validate_login(array $values) if ($user && \password_verify($values['password'], $user['password'])) { unset($user['password']); + $_SESSION['user'] = $user; + $_SESSION['config'] = get_config(); } else { @@ -565,7 +602,9 @@ function validate_config_update(array $values) new Validators\MinLength('password', t('The minimum length is 6 characters'), 6), new Validators\Required('confirmation', t('The confirmation is required')), new Validators\Equals('password', 'confirmation', t('Passwords doesn\'t match')), - new Validators\Required('autoflush', t('Value required')) + new Validators\Required('autoflush', t('Value required')), + new Validators\Required('items_per_page', t('Value required')), + new Validators\Integer('items_per_page', t('Must be an integer')), )); } else { @@ -596,6 +635,9 @@ function save_config(array $values) unset($values['confirmation']); + // Reload configuration in session + $_SESSION['config'] = $values; + $_SESSION['user']['language'] = $values['language']; unset($_COOKIE['language']); diff --git a/schema.php b/schema.php index 7c3be96..0373047 100644 --- a/schema.php +++ b/schema.php @@ -3,6 +3,12 @@ namespace Schema; +function version_9($pdo) +{ + $pdo->exec('ALTER TABLE config ADD COLUMN items_per_page INTEGER DEFAULT 100'); +} + + function version_8($pdo) { $pdo->exec('ALTER TABLE items ADD COLUMN bookmark INTEGER DEFAULT 0'); @@ -77,7 +83,7 @@ function version_1($pdo) title TEXT, author TEXT, content TEXT, - updated TEXT, + updated INTEGER, status TEXT, feed_id INTEGER, FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE diff --git a/templates/app_header.php b/templates/app_header.php index 0419eab..36c3da0 100644 --- a/templates/app_header.php +++ b/templates/app_header.php @@ -19,7 +19,7 @@