Big update

This commit is contained in:
Frederic Guillot 2013-03-17 18:16:25 -04:00
parent b1fdc0767a
commit f035f32967
65 changed files with 280 additions and 58 deletions

1
.gitignore vendored
View File

@ -1 +0,0 @@
src/data/

View File

@ -1,4 +1,4 @@
Miniflux - Minimalist Feed Reader
Miniflux - Minimalist News Reader
=================================
Miniflux is a minimalist web-based news reader.
@ -16,22 +16,22 @@ Features
- Protected by a login/password (only one possible user)
- Use secure headers (only external images are allowed)
- Open external links inside a new tab with a `rel="noreferrer"` attribute
- Mobile CSS (responsive design)
Todo
----
- Remove older items from the database
- Mobile CSS
Requirements
------------
- PHP >= 5.3
- XML extensions (SimpleXML, DOM...)
- Sqlite
- PHP XML extensions (SimpleXML, DOM...)
- PHP Sqlite extensions
Dependencies
------------
Libraries used
--------------
- [PicoFeed](https://github.com/fguillot/picoFeed)
- [PicoFarad](https://github.com/fguillot/picoFarad)
@ -42,4 +42,33 @@ Dependencies
Installation
------------
In progress...
1. You must have a web server with PHP installed (version 5.3 minimum) with the Sqlite and XML extensions
2. Download the source code and copy the directory miniflux where you want
3. Check if the directory data is writeable (Miniflux store everything inside a Sqlite database)
4. With your browser go to <http://yourpersonalserver/miniflux>
5. The default login and password is admin/admin
6. Start to use the software
FAQ
----
### How to update your feeds with a cronjob?
You just need to be inside the directory `miniflux` and run the script `cronjob.php`.
By example:
crontab -e
0 */4 * * * cd /path/to/miniflux && php cronjob.php >/dev/null 2>&1
### How Miniflux update my feeds from the user interface?
Miniflux use an Ajax request to refresh each subscription.
By default, there is only 5 feeds updated in parallel.
### I have 600 subscriptions, how Miniflux handle that?
Your life is cluttered.

View File

@ -1,3 +1,4 @@
figure,
li,
ul,
table,
@ -17,6 +18,8 @@ body {
max-width: 750px;
color: #333;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
a {
@ -49,6 +52,71 @@ h3 {
font-size: 1.2em;
}
blockquote {
border-left: 4px solid #ddd;
padding-left: 25px;
margin-left: 20px;
margin-top: 20px;
margin-bottom: 20px;
color: #777;
line-height: 1.4em;
font-family: Georgia, serif;
}
blockquote + p {
color: #555;
font-style: italic;
}
figcaption {
font-size: 0.8em;
text-transform: uppercase;
color: #777;
}
table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
}
table caption {
font-weight: bold;
font-size: 1.0em;
text-align: left;
padding-bottom: 0.5em;
padding-top: 0.5em;
}
th,
td {
border: 1px solid #ccc;
padding-top: 0.5em;
padding-bottom: 0.5em;
padding-left: 5px;
}
th {
text-align: left;
}
pre {
border: 1px solid #ccc;
border-radius: 10px;
background: #f0f0f0;
padding: 0.8em;
overflow: auto;
color: brown;
}
code {
color: brown;
}
caption code,
p code {
font-size: 1.1em;
}
/* forms */
form {
@ -63,6 +131,10 @@ label {
width: 10em;
}
input {
-webkit-appearance: none;
}
input[type="email"],
input[type="tel"],
input[type="password"],
@ -160,7 +232,7 @@ textarea.form-error {
/* buttons */
.btn {
display: block;
display: inline-block;
color: #333;
border: 1px solid #ccc;
background: #efefef;
@ -172,6 +244,24 @@ textarea.form-error {
border-radius: 2px;
}
a.btn {
text-decoration: none;
font-weight: bold;
}
.btn-red {
border-color: #b0281a;;
background: #d14836;
color: #fff;
}
a.btn-red:hover,
.btn-red:hover,
.btn-red:focus {
color: #fff;
background: #c53727;
}
.btn-blue {
border-color: #3079ed;
background: #4d90fe;
@ -237,6 +327,7 @@ nav .active a {
}
.page-header li {
font-size: 90%;
display: inline;
padding-left: 10px;
padding-right: 10px;
@ -256,10 +347,10 @@ nav .active a {
.items h2 {
font-size: 100%;
font-weight: bold;
margin: 0;
padding: 0;
padding-bottom: 2px;
font-weight: bold;
}
.items a {
@ -267,7 +358,7 @@ nav .active a {
}
.items a:hover,
.items :focus {
.items a:focus {
text-decoration: underline;
}
@ -280,14 +371,29 @@ nav .active a {
color: #aaa;
}
.items .preview {
color: #555;
line-height: 1.5em;
font-size: 100%;
font-family: Georgia, serif;
}
/* item */
.item {
font-size: 110%;
color: #444;
color: #333;
padding-bottom: 50px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}
.item p,
.item li {
font-family: Georgia, serif;
line-height: 1.6em;
}
.item h2,
.item h3 {
font-weight: bold;
}
.item pre,
@ -305,19 +411,6 @@ nav .active a {
margin-left: 25px;
}
.item li {
margin-top: 10px;
}
.item pre {
border: 1px solid #ccc;
border-radius: 10px;
background: #f0f0f0;
padding: 20px;
overflow: auto;
color: brown;
}
.item img {
display: block;
margin-top: 15px;
@ -325,29 +418,71 @@ nav .active a {
max-width: 100%;
}
.item code {
color: brown;
}
.infos {
padding-bottom: 30px;
padding-bottom: 20px;
color: #ccc;
}
.item h1 {
}
.item h1 a {
font-size: 150%;
font-size: 2.1em;
text-decoration: none;
}
blockquote {
border-left: 4px solid #ddd;
padding-left: 25px;
margin-left: 20px;
margin-top: 20px;
margin-bottom: 20px;
color: #666;
line-height: 22px;
.item a:visited {
color: purple;
}
/* mobile design */
@media only screen and (max-width: 480px) {
body {
margin-left: 5px;
margin-right: 5px;
max-width: 480px;
}
nav .active a {
font-weight: normal;
}
.logo {
display: block;
float: none;
}
header {
margin: 0;
width: 100%;
}
header ul {
text-align: left;
}
header li {
padding: 0;
width: 50%;
float: right;
display: block;
line-height: 25px;
}
.page {
clear: both;
padding-top: 20px;
}
.item {
font-size: 0.8em;
}
.item h1 {
font-size: 0.9em;
}
.item .infos {
font-size: 0.9em;
padding: 0;
}
}

View File

Before

Width:  |  Height:  |  Size: 847 B

After

Width:  |  Height:  |  Size: 847 B

View File

@ -56,7 +56,7 @@
if (! response.result) {
window.alert('Unable to refresh this feed: ' + feed_id);
//window.alert('Unable to refresh this feed: ' + feed_id);
}
}
catch (e) {}

1
miniflux/data/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!.gitignore

View File

@ -95,13 +95,28 @@ Router\get_action('history', function() {
});
Router\get_action('confirm-remove', function() {
$id = Request\int_param('feed_id');
Response\html(Template\layout('confirm_remove', array(
'feed' => Model\get_feed($id),
'menu' => 'feeds'
)));
});
Router\get_action('remove', function() {
$id = Request\int_param('feed_id');
if ($id) {
if ($id && Model\remove_feed($id)) {
Model\remove_feed($id);
Session\flash('This subscription has been removed successfully');
}
else {
Session\flash_error('Unable to remove this subscription');
}
Response\redirect('?action=feeds');
@ -160,6 +175,7 @@ Router\get_action('feeds', function() {
Response\html(Template\layout('feeds', array(
'feeds' => Model\get_feeds(),
'nothing_to_read' => Request\int_param('nothing_to_read'),
'menu' => 'feeds'
)));
});
@ -265,8 +281,15 @@ Router\post_action('config', function() {
Router\notfound(function() {
$items = Model\get_unread_items();
if (empty($items)) {
Response\redirect('?action=feeds&nothing_to_read=1');
}
Response\html(Template\layout('unread_items', array(
'items' => Model\get_unread_items(),
'items' => $items,
'menu' => 'unread'
)));
});

View File

@ -129,7 +129,7 @@ function get_unread_items()
{
return \PicoTools\singleton('db')
->table('items')
->columns('items.id', 'items.title', 'items.updated', 'items.url', 'feeds.site_url')
->columns('items.id', 'items.title', 'items.updated', 'items.url', 'feeds.site_url', 'items.content')
->join('feeds', 'id', 'feed_id')
->eq('status', 'unread')
->desc('updated')
@ -197,11 +197,6 @@ function update_feeds()
update_items($feed['id'], $parser->execute()->items);
}
else {
print_r($feed);
die;
}
}
}

View File

@ -0,0 +1,10 @@
<div class="page-header">
<h2>Confirmation</h2>
</div>
<p class="alert alert-info">Do you really want to remove this subscription: "<?= Helper\escape($feed['title']) ?>"?</p>
<div class="form-actions">
<a href="?action=remove&amp;feed_id=<?= $feed['id'] ?>" class="btn btn-red">Yes</a>
or <a href="?action=feeds">cancel</a>
</div>

View File

@ -14,6 +14,10 @@
<?php else: ?>
<?php if ($nothing_to_read): ?>
<p class="alert">Nothing to read, do you want to <a href="?action=refresh-all" data-action="refresh-all">update your subscriptions?</a></p>
<?php endif ?>
<section class="items">
<?php foreach ($feeds as $feed): ?>
<article>
@ -23,7 +27,8 @@
</h2>
<p>
<?= Helper\get_host_from_url($feed['site_url']) ?> |
<a href="?action=remove&amp;feed_id=<?= $feed['id'] ?>">remove</a> |
<a href="<?= Helper\escape($feed['feed_url']) ?>">feed link</a> |
<a href="?action=confirm-remove&amp;feed_id=<?= $feed['id'] ?>">remove</a> |
<a href="?action=refresh-feed&amp;feed_id=<?= $feed['id'] ?>" data-feed-id="<?= $feed['id'] ?>" data-action="refresh-feed">refresh</a>
</p>
</article>

View File

@ -1,6 +1,6 @@
<?php if (empty($items)): ?>
<p class="alert alert-info">No unread items.</p>
<p class="alert alert-info">Nothing to read.</p>
<?php else: ?>
@ -15,6 +15,9 @@
<?php foreach ($items as $item): ?>
<article>
<h2><a href="?action=read&amp;id=<?= urlencode($item['id']) ?>"><?= Helper\escape($item['title']) ?></a></h2>
<p class="preview">
<?= Helper\escape(Helper\summary(strip_tags($item['content']), 50, 300)) ?>
</p>
<p>
<?= Helper\get_host_from_url($item['url']) ?> |
<a href="<?= $item['url'] ?>" rel="noreferrer" target="_blank">direct link</a>

View File

@ -40,7 +40,12 @@ class Filter
'br' => array(),
'del' => array(),
'a' => array('href'),
'img' => array('src', 'width', 'height')
'img' => array('src'),
'figure' => array(),
'figcaption' => array(),
'cite' => array(),
'time' => array('datetime'),
'abbr' => array('title')
);
public $strip_tags_content = array(

View File

@ -55,6 +55,23 @@ function get_host_from_url($url)
}
function summary($value, $min_length = 5, $max_length = 120, $end = '[...]')
{
$length = strlen($value);
if ($length > $max_length) {
return substr($value, 0, strpos($value, ' ', $max_length)).' '.$end;
}
else if ($length < $min_length) {
return '';
}
return $value;
}
function in_list($id, array $listing)
{
if (isset($listing[$id])) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB