Change the database structure to have a single database
This is a major change for the next release of Miniflux. - There is now only one database that can supports multiple users - There is no automated schema migration for this release - A migration procedure is available in the ChangeLog file
This commit is contained in:
parent
3e5a1bc524
commit
82df35a59b
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,6 +17,7 @@ Thumbs.db
|
||||
.nbproject
|
||||
nbproject
|
||||
config.php
|
||||
!app/helpers/*
|
||||
!app/models/*
|
||||
!app/controllers/*
|
||||
!app/templates/*
|
||||
|
@ -16,4 +16,4 @@ before_script:
|
||||
- composer install
|
||||
|
||||
script:
|
||||
- phpunit -c tests/phpunit.unit.xml
|
||||
- ./vendor/bin/phpunit -c tests/phpunit.unit.xml
|
||||
|
18
ChangeLog
Normal file
18
ChangeLog
Normal file
@ -0,0 +1,18 @@
|
||||
Version 1.2.0 (unreleased)
|
||||
-------------
|
||||
|
||||
* Major change to the database structure to have a single database for multiple users
|
||||
* Web access token for the cronjob
|
||||
* New config parameter to disable web access for the cronjob
|
||||
* Debug mode parameter is moved to the config file
|
||||
* The console web page have been removed
|
||||
* New API methods (not backward compatible)
|
||||
* Fever API tokens are longer than before
|
||||
* Add support for Wallabag service
|
||||
* Add unit tests
|
||||
|
||||
Migration procedure from 1.1.x to 1.2.0:
|
||||
|
||||
To import your old database to the new database format, use this script:
|
||||
|
||||
php scripts/migrate-db.php --sqlite-db=/path/to/my/db.sqlite --admin==1
|
6
Makefile
6
Makefile
@ -1,9 +1,10 @@
|
||||
.PHONY: archive
|
||||
.PHONY: docker-image
|
||||
.PHONY: docker-push
|
||||
.PHONY: docker-destroy
|
||||
.PHONY: docker-run
|
||||
.PHONY: archive
|
||||
.PHONY: js
|
||||
.PHONY: unit-test-sqlite
|
||||
|
||||
JS_FILE = assets/js/all.js
|
||||
CONTAINER = miniflux
|
||||
@ -36,3 +37,6 @@ $(JS_FILE): assets/js/app.js \
|
||||
# Build a new archive: make archive version=1.2.3 dst=/tmp
|
||||
archive:
|
||||
@ git archive --format=zip --prefix=miniflux/ v${version} -o ${dst}/miniflux-${version}.zip
|
||||
|
||||
unit-test-sqlite:
|
||||
@ ./vendor/bin/phpunit -c tests/phpunit.unit.xml
|
||||
|
@ -6,25 +6,27 @@ if (file_exists(__DIR__.'/../config.php')) {
|
||||
require __DIR__.'/../config.php';
|
||||
}
|
||||
|
||||
require __DIR__.'/constants.php';
|
||||
require __DIR__.'/check_setup.php';
|
||||
require __DIR__.'/functions.php';
|
||||
require_once __DIR__.'/constants.php';
|
||||
require_once __DIR__.'/check_setup.php';
|
||||
require_once __DIR__.'/functions.php';
|
||||
|
||||
PicoDb\Database::setInstance('db', function() {
|
||||
$db = new PicoDb\Database(array(
|
||||
'driver' => 'sqlite',
|
||||
'filename' => Miniflux\Model\Database\get_path(),
|
||||
'filename' => DB_FILENAME,
|
||||
));
|
||||
|
||||
$db->getStatementHandler()->withLogging();
|
||||
|
||||
if ($db->schema('\Miniflux\Schema')->check(Miniflux\Schema\VERSION)) {
|
||||
return $db;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$errors = $db->getLogMessages();
|
||||
$nb_errors = count($errors);
|
||||
|
||||
$html = 'Unable to migrate the database schema, <strong>please copy and paste this message and create a bug report:</strong><hr/>';
|
||||
$html .= '<pre><code>';
|
||||
$html .= (isset($errors[0]) ? $errors[0] : 'Unknown SQL error').PHP_EOL.PHP_EOL;
|
||||
$html .= (isset($errors[$nb_errors - 1]) ? $errors[$nb_errors - 1] : 'Unknown SQL error').PHP_EOL.PHP_EOL;
|
||||
$html .= '- PHP version: '.phpversion().PHP_EOL;
|
||||
$html .= '- SAPI: '.php_sapi_name().PHP_EOL;
|
||||
$html .= '- PDO Sqlite version: '.phpversion('pdo_sqlite').PHP_EOL;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
defined('APP_VERSION') or define('APP_VERSION', Miniflux\Helper\parse_app_version('$Format:%d$','$Format:%H$'));
|
||||
|
||||
define('HTTP_USER_AGENT', 'Miniflux (https://miniflux.net)');
|
||||
defined('HTTP_TIMEOUT') or define('HTTP_TIMEOUT', 20);
|
||||
defined('HTTP_MAX_RESPONSE_SIZE') or define('HTTP_MAX_RESPONSE_SIZE', 2097152);
|
||||
|
||||
@ -12,9 +13,9 @@ defined('DATA_DIRECTORY') or define('DATA_DIRECTORY', ROOT_DIRECTORY.DIRECTORY_S
|
||||
defined('FAVICON_DIRECTORY') or define('FAVICON_DIRECTORY', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'favicons');
|
||||
defined('FAVICON_URL_PATH') or define('FAVICON_URL_PATH', 'data/favicons');
|
||||
|
||||
defined('ENABLE_MULTIPLE_DB') or define('ENABLE_MULTIPLE_DB', true);
|
||||
defined('DB_FILENAME') or define('DB_FILENAME', 'db.sqlite');
|
||||
defined('DB_FILENAME') or define('DB_FILENAME', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'db.sqlite');
|
||||
|
||||
defined('DEBUG_MODE') or define('DEBUG_MODE', false);
|
||||
defined('DEBUG_FILENAME') or define('DEBUG_FILENAME', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'debug.log');
|
||||
|
||||
defined('THEME_DIRECTORY') or define('THEME_DIRECTORY', 'themes');
|
||||
@ -35,6 +36,7 @@ defined('SUBSCRIPTION_CONCURRENT_REQUESTS') or define('SUBSCRIPTION_CONCURRENT_R
|
||||
defined('RULES_DIRECTORY') or define('RULES_DIRECTORY', ROOT_DIRECTORY.DIRECTORY_SEPARATOR.'rules');
|
||||
|
||||
defined('ENABLE_HSTS') or define('ENABLE_HSTS', true);
|
||||
defined('ENABLE_CRONJOB_HTTP_ACCESS') or define('ENABLE_CRONJOB_HTTP_ACCESS', true);
|
||||
|
||||
defined('BEANSTALKD_HOST') or define('BEANSTALKD_HOST', '127.0.0.1');
|
||||
defined('BEANSTALKD_QUEUE') or define('BEANSTALKD_QUEUE', 'feeds');
|
||||
|
22
app/controllers/about.php
Normal file
22
app/controllers/about.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Helper;
|
||||
|
||||
Router\get_action('about', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
|
||||
Response\html(Template\layout('about', array(
|
||||
'csrf' => Helper\generate_csrf(),
|
||||
'config' => Model\Config\get_all($user_id),
|
||||
'user' => Model\User\get_user_by_id_without_password($user_id),
|
||||
'menu' => 'config',
|
||||
'title' => t('About'),
|
||||
)));
|
||||
});
|
20
app/controllers/api.php
Normal file
20
app/controllers/api.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Template;
|
||||
|
||||
Router\get_action('api', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
|
||||
Response\html(Template\layout('api', array(
|
||||
'config' => Model\Config\get_all($user_id),
|
||||
'user' => Model\User\get_user_by_id_without_password($user_id),
|
||||
'menu' => 'config',
|
||||
'title' => t('Preferences'),
|
||||
)));
|
||||
});
|
@ -1,8 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Session\SessionManager;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Validator;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Model\RememberMe;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Helper;
|
||||
@ -10,13 +15,15 @@ use Miniflux\Model;
|
||||
|
||||
// Logout and destroy session
|
||||
Router\get_action('logout', function () {
|
||||
Model\User\logout();
|
||||
SessionStorage::getInstance()->flush();
|
||||
SessionManager::close();
|
||||
RememberMe\destroy();
|
||||
Response\redirect('?action=login');
|
||||
});
|
||||
|
||||
// Display form login
|
||||
Router\get_action('login', function () {
|
||||
if (Model\User\is_loggedin()) {
|
||||
if (SessionStorage::getInstance()->isLogged()) {
|
||||
Response\redirect('?action=unread');
|
||||
}
|
||||
|
||||
@ -25,8 +32,6 @@ Router\get_action('login', function () {
|
||||
'values' => array(
|
||||
'csrf' => Helper\generate_csrf(),
|
||||
),
|
||||
'databases' => Model\Database\get_list(),
|
||||
'current_database' => Model\Database\select()
|
||||
)));
|
||||
});
|
||||
|
||||
@ -43,7 +48,5 @@ Router\post_action('login', function () {
|
||||
Response\html(Template\load('login', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values + array('csrf' => Helper\generate_csrf()),
|
||||
'databases' => Model\Database\get_list(),
|
||||
'current_database' => Model\Database\select()
|
||||
)));
|
||||
});
|
@ -1,120 +1,127 @@
|
||||
<?php
|
||||
|
||||
use PicoFeed\Syndication\AtomFeedBuilder;
|
||||
use PicoFeed\Syndication\AtomItemBuilder;
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use DateTime;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Handler\Service;
|
||||
use PicoFeed\Syndication\AtomFeedBuilder;
|
||||
use PicoFeed\Syndication\AtomItemBuilder;
|
||||
|
||||
// Ajax call to add or remove a bookmark
|
||||
Router\post_action('bookmark', function () {
|
||||
$id = Request\param('id');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$item_id = Request\param('id');
|
||||
$value = Request\int_param('value');
|
||||
|
||||
if ($value == 1) {
|
||||
Service\sync($user_id, $item_id);
|
||||
}
|
||||
|
||||
Response\json(array(
|
||||
'id' => $id,
|
||||
'id' => $item_id,
|
||||
'value' => $value,
|
||||
'result' => Model\Bookmark\set_flag($id, $value),
|
||||
'result' => Model\Bookmark\set_flag($user_id, $item_id, $value),
|
||||
));
|
||||
});
|
||||
|
||||
// Add new bookmark
|
||||
Router\get_action('bookmark', function () {
|
||||
$id = Request\param('id');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$item_id = Request\param('id');
|
||||
$menu = Request\param('menu');
|
||||
$redirect = Request\param('redirect', 'unread');
|
||||
$offset = Request\int_param('offset', 0);
|
||||
$feed_id = Request\int_param('feed_id', 0);
|
||||
$value = Request\int_param('value');
|
||||
|
||||
Model\Bookmark\set_flag($id, Request\int_param('value'));
|
||||
|
||||
if ($redirect === 'show') {
|
||||
Response\redirect('?action=show&menu='.$menu.'&id='.$id);
|
||||
if ($value == 1) {
|
||||
Service\sync($user_id, $item_id);
|
||||
}
|
||||
|
||||
Response\redirect('?action='.$redirect.'&offset='.$offset.'&feed_id='.$feed_id.'#item-'.$id);
|
||||
Model\Bookmark\set_flag($user_id, $item_id, $value);
|
||||
|
||||
if ($redirect === 'show') {
|
||||
Response\redirect('?action=show&menu='.$menu.'&id='.$item_id);
|
||||
}
|
||||
|
||||
Response\redirect('?action='.$redirect.'&offset='.$offset.'&feed_id='.$feed_id.'#item-'.$item_id);
|
||||
});
|
||||
|
||||
// Display bookmarks page
|
||||
Router\get_action('bookmarks', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$offset = Request\int_param('offset', 0);
|
||||
$group_id = Request\int_param('group_id', null);
|
||||
$feed_ids = array();
|
||||
|
||||
if ($group_id !== null) {
|
||||
$feed_ids = Model\Group\get_feeds_by_group($group_id);
|
||||
$feed_ids = Model\Group\get_feed_ids_by_group($group_id);
|
||||
}
|
||||
|
||||
$nb_items = Model\Bookmark\count_items($feed_ids);
|
||||
$items = Model\Bookmark\get_all_items(
|
||||
$nb_items = Model\Bookmark\count_bookmarked_items($user_id, $feed_ids);
|
||||
$items = Model\Bookmark\get_bookmarked_items(
|
||||
$user_id,
|
||||
$offset,
|
||||
Model\Config\get('items_per_page'),
|
||||
Helper\config('items_per_page'),
|
||||
$feed_ids
|
||||
);
|
||||
|
||||
Response\html(Template\layout('bookmarks', array(
|
||||
'favicons' => Model\Favicon\get_item_favicons($items),
|
||||
'original_marks_read' => Model\Config\get('original_marks_read'),
|
||||
'favicons' => Model\Favicon\get_items_favicons($items),
|
||||
'original_marks_read' => Helper\config('original_marks_read'),
|
||||
'order' => '',
|
||||
'direction' => '',
|
||||
'display_mode' => Model\Config\get('items_display_mode'),
|
||||
'item_title_link' => Model\Config\get('item_title_link'),
|
||||
'display_mode' => Helper\config('items_display_mode'),
|
||||
'item_title_link' => Helper\config('item_title_link'),
|
||||
'group_id' => $group_id,
|
||||
'items' => $items,
|
||||
'nb_items' => $nb_items,
|
||||
'offset' => $offset,
|
||||
'items_per_page' => Model\Config\get('items_per_page'),
|
||||
'items_per_page' => Helper\config('items_per_page'),
|
||||
'nothing_to_read' => Request\int_param('nothing_to_read'),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'bookmarks',
|
||||
'groups' => Model\Group\get_all(),
|
||||
'groups' => Model\Group\get_all($user_id),
|
||||
'title' => t('Bookmarks').' ('.$nb_items.')'
|
||||
)));
|
||||
});
|
||||
|
||||
// Display bookmark feeds
|
||||
Router\get_action('bookmark-feed', function () {
|
||||
// Select database if the parameter is set
|
||||
$database = Request\param('database');
|
||||
$token = Request\param('token');
|
||||
$user = Model\User\get_user_by_token('feed_token', $token);
|
||||
|
||||
if (!empty($database)) {
|
||||
Model\Database\select($database);
|
||||
if (empty($user)) {
|
||||
Response\text('Unauthorized', 401);
|
||||
}
|
||||
|
||||
// Check token
|
||||
$feed_token = Model\Config\get('feed_token');
|
||||
$request_token = Request\param('token');
|
||||
|
||||
if ($feed_token !== $request_token) {
|
||||
Response\text('Access Forbidden', 403);
|
||||
}
|
||||
|
||||
// Build Feed
|
||||
$bookmarks = Model\Bookmark\get_all_items();
|
||||
$bookmarks = Model\Bookmark\get_bookmarked_items($user['id']);
|
||||
|
||||
$feedBuilder = AtomFeedBuilder::create()
|
||||
->withTitle(t('Bookmarks').' - Miniflux')
|
||||
->withFeedUrl(Helper\get_current_base_url().'?action=bookmark-feed&token='.urlencode($feed_token))
|
||||
->withFeedUrl(Helper\get_current_base_url().'?action=bookmark-feed&token='.urlencode($user['feed_token']))
|
||||
->withSiteUrl(Helper\get_current_base_url())
|
||||
->withDate(new DateTime())
|
||||
;
|
||||
|
||||
foreach ($bookmarks as $bookmark) {
|
||||
$article = Model\Item\get($bookmark['id']);
|
||||
$articleDate = new DateTime();
|
||||
$articleDate->setTimestamp($article['updated']);
|
||||
$articleDate->setTimestamp($bookmark['updated']);
|
||||
|
||||
$feedBuilder
|
||||
->withItem(AtomItemBuilder::create($feedBuilder)
|
||||
->withId($article['id'])
|
||||
->withTitle($article['title'])
|
||||
->withUrl($article['url'])
|
||||
->withId($bookmark['id'])
|
||||
->withTitle($bookmark['title'])
|
||||
->withUrl($bookmark['url'])
|
||||
->withUpdatedDate($articleDate)
|
||||
->withPublishedDate($articleDate)
|
||||
->withContent($article['content'])
|
||||
->withContent($bookmark['content'])
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Session;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Session\SessionManager;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Translator;
|
||||
@ -12,39 +14,22 @@ use Miniflux\Handler;
|
||||
|
||||
// Called before each action
|
||||
Router\before(function ($action) {
|
||||
Session\open(BASE_URL_DIRECTORY, SESSION_SAVE_PATH, 0);
|
||||
|
||||
// Select the requested database either from post param database or from the
|
||||
// session variable. If it fails, logout to destroy session and
|
||||
// 'remember me' cookie
|
||||
if (Request\value('database') !== null && ! Model\Database\select(Request\value('database'))) {
|
||||
Model\User\logout();
|
||||
Response\redirect('?action=login');
|
||||
} elseif (! empty($_SESSION['database'])) {
|
||||
if (! Model\Database\select($_SESSION['database'])) {
|
||||
Model\User\logout();
|
||||
Response\redirect('?action=login');
|
||||
}
|
||||
}
|
||||
SessionManager::open(BASE_URL_DIRECTORY, SESSION_SAVE_PATH, 0);
|
||||
|
||||
// These actions are considered to be safe even for unauthenticated users
|
||||
$safe_actions = array('login', 'bookmark-feed', 'select-db', 'logout', 'notfound');
|
||||
|
||||
if (! Model\User\is_loggedin() && ! in_array($action, $safe_actions)) {
|
||||
$safe_actions = array('login', 'bookmark-feed', 'logout', 'notfound');
|
||||
if (! SessionStorage::getInstance()->isLogged() && ! in_array($action, $safe_actions)) {
|
||||
if (! Model\RememberMe\authenticate()) {
|
||||
Model\User\logout();
|
||||
Response\redirect('?action=login');
|
||||
}
|
||||
} elseif (Model\RememberMe\has_cookie()) {
|
||||
Model\RememberMe\refresh();
|
||||
}
|
||||
|
||||
// Load translations
|
||||
$language = Model\Config\get('language') ?: 'en_US';
|
||||
$language = Helper\config('language', 'en_US');
|
||||
Translator\load($language);
|
||||
|
||||
// Set timezone
|
||||
date_default_timezone_set(Model\Config\get('timezone') ?: 'UTC');
|
||||
date_default_timezone_set(Helper\config('timezone', 'UTC'));
|
||||
|
||||
// HTTP secure headers
|
||||
Response\csp(array(
|
||||
@ -64,13 +49,53 @@ Router\before(function ($action) {
|
||||
}
|
||||
});
|
||||
|
||||
// Show help
|
||||
Router\get_action('show-help', function () {
|
||||
Response\html(Template\load('show_help'));
|
||||
});
|
||||
|
||||
// Image proxy (avoid SSL mixed content warnings)
|
||||
Router\get_action('proxy', function () {
|
||||
Handler\Proxy\download(rawurldecode(Request\param('url')));
|
||||
exit;
|
||||
});
|
||||
|
||||
function items_list($status)
|
||||
{
|
||||
$order = Request\param('order', 'updated');
|
||||
$direction = Request\param('direction', Helper\config('items_sorting_direction'));
|
||||
$offset = Request\int_param('offset', 0);
|
||||
$group_id = Request\int_param('group_id', null);
|
||||
$nb_items_page = Helper\config('items_per_page');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$feed_ids = array();
|
||||
|
||||
if ($group_id !== null) {
|
||||
$feed_ids = Model\Group\get_feed_ids_by_group($group_id);
|
||||
}
|
||||
|
||||
$items = Model\Item\get_items_by_status(
|
||||
$user_id,
|
||||
$status,
|
||||
$feed_ids,
|
||||
$offset,
|
||||
$nb_items_page,
|
||||
$order,
|
||||
$direction
|
||||
);
|
||||
|
||||
$nb_items = Model\Item\count_by_status($user_id, $status, $feed_ids);
|
||||
$nb_unread_items = Model\Item\count_by_status($user_id, $status);
|
||||
|
||||
return array(
|
||||
'nothing_to_read' => Request\int_param('nothing_to_read'),
|
||||
'favicons' => Model\Favicon\get_items_favicons($items),
|
||||
'original_marks_read' => Helper\bool_config('original_marks_read'),
|
||||
'display_mode' => Helper\config('items_display_mode'),
|
||||
'item_title_link' => Helper\config('item_title_link'),
|
||||
'items_per_page' => $nb_items_page,
|
||||
'offset' => $offset,
|
||||
'direction' => $direction,
|
||||
'order' => $order,
|
||||
'items' => $items,
|
||||
'nb_items' => $nb_items,
|
||||
'nb_unread_items' => $nb_unread_items,
|
||||
'group_id' => $group_id,
|
||||
'groups' => Model\Group\get_all($user_id),
|
||||
);
|
||||
}
|
||||
|
@ -1,65 +1,19 @@
|
||||
<?php
|
||||
|
||||
use PicoDb\Database;
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Validator;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Session;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model;
|
||||
|
||||
// Display a form to add a new database
|
||||
Router\get_action('new-db', function () {
|
||||
if (ENABLE_MULTIPLE_DB) {
|
||||
Response\html(Template\layout('new_db', array(
|
||||
'errors' => array(),
|
||||
'values' => array(
|
||||
'csrf' => Helper\generate_csrf(),
|
||||
),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'config',
|
||||
'title' => t('New database')
|
||||
)));
|
||||
}
|
||||
|
||||
Response\redirect('?action=database');
|
||||
});
|
||||
|
||||
// Create a new database
|
||||
Router\post_action('new-db', function () {
|
||||
if (ENABLE_MULTIPLE_DB) {
|
||||
$values = Request\values();
|
||||
Helper\check_csrf_values($values);
|
||||
list($valid, $errors) = Validator\User\validate_creation($values);
|
||||
|
||||
if ($valid) {
|
||||
if (Model\Database\create(strtolower($values['name']).'.sqlite', $values['username'], $values['password'])) {
|
||||
Session\flash(t('Database created successfully.'));
|
||||
} else {
|
||||
Session\flash_error(t('Unable to create the new database.'));
|
||||
}
|
||||
|
||||
Response\redirect('?action=database');
|
||||
}
|
||||
|
||||
Response\html(Template\layout('new_db', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values + array('csrf' => Helper\generate_csrf()),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'config',
|
||||
'title' => t('New database')
|
||||
)));
|
||||
}
|
||||
|
||||
Response\redirect('?action=database');
|
||||
});
|
||||
|
||||
// Confirmation box before auto-update
|
||||
Router\get_action('confirm-auto-update', function () {
|
||||
Response\html(Template\layout('confirm_auto_update', array(
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'config',
|
||||
'title' => t('Confirmation')
|
||||
)));
|
||||
@ -68,10 +22,10 @@ Router\get_action('confirm-auto-update', function () {
|
||||
// Auto-update
|
||||
Router\get_action('auto-update', function () {
|
||||
if (ENABLE_AUTO_UPDATE) {
|
||||
if (Model\AutoUpdate\execute(Model\Config\get('auto_update_url'))) {
|
||||
Session\flash(t('Miniflux is updated!'));
|
||||
if (Model\AutoUpdate\execute(Helper\config('auto_update_url'))) {
|
||||
SessionStorage::getInstance()->setFlashMessage(t('Miniflux is updated!'));
|
||||
} else {
|
||||
Session\flash_error(t('Unable to update Miniflux, check the console for errors.'));
|
||||
SessionStorage::getInstance()->setFlashErrorMessage(t('Unable to update Miniflux, check the console for errors.'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,35 +34,22 @@ Router\get_action('auto-update', function () {
|
||||
|
||||
// Re-generate tokens
|
||||
Router\get_action('generate-tokens', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
|
||||
if (Helper\check_csrf(Request\param('csrf'))) {
|
||||
Model\Config\new_tokens();
|
||||
Model\User\regenerate_tokens($user_id);
|
||||
}
|
||||
|
||||
Response\redirect('?action=config');
|
||||
});
|
||||
|
||||
// Optimize the database manually
|
||||
Router\get_action('optimize-db', function () {
|
||||
if (Helper\check_csrf(Request\param('csrf'))) {
|
||||
Database::getInstance('db')->getConnection()->exec('VACUUM');
|
||||
}
|
||||
|
||||
Response\redirect('?action=database');
|
||||
});
|
||||
|
||||
// Download the compressed database
|
||||
Router\get_action('download-db', function () {
|
||||
if (Helper\check_csrf(Request\param('csrf'))) {
|
||||
Response\force_download('db.sqlite.gz');
|
||||
Response\binary(gzencode(file_get_contents(Model\Database\get_path())));
|
||||
}
|
||||
});
|
||||
|
||||
// Display preferences page
|
||||
Router\get_action('config', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
|
||||
Response\html(Template\layout('config', array(
|
||||
'errors' => array(),
|
||||
'values' => Model\Config\get_all() + array('csrf' => Helper\generate_csrf()),
|
||||
'values' => Model\Config\get_all($user_id) + array('csrf' => Helper\generate_csrf()),
|
||||
'languages' => Model\Config\get_languages(),
|
||||
'timezones' => Model\Config\get_timezones(),
|
||||
'autoflush_read_options' => Model\Config\get_autoflush_read_options(),
|
||||
@ -119,7 +60,6 @@ Router\get_action('config', function () {
|
||||
'display_mode' => Model\Config\get_display_mode(),
|
||||
'item_title_link' => Model\Config\get_item_title_link(),
|
||||
'redirect_nothing_to_read_options' => Model\Config\get_nothing_to_read_redirections(),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'config',
|
||||
'title' => t('Preferences')
|
||||
)));
|
||||
@ -127,15 +67,16 @@ Router\get_action('config', function () {
|
||||
|
||||
// Update preferences
|
||||
Router\post_action('config', function () {
|
||||
$values = Request\values() + array('nocontent' => 0, 'image_proxy' => 0, 'favicons' => 0, 'debug_mode' => 0, 'original_marks_read' => 0);
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$values = Request\values() + array('nocontent' => 0, 'image_proxy' => 0, 'favicons' => 0, 'original_marks_read' => 0);
|
||||
Helper\check_csrf_values($values);
|
||||
list($valid, $errors) = Validator\Config\validate_modification($values);
|
||||
|
||||
if ($valid) {
|
||||
if (Model\Config\save($values)) {
|
||||
Session\flash(t('Your preferences are updated.'));
|
||||
if (Model\Config\save($user_id, $values)) {
|
||||
SessionStorage::getInstance()->setFlashMessage(t('Your preferences are updated.'));
|
||||
} else {
|
||||
Session\flash_error(t('Unable to update your preferences.'));
|
||||
SessionStorage::getInstance()->setFlashErrorMessage(t('Unable to update your preferences.'));
|
||||
}
|
||||
|
||||
Response\redirect('?action=config');
|
||||
@ -143,7 +84,7 @@ Router\post_action('config', function () {
|
||||
|
||||
Response\html(Template\layout('config', array(
|
||||
'errors' => $errors,
|
||||
'values' => Model\Config\get_all() + array('csrf' => Helper\generate_csrf()),
|
||||
'values' => Model\Config\get_all($user_id) + array('csrf' => Helper\generate_csrf()),
|
||||
'languages' => Model\Config\get_languages(),
|
||||
'timezones' => Model\Config\get_timezones(),
|
||||
'autoflush_read_options' => Model\Config\get_autoflush_read_options(),
|
||||
@ -154,7 +95,6 @@ Router\post_action('config', function () {
|
||||
'redirect_nothing_to_read_options' => Model\Config\get_nothing_to_read_redirections(),
|
||||
'display_mode' => Model\Config\get_display_mode(),
|
||||
'item_title_link' => Model\Config\get_item_title_link(),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'config',
|
||||
'title' => t('Preferences')
|
||||
)));
|
||||
@ -162,84 +102,17 @@ Router\post_action('config', function () {
|
||||
|
||||
// Get configuration parameters (AJAX request)
|
||||
Router\post_action('get-config', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$return = array();
|
||||
$options = Request\values();
|
||||
|
||||
if (empty($options)) {
|
||||
$return = Model\Config\get_all();
|
||||
$return = Model\Config\get_all($user_id);
|
||||
} else {
|
||||
foreach ($options as $name) {
|
||||
$return[$name] = Model\Config\get($name);
|
||||
$return[$name] = Helper\config($name);
|
||||
}
|
||||
}
|
||||
|
||||
Response\json($return);
|
||||
});
|
||||
|
||||
// Display help page
|
||||
Router\get_action('help', function () {
|
||||
Response\html(Template\layout('help', array(
|
||||
'config' => Model\Config\get_all(),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'config',
|
||||
'title' => t('Preferences')
|
||||
)));
|
||||
});
|
||||
|
||||
// Display about page
|
||||
Router\get_action('about', function () {
|
||||
Response\html(Template\layout('about', array(
|
||||
'csrf' => Helper\generate_csrf(),
|
||||
'config' => Model\Config\get_all(),
|
||||
'db_name' => Model\Database\select(),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'config',
|
||||
'title' => t('Preferences')
|
||||
)));
|
||||
});
|
||||
|
||||
// Display database page
|
||||
Router\get_action('database', function () {
|
||||
Response\html(Template\layout('database', array(
|
||||
'csrf' => Helper\generate_csrf(),
|
||||
'config' => Model\Config\get_all(),
|
||||
'db_size' => filesize(Model\Database\get_path()),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'config',
|
||||
'title' => t('Preferences')
|
||||
)));
|
||||
});
|
||||
|
||||
// Display API page
|
||||
Router\get_action('api', function () {
|
||||
Response\html(Template\layout('api', array(
|
||||
'config' => Model\Config\get_all(),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'config',
|
||||
'title' => t('Preferences')
|
||||
)));
|
||||
});
|
||||
|
||||
// Display bookmark services page
|
||||
Router\get_action('services', function () {
|
||||
Response\html(Template\layout('services', array(
|
||||
'errors' => array(),
|
||||
'values' => Model\Config\get_all() + array('csrf' => Helper\generate_csrf()),
|
||||
'menu' => 'config',
|
||||
'title' => t('Preferences')
|
||||
)));
|
||||
});
|
||||
|
||||
// Update bookmark services
|
||||
Router\post_action('services', function () {
|
||||
$values = Request\values() + array('pinboard_enabled' => 0, 'instapaper_enabled' => 0, 'wallabag_enabled' => 0);
|
||||
Helper\check_csrf_values($values);
|
||||
|
||||
if (Model\Config\save($values)) {
|
||||
Session\flash(t('Your preferences are updated.'));
|
||||
} else {
|
||||
Session\flash_error(t('Unable to update your preferences.'));
|
||||
}
|
||||
|
||||
Response\redirect('?action=services');
|
||||
});
|
||||
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Model;
|
||||
|
||||
// Flush console messages
|
||||
Router\get_action('flush-console', function () {
|
||||
@unlink(DEBUG_FILENAME);
|
||||
Response\redirect('?action=console');
|
||||
});
|
||||
|
||||
// Display console
|
||||
Router\get_action('console', function () {
|
||||
Response\html(Template\layout('console', array(
|
||||
'content' => @file_get_contents(DEBUG_FILENAME),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'config',
|
||||
'title' => t('Console')
|
||||
)));
|
||||
});
|
@ -1,11 +1,12 @@
|
||||
<?php
|
||||
|
||||
use PicoFeed\Parser\MalformedXmlException;
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Validator;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Session;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Handler;
|
||||
@ -13,25 +14,26 @@ use Miniflux\Model;
|
||||
|
||||
// Refresh all feeds, used when Javascript is disabled
|
||||
Router\get_action('refresh-all', function () {
|
||||
Model\Feed\refresh_all();
|
||||
Session\flash(t('Your subscriptions are updated'));
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
Handler\Feed\update_feeds($user_id);
|
||||
SessionStorage::getInstance()->setFlashErrorMessage(t('Your subscriptions are updated'));
|
||||
Response\redirect('?action=unread');
|
||||
});
|
||||
|
||||
// Edit feed form
|
||||
Router\get_action('edit-feed', function () {
|
||||
$id = Request\int_param('feed_id');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$feed_id = Request\int_param('feed_id');
|
||||
|
||||
$values = Model\Feed\get($id);
|
||||
$values = Model\Feed\get_feed($user_id, $feed_id);
|
||||
$values += array(
|
||||
'feed_group_ids' => Model\Group\get_feed_group_ids($id)
|
||||
'feed_group_ids' => Model\Group\get_feed_group_ids($feed_id)
|
||||
);
|
||||
|
||||
Response\html(Template\layout('edit_feed', array(
|
||||
'values' => $values,
|
||||
'errors' => array(),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'groups' => Model\Group\get_all(),
|
||||
'groups' => Model\Group\get_all($user_id),
|
||||
'menu' => 'feeds',
|
||||
'title' => t('Edit subscription')
|
||||
)));
|
||||
@ -39,32 +41,32 @@ Router\get_action('edit-feed', function () {
|
||||
|
||||
// Submit edit feed form
|
||||
Router\post_action('edit-feed', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$values = Request\values();
|
||||
$values += array(
|
||||
'enabled' => 0,
|
||||
'download_content' => 0,
|
||||
'rtl' => 0,
|
||||
'cloak_referrer' => 0,
|
||||
'parsing_error' => 0,
|
||||
'feed_group_ids' => array(),
|
||||
'create_group' => ''
|
||||
);
|
||||
|
||||
list($valid, $errors) = Validator\Feed\validate_modification($values);
|
||||
|
||||
if ($valid) {
|
||||
if (Model\Feed\update($values)) {
|
||||
Session\flash(t('Your subscription has been updated.'));
|
||||
if (Model\Feed\update_feed($user_id, $values['id'], $values)) {
|
||||
SessionStorage::getInstance()->setFlashMessage(t('Your subscription has been updated.'));
|
||||
Response\redirect('?action=feeds');
|
||||
} else {
|
||||
Session\flash_error(t('Unable to edit your subscription.'));
|
||||
SessionStorage::getInstance()->setFlashErrorMessage(t('Unable to edit your subscription.'));
|
||||
}
|
||||
}
|
||||
|
||||
Response\html(Template\layout('edit_feed', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'groups' => Model\Group\get_all(),
|
||||
'groups' => Model\Group\get_all($user_id),
|
||||
'menu' => 'feeds',
|
||||
'title' => t('Edit subscription')
|
||||
)));
|
||||
@ -72,11 +74,11 @@ Router\post_action('edit-feed', function () {
|
||||
|
||||
// Confirmation box to remove a feed
|
||||
Router\get_action('confirm-remove-feed', function () {
|
||||
$id = Request\int_param('feed_id');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$feed_id = Request\int_param('feed_id');
|
||||
|
||||
Response\html(Template\layout('confirm_remove_feed', array(
|
||||
'feed' => Model\Feed\get($id),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'feed' => Model\Feed\get_feed($user_id, $feed_id),
|
||||
'menu' => 'feeds',
|
||||
'title' => t('Confirmation')
|
||||
)));
|
||||
@ -84,12 +86,13 @@ Router\get_action('confirm-remove-feed', function () {
|
||||
|
||||
// Remove a feed
|
||||
Router\get_action('remove-feed', function () {
|
||||
$id = Request\int_param('feed_id');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$feed_id = Request\int_param('feed_id');
|
||||
|
||||
if ($id && Model\Feed\remove($id)) {
|
||||
Session\flash(t('This subscription has been removed successfully.'));
|
||||
if (Model\Feed\remove_feed($user_id, $feed_id)) {
|
||||
SessionStorage::getInstance()->setFlashMessage(t('This subscription has been removed successfully.'));
|
||||
} else {
|
||||
Session\flash_error(t('Unable to remove this subscription.'));
|
||||
SessionStorage::getInstance()->setFlashErrorMessage(t('Unable to remove this subscription.'));
|
||||
}
|
||||
|
||||
Response\redirect('?action=feeds');
|
||||
@ -97,40 +100,43 @@ Router\get_action('remove-feed', function () {
|
||||
|
||||
// Refresh one feed and redirect to unread items
|
||||
Router\get_action('refresh-feed', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$feed_id = Request\int_param('feed_id');
|
||||
$redirect = Request\param('redirect', 'unread');
|
||||
|
||||
Model\Feed\refresh($feed_id);
|
||||
Handler\Feed\update_feed($user_id, $feed_id);
|
||||
Response\redirect('?action='.$redirect.'&feed_id='.$feed_id);
|
||||
});
|
||||
|
||||
// Ajax call to refresh one feed
|
||||
Router\post_action('refresh-feed', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$feed_id = Request\int_param('feed_id', 0);
|
||||
|
||||
Response\json(array(
|
||||
'feed_id' => $feed_id,
|
||||
'result' => Model\Feed\refresh($feed_id),
|
||||
'items_count' => Model\Feed\count_items($feed_id),
|
||||
'result' => Handler\Feed\update_feed($user_id, $feed_id),
|
||||
'items_count' => Model\ItemFeed\count_items_by_status($user_id, $feed_id),
|
||||
));
|
||||
});
|
||||
|
||||
// Display all feeds
|
||||
Router\get_action('feeds', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$nothing_to_read = Request\int_param('nothing_to_read');
|
||||
$nb_unread_items = Model\Item\count_by_status('unread');
|
||||
$nb_unread_items = Model\Item\count_by_status($user_id, 'unread');
|
||||
$feeds = Model\Feed\get_feeds_with_items_count($user_id);
|
||||
|
||||
// possible with remember me function
|
||||
if ($nothing_to_read === 1 && $nb_unread_items > 0) {
|
||||
Response\redirect('?action=unread');
|
||||
}
|
||||
|
||||
Response\html(Template\layout('feeds', array(
|
||||
'favicons' => Model\Favicon\get_all_favicons(),
|
||||
'feeds' => Model\Feed\get_all_item_counts(),
|
||||
'favicons' => Model\Favicon\get_feeds_favicons($feeds),
|
||||
'feeds' => $feeds,
|
||||
'nothing_to_read' => $nothing_to_read,
|
||||
'nb_unread_items' => $nb_unread_items,
|
||||
'nb_failed_feeds' => Model\Feed\count_failed_feeds(),
|
||||
'nb_failed_feeds' => Model\Feed\count_failed_feeds($user_id),
|
||||
'menu' => 'feeds',
|
||||
'title' => t('Subscriptions')
|
||||
)));
|
||||
@ -138,21 +144,21 @@ Router\get_action('feeds', function () {
|
||||
|
||||
// Display form to add one feed
|
||||
Router\get_action('add', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$values = array(
|
||||
'download_content' => 0,
|
||||
'rtl' => 0,
|
||||
'cloak_referrer' => 0,
|
||||
'create_group' => '',
|
||||
'feed_group_ids' => array()
|
||||
'feed_group_ids' => array(),
|
||||
);
|
||||
|
||||
Response\html(Template\layout('add', array(
|
||||
'values' => $values + array('csrf' => Helper\generate_csrf()),
|
||||
'errors' => array(),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'groups' => Model\Group\get_all(),
|
||||
'groups' => Model\Group\get_all($user_id),
|
||||
'menu' => 'feeds',
|
||||
'title' => t('New subscription')
|
||||
'title' => t('New subscription'),
|
||||
)));
|
||||
});
|
||||
|
||||
@ -162,14 +168,18 @@ Router\action('subscribe', function () {
|
||||
$values = Request\values();
|
||||
Helper\check_csrf_values($values);
|
||||
$url = isset($values['url']) ? $values['url'] : '';
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
} else {
|
||||
$values = array();
|
||||
$url = Request\param('url');
|
||||
$token = Request\param('token');
|
||||
$user = Model\User\get_user_by_token('bookmarklet_token', $token);
|
||||
$values = array();
|
||||
|
||||
if ($token !== Model\Config\get('bookmarklet_token')) {
|
||||
Response\text('Access Forbidden', 403);
|
||||
if (empty($user)) {
|
||||
Response\text('Unauthorized', 401);
|
||||
}
|
||||
|
||||
$user_id = $user['id'];
|
||||
}
|
||||
|
||||
$values += array(
|
||||
@ -177,85 +187,30 @@ Router\action('subscribe', function () {
|
||||
'download_content' => 0,
|
||||
'rtl' => 0,
|
||||
'cloak_referrer' => 0,
|
||||
'create_group' => '',
|
||||
'feed_group_ids' => array()
|
||||
'feed_group_ids' => array(),
|
||||
);
|
||||
|
||||
try {
|
||||
$feed_id = Model\Feed\create(
|
||||
list($feed_id, $error_message) = Handler\Feed\create_feed(
|
||||
$user_id,
|
||||
$values['url'],
|
||||
$values['download_content'],
|
||||
$values['rtl'],
|
||||
$values['cloak_referrer'],
|
||||
$values['feed_group_ids'],
|
||||
$values['create_group']
|
||||
$values['groups']
|
||||
);
|
||||
} catch (UnexpectedValueException $e) {
|
||||
$error_message = t('This subscription already exists.');
|
||||
} catch (PicoFeed\Client\InvalidCertificateException $e) {
|
||||
$error_message = t('Invalid SSL certificate.');
|
||||
} catch (PicoFeed\Client\InvalidUrlException $e) {
|
||||
$error_message = $e->getMessage();
|
||||
} catch (PicoFeed\Client\MaxRedirectException $e) {
|
||||
$error_message = t('Maximum number of HTTP redirections exceeded.');
|
||||
} catch (PicoFeed\Client\MaxSizeException $e) {
|
||||
$error_message = t('The content size exceeds to maximum allowed size.');
|
||||
} catch (PicoFeed\Client\TimeoutException $e) {
|
||||
$error_message = t('Connection timeout.');
|
||||
} catch (PicoFeed\Parser\MalformedXmlException $e) {
|
||||
$error_message = t('Feed is malformed.');
|
||||
} catch (PicoFeed\Reader\SubscriptionNotFoundException $e) {
|
||||
$error_message = t('Unable to find a subscription.');
|
||||
} catch (PicoFeed\Reader\UnsupportedFeedFormatException $e) {
|
||||
$error_message = t('Unable to detect the feed format.');
|
||||
}
|
||||
|
||||
Model\Config\write_debug();
|
||||
|
||||
if (isset($feed_id) && $feed_id !== false) {
|
||||
Session\flash(t('Subscription added successfully.'));
|
||||
if ($feed_id >= 1) {
|
||||
SessionStorage::getInstance()->setFlashMessage(t('Subscription added successfully.'));
|
||||
Response\redirect('?action=feed-items&feed_id='.$feed_id);
|
||||
} else {
|
||||
if (! isset($error_message)) {
|
||||
$error_message = t('Error occured.');
|
||||
}
|
||||
|
||||
Session\flash_error($error_message);
|
||||
SessionStorage::getInstance()->setFlashErrorMessage($error_message);
|
||||
}
|
||||
|
||||
Response\html(Template\layout('add', array(
|
||||
'values' => $values + array('csrf' => Helper\generate_csrf()),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'groups' => Model\Group\get_all(),
|
||||
'groups' => Model\Group\get_all($user_id),
|
||||
'menu' => 'feeds',
|
||||
'title' => t('Subscriptions')
|
||||
'title' => t('Subscriptions'),
|
||||
)));
|
||||
});
|
||||
|
||||
// OPML export
|
||||
Router\get_action('export', function () {
|
||||
Response\force_download('feeds.opml');
|
||||
Response\xml(Handler\Opml\export_all_feeds());
|
||||
});
|
||||
|
||||
// OPML import form
|
||||
Router\get_action('import', function () {
|
||||
Response\html(Template\layout('import', array(
|
||||
'errors' => array(),
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'feeds',
|
||||
'title' => t('OPML Import')
|
||||
)));
|
||||
});
|
||||
|
||||
// OPML importation
|
||||
Router\post_action('import', function () {
|
||||
try {
|
||||
Model\Feed\import_opml(Request\file_content('file'));
|
||||
Session\flash(t('Your feeds have been imported.'));
|
||||
Response\redirect('?action=feeds');
|
||||
} catch (MalformedXmlException $e) {
|
||||
Session\flash_error(t('Unable to import your OPML file.').' ('.$e->getMessage().')');
|
||||
Response\redirect('?action=import');
|
||||
}
|
||||
});
|
||||
|
26
app/controllers/help.php
Normal file
26
app/controllers/help.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Template;
|
||||
|
||||
// Display help page
|
||||
Router\get_action('help', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
|
||||
Response\html(Template\layout('help', array(
|
||||
'config' => Model\Config\get_all($user_id),
|
||||
'menu' => 'config',
|
||||
'title' => t('Preferences')
|
||||
)));
|
||||
});
|
||||
|
||||
// Show help
|
||||
Router\get_action('show-help', function () {
|
||||
Response\html(Template\load('show_help'));
|
||||
});
|
||||
|
@ -1,62 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model;
|
||||
|
||||
// Display history page
|
||||
Router\get_action('history', function () {
|
||||
$order = Request\param('order', 'updated');
|
||||
$direction = Request\param('direction', Model\Config\get('items_sorting_direction'));
|
||||
$offset = Request\int_param('offset', 0);
|
||||
$group_id = Request\int_param('group_id', null);
|
||||
$feed_ids = array();
|
||||
$params = items_list(Model\Item\STATUS_READ);
|
||||
|
||||
if ($group_id !== null) {
|
||||
$feed_ids = Model\Group\get_feeds_by_group($group_id);
|
||||
}
|
||||
|
||||
$items = Model\Item\get_all_by_status(
|
||||
'read',
|
||||
$feed_ids,
|
||||
$offset,
|
||||
Model\Config\get('items_per_page'),
|
||||
$order,
|
||||
$direction
|
||||
);
|
||||
|
||||
$nb_items = Model\Item\count_by_status('read', $feed_ids);
|
||||
|
||||
Response\html(Template\layout('history', array(
|
||||
'favicons' => Model\Favicon\get_item_favicons($items),
|
||||
'original_marks_read' => Model\Config\get('original_marks_read'),
|
||||
'items' => $items,
|
||||
'order' => $order,
|
||||
'direction' => $direction,
|
||||
'display_mode' => Model\Config\get('items_display_mode'),
|
||||
'item_title_link' => Model\Config\get('item_title_link'),
|
||||
'group_id' => $group_id,
|
||||
'nb_items' => $nb_items,
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'offset' => $offset,
|
||||
'items_per_page' => Model\Config\get('items_per_page'),
|
||||
'nothing_to_read' => Request\int_param('nothing_to_read'),
|
||||
Response\html(Template\layout('history', $params + array(
|
||||
'title' => t('History') . ' (' . $params['nb_items'] . ')',
|
||||
'menu' => 'history',
|
||||
'groups' => Model\Group\get_all(),
|
||||
'title' => t('History').' ('.$nb_items.')'
|
||||
)));
|
||||
});
|
||||
|
||||
// Confirmation box to flush history
|
||||
Router\get_action('confirm-flush-history', function () {
|
||||
$group_id = Request\int_param('group_id', null);
|
||||
$group_id = Request\int_param('group_id');
|
||||
|
||||
Response\html(Template\layout('confirm_flush_items', array(
|
||||
'group_id' => $group_id,
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'menu' => 'history',
|
||||
'title' => t('Confirmation')
|
||||
)));
|
||||
@ -64,12 +32,13 @@ Router\get_action('confirm-flush-history', function () {
|
||||
|
||||
// Flush history
|
||||
Router\get_action('flush-history', function () {
|
||||
$group_id = Request\int_param('group_id', null);
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$group_id = Request\int_param('group_id');
|
||||
|
||||
if ($group_id !== null) {
|
||||
Model\ItemGroup\mark_all_as_removed($group_id);
|
||||
if ($group_id !== 0) {
|
||||
Model\ItemGroup\change_items_status($user_id, $group_id, Model\Item\STATUS_READ, Model\Item\STATUS_REMOVED);
|
||||
} else {
|
||||
Model\Item\mark_all_as_removed();
|
||||
Model\Item\change_items_status($user_id, Model\Item\STATUS_READ, Model\Item\STATUS_REMOVED);
|
||||
}
|
||||
|
||||
Response\redirect('?action=history');
|
||||
|
@ -1,101 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Handler;
|
||||
use Miniflux\Model;
|
||||
|
||||
// Display unread items
|
||||
Router\get_action('unread', function () {
|
||||
Model\Item\autoflush_read();
|
||||
Model\Item\autoflush_unread();
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
|
||||
$order = Request\param('order', 'updated');
|
||||
$direction = Request\param('direction', Model\Config\get('items_sorting_direction'));
|
||||
$offset = Request\int_param('offset', 0);
|
||||
$group_id = Request\int_param('group_id', null);
|
||||
$feed_ids = array();
|
||||
Model\Item\autoflush_read($user_id);
|
||||
Model\Item\autoflush_unread($user_id);
|
||||
|
||||
if ($group_id !== null) {
|
||||
$feed_ids = Model\Group\get_feeds_by_group($group_id);
|
||||
}
|
||||
$params = items_list(Model\Item\STATUS_UNREAD);
|
||||
|
||||
$items = Model\Item\get_all_by_status(
|
||||
'unread',
|
||||
$feed_ids,
|
||||
$offset,
|
||||
Model\Config\get('items_per_page'),
|
||||
$order,
|
||||
$direction
|
||||
);
|
||||
|
||||
$nb_items = Model\Item\count_by_status('unread', $feed_ids);
|
||||
$nb_unread_items = Model\Item\count_by_status('unread');
|
||||
|
||||
if ($nb_unread_items === 0) {
|
||||
$action = Model\Config\get('redirect_nothing_to_read');
|
||||
if ($params['nb_unread_items'] === 0) {
|
||||
$action = Helper\config('redirect_nothing_to_read', 'feeds');
|
||||
Response\redirect('?action='.$action.'¬hing_to_read=1');
|
||||
}
|
||||
|
||||
Response\html(Template\layout('unread_items', array(
|
||||
'favicons' => Model\Favicon\get_item_favicons($items),
|
||||
'original_marks_read' => Model\Config\get('original_marks_read'),
|
||||
'order' => $order,
|
||||
'direction' => $direction,
|
||||
'display_mode' => Model\Config\get('items_display_mode'),
|
||||
'item_title_link' => Model\Config\get('item_title_link'),
|
||||
'group_id' => $group_id,
|
||||
'items' => $items,
|
||||
'nb_items' => $nb_items,
|
||||
'nb_unread_items' => $nb_unread_items,
|
||||
'offset' => $offset,
|
||||
'items_per_page' => Model\Config\get('items_per_page'),
|
||||
'title' => 'Miniflux ('.$nb_items.')',
|
||||
Response\html(Template\layout('unread_items', $params + array(
|
||||
'title' => 'Miniflux (' . $params['nb_items'] . ')',
|
||||
'menu' => 'unread',
|
||||
'groups' => Model\Group\get_all()
|
||||
)));
|
||||
});
|
||||
|
||||
// Show item
|
||||
Router\get_action('show', function () {
|
||||
$id = Request\param('id');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$item_id = Request\param('id');
|
||||
$menu = Request\param('menu');
|
||||
$item = Model\Item\get($id);
|
||||
$feed = Model\Feed\get($item['feed_id']);
|
||||
$item = Model\Item\get_item($user_id, $item_id);
|
||||
$feed = Model\Feed\get_feed($user_id, $item['feed_id']);
|
||||
$group_id = Request\int_param('group_id', null);
|
||||
|
||||
Model\Item\set_read($id);
|
||||
Model\Item\change_item_status($user_id, $item_id, Model\Item\STATUS_READ);
|
||||
$item['status'] = 'read';
|
||||
|
||||
switch ($menu) {
|
||||
case 'unread':
|
||||
$nav = Model\Item\get_nav($item, array('unread'), array(1, 0), null, $group_id);
|
||||
$nav = Model\Item\get_item_nav($user_id, $item, array('unread'), array(1, 0), null, $group_id);
|
||||
break;
|
||||
case 'history':
|
||||
$nav = Model\Item\get_nav($item, array('read'));
|
||||
$nav = Model\Item\get_item_nav($user_id, $item, array('read'));
|
||||
break;
|
||||
case 'feed-items':
|
||||
$nav = Model\Item\get_nav($item, array('unread', 'read'), array(1, 0), $item['feed_id']);
|
||||
$nav = Model\Item\get_item_nav($user_id, $item, array('unread', 'read'), array(1, 0), $item['feed_id']);
|
||||
break;
|
||||
case 'bookmarks':
|
||||
$nav = Model\Item\get_nav($item, array('unread', 'read'), array(1));
|
||||
$nav = Model\Item\get_item_nav($user_id, $item, array('unread', 'read'), array(1));
|
||||
break;
|
||||
}
|
||||
|
||||
$image_proxy = (bool) Model\Config\get('image_proxy');
|
||||
$image_proxy = (bool) Helper\config('image_proxy');
|
||||
|
||||
// add the image proxy if requested and required
|
||||
$item['content'] = Handler\Proxy\rewrite_html($item['content'], $item['url'], $image_proxy, $feed['cloak_referrer']);
|
||||
|
||||
if ($image_proxy && strpos($item['enclosure_type'], 'image') === 0) {
|
||||
$item['enclosure'] = Handler\Proxy\rewrite_link($item['enclosure']);
|
||||
$item['enclosure_url'] = Handler\Proxy\rewrite_link($item['enclosure_url']);
|
||||
}
|
||||
|
||||
Response\html(Template\layout('show_item', array(
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'item' => $item,
|
||||
'feed' => $feed,
|
||||
'item_nav' => isset($nav) ? $nav : null,
|
||||
@ -107,27 +79,27 @@ Router\get_action('show', function () {
|
||||
|
||||
// Display feed items page
|
||||
Router\get_action('feed-items', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$feed_id = Request\int_param('feed_id', 0);
|
||||
$offset = Request\int_param('offset', 0);
|
||||
$nb_items = Model\ItemFeed\count_items($feed_id);
|
||||
$feed = Model\Feed\get($feed_id);
|
||||
$feed = Model\Feed\get_feed($user_id, $feed_id);
|
||||
$order = Request\param('order', 'updated');
|
||||
$direction = Request\param('direction', Model\Config\get('items_sorting_direction'));
|
||||
$items = Model\ItemFeed\get_all_items($feed_id, $offset, Model\Config\get('items_per_page'), $order, $direction);
|
||||
$direction = Request\param('direction', Helper\config('items_sorting_direction'));
|
||||
$items = Model\ItemFeed\get_all_items($user_id, $feed_id, $offset, Helper\config('items_per_page'), $order, $direction);
|
||||
$nb_items = Model\ItemFeed\count_items($user_id, $feed_id);
|
||||
|
||||
Response\html(Template\layout('feed_items', array(
|
||||
'favicons' => Model\Favicon\get_favicons(array($feed['id'])),
|
||||
'original_marks_read' => Model\Config\get('original_marks_read'),
|
||||
'favicons' => Model\Favicon\get_favicons_by_feed_ids(array($feed['id'])),
|
||||
'original_marks_read' => Helper\config('original_marks_read'),
|
||||
'order' => $order,
|
||||
'direction' => $direction,
|
||||
'display_mode' => Model\Config\get('items_display_mode'),
|
||||
'display_mode' => Helper\config('items_display_mode'),
|
||||
'feed' => $feed,
|
||||
'items' => $items,
|
||||
'nb_items' => $nb_items,
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'offset' => $offset,
|
||||
'items_per_page' => Model\Config\get('items_per_page'),
|
||||
'item_title_link' => Model\Config\get('item_title_link'),
|
||||
'items_per_page' => Helper\config('items_per_page'),
|
||||
'item_title_link' => Helper\config('item_title_link'),
|
||||
'menu' => 'feed-items',
|
||||
'title' => '('.$nb_items.') '.$feed['title']
|
||||
)));
|
||||
@ -135,43 +107,56 @@ Router\get_action('feed-items', function () {
|
||||
|
||||
// Ajax call to download an item (fetch the full content from the original website)
|
||||
Router\post_action('download-item', function () {
|
||||
$id = Request\param('id');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$item_id = Request\param('id');
|
||||
|
||||
$item = Model\Item\get($id);
|
||||
$feed = Model\Feed\get($item['feed_id']);
|
||||
$item = Model\Item\get_item($user_id, $item_id);
|
||||
$feed = Model\Feed\get_feed($user_id, $item['feed_id']);
|
||||
|
||||
$download = Model\Item\download_contents($id);
|
||||
$download['content'] = Handler\Proxy\rewrite_html($download['content'], $item['url'], Model\Config\get('image_proxy'), $feed['cloak_referrer']);
|
||||
$download = Handler\Item\download_item_content($user_id, $item_id);
|
||||
$download['content'] = Handler\Proxy\rewrite_html(
|
||||
$download['content'],
|
||||
$item['url'],
|
||||
Helper\bool_config('image_proxy'),
|
||||
(bool) $feed['cloak_referrer']
|
||||
);
|
||||
|
||||
Response\json($download);
|
||||
});
|
||||
|
||||
// Ajax call to mark item read
|
||||
Router\post_action('mark-item-read', function () {
|
||||
Model\Item\set_read(Request\param('id'));
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$item_id = Request\param('id');
|
||||
Model\Item\change_item_status($user_id, $item_id, Model\Item\STATUS_READ);
|
||||
Response\json(array('Ok'));
|
||||
});
|
||||
|
||||
// Ajax call to mark item as removed
|
||||
Router\post_action('mark-item-removed', function () {
|
||||
Model\Item\set_removed(Request\param('id'));
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$item_id = Request\param('id');
|
||||
Model\Item\change_item_status($user_id, $item_id, Model\Item\STATUS_REMOVED);
|
||||
Response\json(array('Ok'));
|
||||
});
|
||||
|
||||
// Ajax call to mark item unread
|
||||
Router\post_action('mark-item-unread', function () {
|
||||
Model\Item\set_unread(Request\param('id'));
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$item_id = Request\param('id');
|
||||
Model\Item\change_item_status($user_id, $item_id, Model\Item\STATUS_UNREAD);
|
||||
Response\json(array('Ok'));
|
||||
});
|
||||
|
||||
// Mark unread items as read
|
||||
Router\get_action('mark-all-read', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$group_id = Request\int_param('group_id', null);
|
||||
|
||||
if ($group_id !== null) {
|
||||
Model\ItemGroup\mark_all_as_read($group_id);
|
||||
Model\ItemGroup\change_items_status($user_id, $group_id, Model\Item\STATUS_UNREAD, Model\Item\STATUS_READ);
|
||||
} else {
|
||||
Model\Item\mark_all_as_read();
|
||||
Model\Item\change_items_status($user_id, Model\Item\STATUS_UNREAD, Model\Item\STATUS_READ);
|
||||
}
|
||||
|
||||
Response\redirect('?action=unread');
|
||||
@ -179,9 +164,11 @@ Router\get_action('mark-all-read', function () {
|
||||
|
||||
// Mark all unread items as read for a specific feed
|
||||
Router\get_action('mark-feed-as-read', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$feed_id = Request\int_param('feed_id');
|
||||
|
||||
Model\ItemFeed\mark_all_as_read($feed_id);
|
||||
Model\ItemFeed\change_items_status($user_id, $feed_id, Model\Item\STATUS_UNREAD, Model\Item\STATUS_READ);
|
||||
|
||||
Response\redirect('?action=feed-items&feed_id='.$feed_id);
|
||||
});
|
||||
|
||||
@ -190,48 +177,55 @@ Router\get_action('mark-feed-as-read', function () {
|
||||
// that where marked read from the frontend, since the number of unread items
|
||||
// on page 2+ is unknown.
|
||||
Router\post_action('mark-feed-as-read', function () {
|
||||
Model\ItemFeed\mark_all_as_read(Request\int_param('feed_id'));
|
||||
$nb_items = Model\Item\count_by_status('unread');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$feed_id = Request\int_param('feed_id');
|
||||
|
||||
Model\ItemFeed\change_items_status($user_id, $feed_id, Model\Item\STATUS_UNREAD, Model\Item\STATUS_READ);
|
||||
|
||||
$nb_items = Model\Item\count_by_status($user_id, Model\Item\STATUS_READ);
|
||||
Response\raw($nb_items);
|
||||
});
|
||||
|
||||
// Mark item as read and redirect to the listing page
|
||||
Router\get_action('mark-item-read', function () {
|
||||
$id = Request\param('id');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$item_id = Request\param('id');
|
||||
$redirect = Request\param('redirect', 'unread');
|
||||
$offset = Request\int_param('offset', 0);
|
||||
$feed_id = Request\int_param('feed_id', 0);
|
||||
|
||||
Model\Item\set_read($id);
|
||||
Response\redirect('?action='.$redirect.'&offset='.$offset.'&feed_id='.$feed_id.'#item-'.$id);
|
||||
Model\Item\change_item_status($user_id, $item_id, Model\Item\STATUS_READ);
|
||||
Response\redirect('?action='.$redirect.'&offset='.$offset.'&feed_id='.$feed_id.'#item-'.$item_id);
|
||||
});
|
||||
|
||||
// Mark item as unread and redirect to the listing page
|
||||
Router\get_action('mark-item-unread', function () {
|
||||
$id = Request\param('id');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$item_id = Request\param('id');
|
||||
$redirect = Request\param('redirect', 'history');
|
||||
$offset = Request\int_param('offset', 0);
|
||||
$feed_id = Request\int_param('feed_id', 0);
|
||||
|
||||
Model\Item\set_unread($id);
|
||||
Response\redirect('?action='.$redirect.'&offset='.$offset.'&feed_id='.$feed_id.'#item-'.$id);
|
||||
Model\Item\change_item_status($user_id, $item_id, Model\Item\STATUS_UNREAD);
|
||||
Response\redirect('?action='.$redirect.'&offset='.$offset.'&feed_id='.$feed_id.'#item-'.$item_id);
|
||||
});
|
||||
|
||||
// Mark item as removed and redirect to the listing page
|
||||
Router\get_action('mark-item-removed', function () {
|
||||
$id = Request\param('id');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$item_id = Request\param('id');
|
||||
$redirect = Request\param('redirect', 'history');
|
||||
$offset = Request\int_param('offset', 0);
|
||||
$feed_id = Request\int_param('feed_id', 0);
|
||||
|
||||
Model\Item\set_removed($id);
|
||||
Model\Item\change_item_status($user_id, $item_id, Model\Item\STATUS_REMOVED);
|
||||
Response\redirect('?action='.$redirect.'&offset='.$offset.'&feed_id='.$feed_id);
|
||||
});
|
||||
|
||||
Router\post_action('latest-feeds-items', function () {
|
||||
$items = Model\Item\get_latest_feeds_items();
|
||||
$nb_unread_items = Model\Item\count_by_status('unread');
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$items = Model\Item\get_latest_feeds_items($user_id);
|
||||
$nb_unread_items = Model\Item\count_by_status($user_id, 'unread');
|
||||
|
||||
$feeds = array_reduce($items, function ($result, $item) {
|
||||
$result[$item['id']] = array(
|
||||
|
40
app/controllers/opml.php
Normal file
40
app/controllers/opml.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Exception;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Handler;
|
||||
|
||||
// OPML export
|
||||
Router\get_action('export', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
Response\force_download('feeds.opml');
|
||||
Response\xml(Handler\Opml\export_all_feeds($user_id));
|
||||
});
|
||||
|
||||
// OPML import form
|
||||
Router\get_action('import', function () {
|
||||
Response\html(Template\layout('import', array(
|
||||
'errors' => array(),
|
||||
'menu' => 'feeds',
|
||||
'title' => t('OPML Import')
|
||||
)));
|
||||
});
|
||||
|
||||
// OPML importation
|
||||
Router\post_action('import', function () {
|
||||
try {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
Handler\Opml\import_opml($user_id, Request\file_content('file'));
|
||||
SessionStorage::getInstance()->setFlashMessage(t('Your feeds have been imported.'));
|
||||
Response\redirect('?action=feeds');
|
||||
} catch (Exception $e) {
|
||||
SessionStorage::getInstance()->setFlashErrorMessage(t('Unable to import your OPML file.').' ('.$e->getMessage().')');
|
||||
Response\redirect('?action=import');
|
||||
}
|
||||
});
|
48
app/controllers/profile.php
Normal file
48
app/controllers/profile.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Validator;
|
||||
|
||||
Router\get_action('profile', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
|
||||
Response\html(Template\layout('profile', array(
|
||||
'errors' => array(),
|
||||
'values' => Model\User\get_user_by_id_without_password($user_id) + array('csrf' => Helper\generate_csrf()),
|
||||
'menu' => 'config',
|
||||
'title' => t('User Profile')
|
||||
)));
|
||||
});
|
||||
|
||||
Router\post_action('profile', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$values = Request\values();
|
||||
Helper\check_csrf_values($values);
|
||||
list($valid, $errors) = Validator\User\validate_modification($values);
|
||||
|
||||
if ($valid) {
|
||||
$new_password = empty($values['password']) ? null : $values['password'];
|
||||
if (Model\User\update_user($user_id, $values['username'], $new_password)) {
|
||||
SessionStorage::getInstance()->setFlashMessage(t('Your preferences are updated.'));
|
||||
} else {
|
||||
SessionStorage::getInstance()->setFlashErrorMessage(t('Unable to update your preferences.'));
|
||||
}
|
||||
|
||||
Response\redirect('?action=profile');
|
||||
}
|
||||
|
||||
Response\html(Template\layout('profile', array(
|
||||
'errors' => $errors,
|
||||
'values' => Model\User\get_user_by_id_without_password($user_id) + array('csrf' => Helper\generate_csrf()),
|
||||
'menu' => 'config',
|
||||
'title' => t('User Profile')
|
||||
)));
|
||||
});
|
@ -1,39 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model;
|
||||
|
||||
// Display search results page
|
||||
Router\get_action('search', function() {
|
||||
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$text = Request\param('text', '');
|
||||
$offset = Request\int_param('offset', 0);
|
||||
|
||||
$items = array();
|
||||
$nb_items = 0;
|
||||
if ($text) {
|
||||
$items = Model\Search\get_all_items($text, $offset, Model\Config\get('items_per_page'));
|
||||
$nb_items = Model\Search\count_items($text);
|
||||
$items = Model\ItemSearch\get_all_items($user_id, $text, $offset, Helper\config('items_per_page'));
|
||||
$nb_items = Model\ItemSearch\count_items($user_id, $text);
|
||||
}
|
||||
|
||||
Response\html(Template\layout('search', array(
|
||||
'favicons' => Model\Favicon\get_item_favicons($items),
|
||||
'original_marks_read' => Model\Config\get('original_marks_read'),
|
||||
'favicons' => Model\Favicon\get_items_favicons($items),
|
||||
'original_marks_read' => Helper\config('original_marks_read'),
|
||||
'text' => $text,
|
||||
'items' => $items,
|
||||
'order' => '',
|
||||
'direction' => '',
|
||||
'display_mode' => Model\Config\get('items_display_mode'),
|
||||
'item_title_link' => Model\Config\get('item_title_link'),
|
||||
'display_mode' => Helper\config('items_display_mode'),
|
||||
'item_title_link' => Helper\config('item_title_link'),
|
||||
'group_id' => array(),
|
||||
'nb_items' => $nb_items,
|
||||
'nb_unread_items' => Model\Item\count_by_status('unread'),
|
||||
'offset' => $offset,
|
||||
'items_per_page' => Model\Config\get('items_per_page'),
|
||||
'items_per_page' => Helper\config('items_per_page'),
|
||||
'nothing_to_read' => Request\int_param('nothing_to_read'),
|
||||
'menu' => 'search',
|
||||
'title' => t('Search').' ('.$nb_items.')'
|
||||
|
38
app/controllers/services.php
Normal file
38
app/controllers/services.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Helper;
|
||||
|
||||
// Display bookmark services page
|
||||
Router\get_action('services', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
|
||||
Response\html(Template\layout('services', array(
|
||||
'errors' => array(),
|
||||
'values' => Model\Config\get_all($user_id) + array('csrf' => Helper\generate_csrf()),
|
||||
'menu' => 'config',
|
||||
'title' => t('Preferences')
|
||||
)));
|
||||
});
|
||||
|
||||
// Update bookmark services
|
||||
Router\post_action('services', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$values = Request\values() + array('pinboard_enabled' => 0, 'instapaper_enabled' => 0, 'wallabag_enabled' => 0);
|
||||
Helper\check_csrf_values($values);
|
||||
|
||||
if (Model\Config\save($user_id, $values)) {
|
||||
SessionStorage::getInstance()->setFlashMessage(t('Your preferences are updated.'));
|
||||
} else {
|
||||
SessionStorage::getInstance()->setFlashErrorMessage(t('Unable to update your preferences.'));
|
||||
}
|
||||
|
||||
Response\redirect('?action=services');
|
||||
});
|
134
app/controllers/users.php
Normal file
134
app/controllers/users.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Controller;
|
||||
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Template;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Validator;
|
||||
|
||||
Router\get_action('users', function () {
|
||||
if (! SessionStorage::getInstance()->isAdmin()) {
|
||||
Response\text('Access Forbidden', 403);
|
||||
}
|
||||
|
||||
Response\html(Template\layout('users', array(
|
||||
'users' => Model\User\get_all_users(),
|
||||
'menu' => 'config',
|
||||
'title' => t('Users'),
|
||||
)));
|
||||
});
|
||||
|
||||
Router\get_action('new-user', function () {
|
||||
if (! SessionStorage::getInstance()->isAdmin()) {
|
||||
Response\text('Access Forbidden', 403);
|
||||
}
|
||||
|
||||
Response\html(Template\layout('new_user', array(
|
||||
'values' => array('csrf' => Helper\generate_csrf()),
|
||||
'errors' => array(),
|
||||
'menu' => 'config',
|
||||
'title' => t('New User'),
|
||||
)));
|
||||
});
|
||||
|
||||
Router\post_action('new-user', function () {
|
||||
if (! SessionStorage::getInstance()->isAdmin()) {
|
||||
Response\text('Access Forbidden', 403);
|
||||
}
|
||||
|
||||
$values = Request\values() + array('is_admin' => 0);
|
||||
Helper\check_csrf_values($values);
|
||||
list($valid, $errors) = Validator\User\validate_creation($values);
|
||||
|
||||
if ($valid) {
|
||||
if (Model\User\create_user($values['username'], $values['password'], (bool) $values['is_admin'])) {
|
||||
SessionStorage::getInstance()->setFlashMessage(t('New user created successfully.'));
|
||||
} else {
|
||||
SessionStorage::getInstance()->setFlashErrorMessage(t('Unable to create this user.'));
|
||||
}
|
||||
|
||||
Response\redirect('?action=users');
|
||||
}
|
||||
|
||||
Response\html(Template\layout('new_user', array(
|
||||
'values' => $values + array('csrf' => Helper\generate_csrf()),
|
||||
'errors' => $errors,
|
||||
'menu' => 'config',
|
||||
'title' => t('New User'),
|
||||
)));
|
||||
});
|
||||
|
||||
Router\get_action('edit-user', function () {
|
||||
if (! SessionStorage::getInstance()->isAdmin()) {
|
||||
Response\text('Access Forbidden', 403);
|
||||
}
|
||||
|
||||
$user = Model\User\get_user_by_id_without_password(Request\int_param('user_id'));
|
||||
|
||||
if (empty($user)) {
|
||||
Response\redirect('?action=users');
|
||||
}
|
||||
|
||||
Response\html(Template\layout('edit_user', array(
|
||||
'values' => $user + array('csrf' => Helper\generate_csrf()),
|
||||
'errors' => array(),
|
||||
'menu' => 'config',
|
||||
'title' => t('Edit User'),
|
||||
)));
|
||||
});
|
||||
|
||||
Router\post_action('edit-user', function () {
|
||||
if (! SessionStorage::getInstance()->isAdmin()) {
|
||||
Response\text('Access Forbidden', 403);
|
||||
}
|
||||
|
||||
$values = Request\values() + array('is_admin' => 0);
|
||||
Helper\check_csrf_values($values);
|
||||
list($valid, $errors) = Validator\User\validate_modification($values);
|
||||
|
||||
if ($valid) {
|
||||
$new_password = empty($values['password']) ? null : $values['password'];
|
||||
$is_admin = $values['is_admin'] == 1 ? 1 : 0;
|
||||
if (Model\User\update_user($values['id'], $values['username'], $new_password, $is_admin)) {
|
||||
SessionStorage::getInstance()->setFlashMessage(t('User modified successfully.'));
|
||||
} else {
|
||||
SessionStorage::getInstance()->setFlashErrorMessage(t('Unable to edit this user.'));
|
||||
}
|
||||
|
||||
Response\redirect('?action=users');
|
||||
}
|
||||
|
||||
Response\html(Template\layout('edit_user', array(
|
||||
'values' => $values + array('csrf' => Helper\generate_csrf()),
|
||||
'errors' => $errors,
|
||||
'menu' => 'config',
|
||||
'title' => t('Edit User'),
|
||||
)));
|
||||
});
|
||||
|
||||
Router\get_action('confirm-remove-user', function () {
|
||||
if (! SessionStorage::getInstance()->isAdmin()) {
|
||||
Response\text('Access Forbidden', 403);
|
||||
}
|
||||
|
||||
Response\html(Template\layout('confirm_remove_user', array(
|
||||
'user' => Model\User\get_user_by_id_without_password(Request\int_param('user_id')),
|
||||
'csrf_token' => Helper\generate_csrf(),
|
||||
'menu' => 'config',
|
||||
'title' => t('Remove User'),
|
||||
)));
|
||||
});
|
||||
|
||||
Router\get_action('remove-user', function () {
|
||||
if (! SessionStorage::getInstance()->isAdmin() || ! Helper\check_csrf(Request\param('csrf'))) {
|
||||
Response\text('Access Forbidden', 403);
|
||||
}
|
||||
|
||||
Model\User\remove_user(Request\int_param('user_id'));
|
||||
Response\redirect('?action=users');
|
||||
});
|
@ -2,9 +2,13 @@
|
||||
|
||||
namespace Miniflux\Session;
|
||||
|
||||
use Miniflux\Helper;
|
||||
|
||||
class SessionManager
|
||||
{
|
||||
const SESSION_LIFETIME = 2678400;
|
||||
|
||||
function open($base_path = '/', $save_path = '', $session_lifetime = SESSION_LIFETIME)
|
||||
public static function open($base_path = '/', $save_path = '', $duration = self::SESSION_LIFETIME)
|
||||
{
|
||||
if ($save_path !== '') {
|
||||
session_save_path($save_path);
|
||||
@ -12,10 +16,10 @@ function open($base_path = '/', $save_path = '', $session_lifetime = SESSION_LIF
|
||||
|
||||
// HttpOnly and secure flags for session cookie
|
||||
session_set_cookie_params(
|
||||
$session_lifetime,
|
||||
$duration,
|
||||
$base_path ?: '/',
|
||||
null,
|
||||
isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on',
|
||||
Helper\is_secure_connection(),
|
||||
true
|
||||
);
|
||||
|
||||
@ -28,7 +32,7 @@ function open($base_path = '/', $save_path = '', $session_lifetime = SESSION_LIF
|
||||
ini_set('session.hash_bits_per_character', 6);
|
||||
|
||||
// Custom session name
|
||||
session_name('__$');
|
||||
session_name('MX_SID');
|
||||
|
||||
session_start();
|
||||
|
||||
@ -39,17 +43,114 @@ function open($base_path = '/', $save_path = '', $session_lifetime = SESSION_LIF
|
||||
}
|
||||
}
|
||||
|
||||
function close()
|
||||
public static function close()
|
||||
{
|
||||
session_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
function flash($message)
|
||||
|
||||
class SessionStorage
|
||||
{
|
||||
private static $instance = null;
|
||||
|
||||
public function __construct(array $session = null)
|
||||
{
|
||||
if (! isset($_SESSION)) {
|
||||
$_SESSION = array();
|
||||
}
|
||||
|
||||
$_SESSION = $session ?: $_SESSION;
|
||||
}
|
||||
|
||||
public static function getInstance(array $session = null)
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new static($session);
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function flush()
|
||||
{
|
||||
$_SESSION = array();
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function flushConfig()
|
||||
{
|
||||
unset($_SESSION['config']);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setConfig(array $config)
|
||||
{
|
||||
$_SESSION['config'] = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConfig()
|
||||
{
|
||||
return $this->getValue('config');
|
||||
}
|
||||
|
||||
public function setUser(array $user)
|
||||
{
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['is_admin'] = (bool) $user['is_admin'];
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserId()
|
||||
{
|
||||
return $this->getValue('user_id');
|
||||
}
|
||||
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->getValue('username');
|
||||
}
|
||||
|
||||
public function isAdmin()
|
||||
{
|
||||
return $this->getValue('is_admin');
|
||||
}
|
||||
|
||||
public function isLogged()
|
||||
{
|
||||
return $this->getValue('user_id') !== null;
|
||||
}
|
||||
|
||||
public function setFlashMessage($message)
|
||||
{
|
||||
$_SESSION['flash_message'] = $message;
|
||||
return $this;
|
||||
}
|
||||
|
||||
function flash_error($message)
|
||||
public function setFlashErrorMessage($message)
|
||||
{
|
||||
$_SESSION['flash_error_message'] = $message;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFlashMessage()
|
||||
{
|
||||
$message = $this->getValue('flash_message');
|
||||
unset($_SESSION['flash_message']);
|
||||
return $message;
|
||||
}
|
||||
|
||||
public function getFlashErrorMessage()
|
||||
{
|
||||
$message = $this->getValue('flash_error_message');
|
||||
unset($_SESSION['flash_error_message']);
|
||||
return $message;
|
||||
}
|
||||
|
||||
protected function getValue($key)
|
||||
{
|
||||
return isset($_SESSION[$key]) ? $_SESSION[$key] : null;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Miniflux\Template;
|
||||
|
||||
use Miniflux\Model;
|
||||
|
||||
const PATH = 'app/templates/';
|
||||
|
||||
// Template\load('template_name', ['bla' => 'value']);
|
||||
@ -30,5 +32,8 @@ function load()
|
||||
|
||||
function layout($template_name, array $template_args = array(), $layout_name = 'layout')
|
||||
{
|
||||
return load($layout_name, $template_args + array('content_for_layout' => load($template_name, $template_args)));
|
||||
return load(
|
||||
$layout_name,
|
||||
$template_args + array('content_for_layout' => load($template_name, $template_args))
|
||||
);
|
||||
}
|
||||
|
@ -24,3 +24,14 @@ function dt()
|
||||
{
|
||||
return call_user_func_array('\Miniflux\Translator\datetime', func_get_args());
|
||||
}
|
||||
|
||||
function get_cli_option($option, array $options)
|
||||
{
|
||||
$value = null;
|
||||
|
||||
if (! empty($options[$option]) && ctype_digit($options[$option])) {
|
||||
$value = (int) $options[$option];
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
161
app/handlers/feed.php
Normal file
161
app/handlers/feed.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Handler\Feed;
|
||||
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model;
|
||||
use PicoFeed;
|
||||
use PicoFeed\Config\Config as ReaderConfig;
|
||||
use PicoFeed\Logging\Logger;
|
||||
use PicoFeed\Reader\Reader;
|
||||
|
||||
function fetch_feed($url, $download_content = false, $etag = '', $last_modified = '')
|
||||
{
|
||||
$error_message = '';
|
||||
$feed = null;
|
||||
$resource = null;
|
||||
|
||||
try {
|
||||
$reader = new Reader(get_reader_config());
|
||||
$resource = $reader->discover($url, $last_modified, $etag);
|
||||
|
||||
if ($resource->isModified()) {
|
||||
$parser = $reader->getParser(
|
||||
$resource->getUrl(),
|
||||
$resource->getContent(),
|
||||
$resource->getEncoding()
|
||||
);
|
||||
|
||||
if ($download_content) {
|
||||
$parser->enableContentGrabber();
|
||||
}
|
||||
|
||||
$feed = $parser->execute();
|
||||
}
|
||||
} catch (PicoFeed\Client\InvalidCertificateException $e) {
|
||||
$error_message = t('Invalid SSL certificate.');
|
||||
} catch (PicoFeed\Client\InvalidUrlException $e) {
|
||||
$error_message = $e->getMessage();
|
||||
} catch (PicoFeed\Client\MaxRedirectException $e) {
|
||||
$error_message = t('Maximum number of HTTP redirection exceeded.');
|
||||
} catch (PicoFeed\Client\MaxSizeException $e) {
|
||||
$error_message = t('The content size exceeds to maximum allowed size.');
|
||||
} catch (PicoFeed\Client\TimeoutException $e) {
|
||||
$error_message = t('Connection timeout.');
|
||||
} catch (PicoFeed\Parser\MalformedXmlException $e) {
|
||||
$error_message = t('Feed is malformed.');
|
||||
} catch (PicoFeed\Reader\SubscriptionNotFoundException $e) {
|
||||
$error_message = t('Unable to find a subscription.');
|
||||
} catch (PicoFeed\Reader\UnsupportedFeedFormatException $e) {
|
||||
$error_message = t('Unable to detect the feed format.');
|
||||
}
|
||||
|
||||
return array($feed, $resource, $error_message);
|
||||
}
|
||||
|
||||
function create_feed($user_id, $url, $download_content = false, $rtl = false, $cloak_referrer = false, array $feed_group_ids = array(), $group_name = null)
|
||||
{
|
||||
$feed_id = null;
|
||||
list($feed, $resource, $error_message) = fetch_feed($url, $download_content);
|
||||
|
||||
if ($feed !== null) {
|
||||
$feed_id = Model\Feed\create(
|
||||
$user_id,
|
||||
$feed,
|
||||
$resource->getEtag(),
|
||||
$resource->getLastModified(),
|
||||
$rtl,
|
||||
$download_content,
|
||||
$cloak_referrer
|
||||
);
|
||||
|
||||
if ($feed_id === -1) {
|
||||
$error_message = t('This subscription already exists.');
|
||||
} else if ($feed_id === false) {
|
||||
$error_message = t('Unable to save this subscription in the database.');
|
||||
} else {
|
||||
Model\Favicon\create_feed_favicon($feed_id, $feed->getSiteUrl(), $feed->getIcon());
|
||||
|
||||
if (! empty($feed_group_ids)) {
|
||||
Model\Group\update_feed_groups($user_id, $feed_id, $feed_group_ids, $group_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($feed_id, $error_message);
|
||||
}
|
||||
|
||||
function update_feed($user_id, $feed_id)
|
||||
{
|
||||
$subscription = Model\Feed\get_feed($user_id, $feed_id);
|
||||
|
||||
list($feed, $resource, $error_message) = fetch_feed(
|
||||
$subscription['feed_url'],
|
||||
(bool) $subscription['download_content'],
|
||||
$subscription['etag'],
|
||||
$subscription['last_modified']
|
||||
);
|
||||
|
||||
if (! empty($error_message)) {
|
||||
Model\Feed\update_feed($user_id, $feed_id, array(
|
||||
'last_checked' => time(),
|
||||
'parsing_error' => 1,
|
||||
));
|
||||
|
||||
return false;
|
||||
} else {
|
||||
|
||||
Model\Feed\update_feed($user_id, $feed_id, array(
|
||||
'etag' => $resource->getEtag(),
|
||||
'last_modified' => $resource->getLastModified(),
|
||||
'last_checked' => time(),
|
||||
'parsing_error' => 0,
|
||||
));
|
||||
}
|
||||
|
||||
if ($feed !== null) {
|
||||
Model\Item\update_feed_items($user_id, $feed_id, $feed->getItems(), $subscription['rtl']);
|
||||
Model\Favicon\create_feed_favicon($feed_id, $feed->getSiteUrl(), $feed->getIcon());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function update_feeds($user_id, $limit = null)
|
||||
{
|
||||
foreach (Model\Feed\get_feed_ids($user_id, $limit) as $feed_id) {
|
||||
update_feed($user_id, $feed_id);
|
||||
}
|
||||
}
|
||||
|
||||
function get_reader_config()
|
||||
{
|
||||
$config = new ReaderConfig;
|
||||
$config->setTimezone(Helper\config('timezone'));
|
||||
|
||||
// Client
|
||||
$config->setClientTimeout(HTTP_TIMEOUT);
|
||||
$config->setClientUserAgent(HTTP_USER_AGENT);
|
||||
$config->setMaxBodySize(HTTP_MAX_RESPONSE_SIZE);
|
||||
|
||||
// Grabber
|
||||
$config->setGrabberRulesFolder(RULES_DIRECTORY);
|
||||
|
||||
// Proxy
|
||||
$config->setProxyHostname(PROXY_HOSTNAME);
|
||||
$config->setProxyPort(PROXY_PORT);
|
||||
$config->setProxyUsername(PROXY_USERNAME);
|
||||
$config->setProxyPassword(PROXY_PASSWORD);
|
||||
|
||||
// Filter
|
||||
$config->setFilterIframeWhitelist(Model\Config\get_iframe_whitelist());
|
||||
|
||||
// Parser
|
||||
$config->setParserHashAlgo('crc32b');
|
||||
|
||||
if (DEBUG_MODE) {
|
||||
Logger::enable();
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
33
app/handlers/item.php
Normal file
33
app/handlers/item.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Handler\Item;
|
||||
|
||||
use Miniflux\Handler;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model;
|
||||
use PicoDb\Database;
|
||||
|
||||
function download_item_content($user_id, $item_id)
|
||||
{
|
||||
$item = Model\Item\get_item($user_id, $item_id);
|
||||
$content = Handler\Scraper\download_content($item['url']);
|
||||
|
||||
if (! empty($content)) {
|
||||
if (! Helper\config('nocontent')) {
|
||||
Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('id', $item['id'])
|
||||
->save(array('content' => $content));
|
||||
}
|
||||
|
||||
return array(
|
||||
'result' => true,
|
||||
'content' => $content
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'result' => false,
|
||||
'content' => ''
|
||||
);
|
||||
}
|
@ -2,20 +2,20 @@
|
||||
|
||||
namespace Miniflux\Handler\Opml;
|
||||
|
||||
use Miniflux\Model\Feed;
|
||||
use Miniflux\Model\Group;
|
||||
use Miniflux\Model;
|
||||
use PicoDb\Database;
|
||||
use PicoFeed\Serialization\Subscription;
|
||||
use PicoFeed\Serialization\SubscriptionList;
|
||||
use PicoFeed\Serialization\SubscriptionListBuilder;
|
||||
use PicoFeed\Serialization\SubscriptionListParser;
|
||||
|
||||
|
||||
function export_all_feeds()
|
||||
function export_all_feeds($user_id)
|
||||
{
|
||||
$feeds = Feed\get_all();
|
||||
$feeds = Model\Feed\get_feeds($user_id);
|
||||
$subscriptionList = SubscriptionList::create()->setTitle(t('Subscriptions'));
|
||||
|
||||
foreach ($feeds as $feed) {
|
||||
$groups = Group\get_feed_groups($feed['id']);
|
||||
$groups = Model\Group\get_feed_groups($feed['id']);
|
||||
$category = '';
|
||||
|
||||
if (!empty($groups)) {
|
||||
@ -32,3 +32,36 @@ function export_all_feeds()
|
||||
|
||||
return SubscriptionListBuilder::create($subscriptionList)->build();
|
||||
}
|
||||
|
||||
function import_opml($user_id, $content)
|
||||
{
|
||||
$subscriptionList = SubscriptionListParser::create($content)->parse();
|
||||
|
||||
$db = Database::getInstance('db');
|
||||
$db->startTransaction();
|
||||
|
||||
foreach ($subscriptionList->subscriptions as $subscription) {
|
||||
if (! $db->table('feeds')->eq('user_id', $user_id)->eq('feed_url', $subscription->getFeedUrl())->exists()) {
|
||||
$db->table('feeds')->insert(array(
|
||||
'user_id' => $user_id,
|
||||
'title' => $subscription->getTitle(),
|
||||
'site_url' => $subscription->getSiteUrl(),
|
||||
'feed_url' => $subscription->getFeedUrl(),
|
||||
));
|
||||
|
||||
if ($subscription->getCategory() !== '') {
|
||||
$feed_id = $db->getLastId();
|
||||
$group_id = Model\Group\get_group_id_from_title($user_id, $subscription->getCategory());
|
||||
|
||||
if (empty($group_id)) {
|
||||
$group_id = Model\Group\create_group($user_id, $subscription->getCategory());
|
||||
}
|
||||
|
||||
Model\Group\associate_feed_groups($feed_id, array($group_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$db->closeTransaction();
|
||||
return true;
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
namespace Miniflux\Handler\Proxy;
|
||||
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model\Config;
|
||||
use PicoFeed\Client\ClientException;
|
||||
use PicoFeed\Config\Config as PicoFeedConfig;
|
||||
use PicoFeed\Filter\Filter;
|
||||
@ -51,15 +50,14 @@ function rewrite_html($html, $website, $proxy_images, $cloak_referrer)
|
||||
function download($url)
|
||||
{
|
||||
try {
|
||||
if ((bool) Config\get('debug_mode')) {
|
||||
if (DEBUG_MODE) {
|
||||
Logger::enable();
|
||||
}
|
||||
|
||||
$client = Client::getInstance();
|
||||
$client->setUserAgent(Config\HTTP_USER_AGENT);
|
||||
$client->setUserAgent(HTTP_USER_AGENT);
|
||||
$client->enablePassthroughMode();
|
||||
$client->execute($url);
|
||||
} catch (ClientException $e) {}
|
||||
|
||||
Config\write_debug();
|
||||
} catch (ClientException $e) {
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,13 @@
|
||||
namespace Miniflux\Handler\Scraper;
|
||||
|
||||
use PicoFeed\Scraper\Scraper;
|
||||
use Miniflux\Model\Config;
|
||||
use Miniflux\Handler;
|
||||
|
||||
function download_contents($url)
|
||||
function download_content($url)
|
||||
{
|
||||
$contents = '';
|
||||
|
||||
$scraper = new Scraper(Config\get_reader_config());
|
||||
$scraper = new Scraper(Handler\Feed\get_reader_config());
|
||||
$scraper->setUrl($url);
|
||||
$scraper->execute();
|
||||
|
||||
|
@ -2,24 +2,24 @@
|
||||
|
||||
namespace Miniflux\Handler\Service;
|
||||
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Helper;
|
||||
use PicoFeed\Client\Client;
|
||||
use PicoFeed\Client\ClientException;
|
||||
use Miniflux\Model\Config;
|
||||
use Miniflux\Model\Item;
|
||||
|
||||
function sync($item_id)
|
||||
function sync($user_id, $item_id)
|
||||
{
|
||||
$item = Item\get($item_id);
|
||||
$item = Model\Item\get_item($user_id, $item_id);
|
||||
|
||||
if ((bool) Config\get('pinboard_enabled')) {
|
||||
if (Helper\bool_config('pinboard_enabled')) {
|
||||
pinboard_sync($item);
|
||||
}
|
||||
|
||||
if ((bool) Config\get('instapaper_enabled')) {
|
||||
if (Helper\bool_config('instapaper_enabled')) {
|
||||
instapaper_sync($item);
|
||||
}
|
||||
|
||||
if ((bool) Config\get('wallabag_enabled')) {
|
||||
if (Helper\bool_config('wallabag_enabled')) {
|
||||
wallabag_sync($item);
|
||||
}
|
||||
}
|
||||
@ -27,8 +27,8 @@ function sync($item_id)
|
||||
function instapaper_sync(array $item)
|
||||
{
|
||||
$params = array(
|
||||
'username' => Config\get('instapaper_username'),
|
||||
'password' => Config\get('instapaper_password'),
|
||||
'username' => Helper\config('instapaper_username'),
|
||||
'password' => Helper\config('instapaper_password'),
|
||||
'url' => $item['url'],
|
||||
'title' => $item['title'],
|
||||
);
|
||||
@ -47,11 +47,11 @@ function instapaper_sync(array $item)
|
||||
function pinboard_sync(array $item)
|
||||
{
|
||||
$params = array(
|
||||
'auth_token' => Config\get('pinboard_token'),
|
||||
'auth_token' => Helper\config('pinboard_token'),
|
||||
'format' => 'json',
|
||||
'url' => $item['url'],
|
||||
'description' => $item['title'],
|
||||
'tags' => Config\get('pinboard_tags'),
|
||||
'tags' => Helper\config('pinboard_tags'),
|
||||
);
|
||||
|
||||
$url = 'https://api.pinboard.in/v1/posts/add?'.http_build_query($params);
|
||||
@ -79,7 +79,7 @@ function wallabag_has_url($url)
|
||||
if ($token === false) {
|
||||
return false;
|
||||
}
|
||||
$apiUrl = rtrim(Config\get('wallabag_url'), '\/') . '/api/entries/exists.json?url=' . urlencode($url);
|
||||
$apiUrl = rtrim(Helper\config('wallabag_url'), '\/') . '/api/entries/exists.json?url=' . urlencode($url);
|
||||
$headers = array('Authorization: Bearer ' . $token);
|
||||
$response = api_get_call($apiUrl, $headers);
|
||||
if ($response !== false) {
|
||||
@ -94,7 +94,7 @@ function wallabag_add_item($url, $title)
|
||||
if ($token === false) {
|
||||
return false;
|
||||
}
|
||||
$apiUrl = rtrim(Config\get('wallabag_url'), '\/') . '/api/entries.json';
|
||||
$apiUrl = rtrim(Helper\config('wallabag_url'), '\/') . '/api/entries.json';
|
||||
$headers = array('Authorization: Bearer ' . $token);
|
||||
$data = array(
|
||||
'url' => $url,
|
||||
@ -112,13 +112,13 @@ function wallabag_get_access_token()
|
||||
if (!empty($_SESSION['wallabag_access_token'])) {
|
||||
return $_SESSION['wallabag_access_token'];
|
||||
}
|
||||
$url = rtrim(Config\get('wallabag_url'), '\/') . '/oauth/v2/token';
|
||||
$url = rtrim(Helper\config('wallabag_url'), '\/') . '/oauth/v2/token';
|
||||
$data = array(
|
||||
'grant_type' => 'password',
|
||||
'client_id' => Config\get('wallabag_client_id'),
|
||||
'client_secret' => Config\get('wallabag_client_secret'),
|
||||
'username' => Config\get('wallabag_username'),
|
||||
'password' => Config\get('wallabag_password')
|
||||
'client_id' => Helper\config('wallabag_client_id'),
|
||||
'client_secret' => Helper\config('wallabag_client_secret'),
|
||||
'username' => Helper\config('wallabag_username'),
|
||||
'password' => Helper\config('wallabag_password')
|
||||
);
|
||||
$response = api_post_call($url, $data);
|
||||
if ($response !== false) {
|
||||
@ -135,7 +135,7 @@ function api_get_call($url, array $headers = array())
|
||||
{
|
||||
try {
|
||||
$client = Client::getInstance();
|
||||
$client->setUserAgent(Config\HTTP_USER_AGENT);
|
||||
$client->setUserAgent(HTTP_USER_AGENT);
|
||||
if ($headers) {
|
||||
$client->setHeaders($headers);
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Miniflux\Helper;
|
||||
|
||||
use PicoFeed\Logging\Logger;
|
||||
|
||||
function escape($value)
|
||||
{
|
||||
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
|
||||
@ -43,7 +45,11 @@ function get_current_base_url()
|
||||
{
|
||||
$url = is_secure_connection() ? 'https://' : 'http://';
|
||||
$url .= $_SERVER['HTTP_HOST'];
|
||||
|
||||
if (strpos($_SERVER['HTTP_HOST'], ':') === false) {
|
||||
$url .= $_SERVER['SERVER_PORT'] == 80 || $_SERVER['SERVER_PORT'] == 443 ? '' : ':'.$_SERVER['SERVER_PORT'];
|
||||
}
|
||||
|
||||
$url .= str_replace('\\', '/', dirname($_SERVER['PHP_SELF'])) !== '/' ? str_replace('\\', '/', dirname($_SERVER['PHP_SELF'])).'/' : '/';
|
||||
|
||||
return $url;
|
||||
@ -53,3 +59,9 @@ function is_secure_connection()
|
||||
{
|
||||
return ! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
|
||||
}
|
||||
|
||||
function write_debug_file() {
|
||||
if (DEBUG_MODE) {
|
||||
file_put_contents(DEBUG_FILENAME, implode(PHP_EOL, Logger::getMessages()), FILE_APPEND|LOCK_EX);
|
||||
}
|
||||
}
|
||||
|
38
app/helpers/config.php
Normal file
38
app/helpers/config.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Helper;
|
||||
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
|
||||
function config($parameter, $default = null)
|
||||
{
|
||||
$session = SessionStorage::getInstance();
|
||||
$cache = $session->getConfig();
|
||||
$value = null;
|
||||
|
||||
if (empty($cache)) {
|
||||
$cache = Model\Config\get_all($session->getUserId());
|
||||
$session->setConfig($cache);
|
||||
}
|
||||
|
||||
if (array_key_exists($parameter, $cache)) {
|
||||
$value = $cache[$parameter];
|
||||
}
|
||||
|
||||
if ($value === null) {
|
||||
$value = $default;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
function bool_config($parameter, $default = false)
|
||||
{
|
||||
return (bool) config($parameter, $default);
|
||||
}
|
||||
|
||||
function int_config($parameter, $default = false)
|
||||
{
|
||||
return (int) config($parameter, $default);
|
||||
}
|
@ -22,7 +22,7 @@ function favicon_extension($type)
|
||||
function favicon(array $favicons, $feed_id)
|
||||
{
|
||||
if (! empty($favicons[$feed_id])) {
|
||||
return '<img src="'.FAVICON_URL_PATH.'/'.$favicons[$feed_id]['hash'].favicon_extension($favicons[$feed_id]['type']).'" class="favicon"/>';
|
||||
return '<img src="'.FAVICON_URL_PATH.'/'.$favicons[$feed_id]['hash'].favicon_extension($favicons[$feed_id]['type']).'" class="favicon">';
|
||||
}
|
||||
|
||||
return '';
|
||||
|
@ -2,7 +2,17 @@
|
||||
|
||||
namespace Miniflux\Helper;
|
||||
|
||||
use Miniflux\Model\Config;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
|
||||
function get_user_id()
|
||||
{
|
||||
return SessionStorage::getInstance()->getUserId();
|
||||
}
|
||||
|
||||
function is_admin()
|
||||
{
|
||||
return SessionStorage::getInstance()->isAdmin();
|
||||
}
|
||||
|
||||
function flash($type, $html)
|
||||
{
|
||||
@ -16,14 +26,18 @@ function flash($type, $html)
|
||||
return $data;
|
||||
}
|
||||
|
||||
function is_rtl(array $item)
|
||||
function rtl(array $item)
|
||||
{
|
||||
return ! empty($item['rtl']) || \PicoFeed\Parser\Parser::isLanguageRTL($item['language']);
|
||||
if ($item['rtl'] == 1) {
|
||||
return 'dir="rtl"';
|
||||
}
|
||||
|
||||
return 'dir="ltr"';
|
||||
}
|
||||
|
||||
function css()
|
||||
{
|
||||
$theme = Config\get('theme');
|
||||
$theme = config('theme');
|
||||
|
||||
if ($theme !== 'original') {
|
||||
$css_file = THEME_DIRECTORY.'/'.$theme.'/css/app.css';
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
'Auto-Update URL' => ':تحديث تلقائي للميني فلكس من الرابط',
|
||||
'Update Miniflux' => 'تحديث برنامج Miniflux',
|
||||
'Miniflux is updated!' => 'بنجاح! Miniflux تمت عملية تحديث برنامج',
|
||||
'Unable to update Miniflux, check the console for errors.' => 'غير قادر على تحديث برنامج Miniflux لمزيد من المعلومات يرجى الذهاب إلى نافذة رسائل الإشعارات',
|
||||
'Don\'t forget to backup your database' => 'لاتنسى إنشاء نسخة إحتياطية من قاعدة البيانات',
|
||||
'The name must have only alpha-numeric characters' => 'يجب إدخال أحرف أو أرقام فقط',
|
||||
'New database' => 'إنشاء قاعدة بيانات جديده',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
'about' => 'حول البرنامج',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => 'سيتم إستبدال هذه النسخة من برنامج ميني فلكس بأحدث نسخه ... هل أنت متأكد من انك تريد ذلك؟ ?',
|
||||
'database' => 'قاعدة البيانات',
|
||||
'Console' => 'Console',
|
||||
'Miniflux API' => 'Miniflux API',
|
||||
'menu' => 'قائمة',
|
||||
'Default' => 'إفتراضي',
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
'Auto-Update URL' => 'URL automatické aktualizace',
|
||||
'Update Miniflux' => 'Aktualizovat Miniflux',
|
||||
'Miniflux is updated!' => 'Miniflux je aktualizovaný!',
|
||||
'Unable to update Miniflux, check the console for errors.' => 'Nelze aktualizovat Miniflux, zkontrolujte konzoli na chyby.',
|
||||
'Don\'t forget to backup your database' => 'Nezapomeňte zálohovat vaši databázi',
|
||||
'The name must have only alpha-numeric characters' => 'Jméno smí obsahovat pouze písmena a číslice',
|
||||
'New database' => 'Nová databáze',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
'about' => 'o',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => 'Tato akce aktualizuje Miniflux na poslední vývojovou verzi. Jste si jistí?',
|
||||
'database' => 'databáze',
|
||||
'Console' => 'konzole',
|
||||
'Miniflux API' => 'Miniflux API',
|
||||
'menu' => 'nabídka',
|
||||
'Default' => 'Výchozí',
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
'Auto-Update URL' => 'Auto-Update URL',
|
||||
'Update Miniflux' => 'Miniflux aktualisieren',
|
||||
'Miniflux is updated!' => 'Miniflux wurde erfolgreich aktualisiert!',
|
||||
'Unable to update Miniflux, check the console for errors.' => 'Aktualisierung von Miniflux fehlgeschlagen, überprüfe die Konsole nach Fehlermeldungen.',
|
||||
'Don\'t forget to backup your database' => 'Vergiss nicht, die Datenbank zu sichern',
|
||||
'The name must have only alpha-numeric characters' => 'Der Name darf nur alphanumerische Zeichen enthalten',
|
||||
'New database' => 'Neue Datenbank',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
'about' => 'über',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => 'Miniflux wird auf die aktuelle Entwicklungsversion aktualisiert. Bist du sicher?',
|
||||
'database' => 'Datenbank',
|
||||
'Console' => 'Konsole',
|
||||
'Miniflux API' => 'Miniflux API',
|
||||
'menu' => 'Menü',
|
||||
'Default' => 'Standard',
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
'Auto-Update URL' => 'Actualizar automáticamente la URL',
|
||||
'Update Miniflux' => 'Actualizar Miniflux',
|
||||
'Miniflux is updated!' => 'Miniflux esta actualizado',
|
||||
'Unable to update Miniflux, check the console for errors.' => 'No ha sido posible actualizar Miniflux, lea los errores en la consola',
|
||||
'Don\'t forget to backup your database' => 'No olvides de hacer una copia de seguridad de la base de datos',
|
||||
'The name must have only alpha-numeric characters' => 'El nombre sólo puede contener caractéres alfanuméricos',
|
||||
'New database' => 'Nueva base de datos',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
'about' => 'acerca de',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => 'Esta acción actualizará Miniflux a la última versión de desarrollo, ¿está seguro?',
|
||||
'database' => 'base de datos',
|
||||
'Console' => 'Consola',
|
||||
'Miniflux API' => 'API Miniflux',
|
||||
'menu' => 'menú',
|
||||
'Default' => 'Por defecto',
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
'Auto-Update URL' => 'URL de mise à jour automatique',
|
||||
'Update Miniflux' => 'Mettre à jour Miniflux',
|
||||
'Miniflux is updated!' => 'Miniflux a été mis à jour avec succès !',
|
||||
'Unable to update Miniflux, check the console for errors.' => 'Impossible de mettre à jour Miniflux, allez-voir les erreurs dans la console.',
|
||||
'Don\'t forget to backup your database' => 'N\'oubliez pas de sauvegarder votre base de données',
|
||||
'The name must have only alpha-numeric characters' => 'Le nom doit avoir seulement des caractères alphanumériques',
|
||||
'New database' => 'Nouvelle base de données',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
'about' => 'a propos',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => 'Cette action va mettre à jour Miniflux avec la dernière version en cours de développement, êtes-vous certain ?',
|
||||
'database' => 'base de données',
|
||||
'Console' => 'Console',
|
||||
'Miniflux API' => 'Miniflux API',
|
||||
'menu' => 'menu',
|
||||
'Default' => 'Défaut',
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
// 'Auto-Update URL' => '',
|
||||
// 'Update Miniflux' => '',
|
||||
// 'Miniflux is updated!' => '',
|
||||
// 'Unable to update Miniflux, check the console for errors.' => '',
|
||||
// 'Don\'t forget to backup your database' => '',
|
||||
// 'The name must have only alpha-numeric characters' => '',
|
||||
// 'New database' => '',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
// 'about' => '',
|
||||
// 'This action will update Miniflux with the last development version, are you sure?' => '',
|
||||
// 'database' => '',
|
||||
// 'Console' => '',
|
||||
// 'Miniflux API' => '',
|
||||
// 'menu' => '',
|
||||
// 'Default' => '',
|
||||
|
@ -159,7 +159,6 @@ return array(
|
||||
'Auto-Update URL' => '自動更新のURL',
|
||||
'Update Miniflux' => 'Minifluxを更新',
|
||||
'Miniflux is updated!' => 'Minifluxは更新されました!',
|
||||
'Unable to update Miniflux, check the console for errors.' => 'Minifluxを更新できません。エラーコンソールを確認してください。',
|
||||
'Don\'t forget to backup your database' => 'データベースのバックアップを忘れないで下さい',
|
||||
'The name must have only alpha-numeric characters' => '名前には英数字のみを使用することが出来ます',
|
||||
'New database' => '新しいデータベース',
|
||||
@ -202,7 +201,6 @@ return array(
|
||||
'about' => 'Minifluxについて',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => '最新の開発バージョンでMinifluxを更新します。よろしいですか?',
|
||||
'database' => 'データベース',
|
||||
'Console' => 'コンソール',
|
||||
'Miniflux API' => 'Miniflux API',
|
||||
'menu' => 'メニュー',
|
||||
'Default' => 'デフォルト',
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
'Auto-Update URL' => 'URL de atualização automática',
|
||||
'Update Miniflux' => 'Atualizar Miniflux',
|
||||
'Miniflux is updated!' => 'Miniflux foi atualizado!',
|
||||
'Unable to update Miniflux, check the console for errors.' => 'Incapaz de atualizar Miniflux, verifique o console para erros',
|
||||
'Don\'t forget to backup your database' => 'Não esqueça de fazer backup de seu banco de dados',
|
||||
'The name must have only alpha-numeric characters' => 'O nome deve conter apenas caracteres alfa-numéricos',
|
||||
'New database' => 'Novo banco de dados',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
'about' => 'sobre',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => 'Esta ação irá atualizar o Miniflux com a última versão de desenvolvimento, você tem certeza?',
|
||||
'database' => 'banco de dados',
|
||||
'Console' => 'Console',
|
||||
'Miniflux API' => 'API do Miniflux',
|
||||
'menu' => 'menu',
|
||||
'Default' => 'Padrão',
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
'Auto-Update URL' => 'URL автоматического обновления',
|
||||
'Update Miniflux' => 'Обновить Miniflux',
|
||||
'Miniflux is updated!' => 'Miniflux обновлен!',
|
||||
'Unable to update Miniflux, check the console for errors.' => 'Невозможно обновить Miniflux, смотрите ошибки в консоле.',
|
||||
'Don\'t forget to backup your database' => 'Не забудьте предварительно сделать резервную копию базы данных',
|
||||
'The name must have only alpha-numeric characters' => 'Название должно состоять только из алфавитно-цифровых символов',
|
||||
'New database' => 'Новая база данных',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
'about' => 'о программе',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => 'Это действие обновит Miniflux до последней разрабатываемой версии, вы уверены?',
|
||||
'database' => 'база данных',
|
||||
'Console' => 'Консоль',
|
||||
'Miniflux API' => 'Miniflux API',
|
||||
'menu' => 'меню',
|
||||
'Default' => 'По-умолчанию',
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
'Auto-Update URL' => 'УРЛ за аутоматско ажурирање',
|
||||
'Update Miniflux' => 'Ажурирај Минифлукс',
|
||||
'Miniflux is updated!' => 'Минифлукс је успешно ажуриран !',
|
||||
'Unable to update Miniflux, check the console for errors.' => 'Неуспешно ажурирање Минифлукса, проверите конзолу за списак грешака.',
|
||||
'Don\'t forget to backup your database' => 'Не заборавите да бекапујете базу података',
|
||||
'The name must have only alpha-numeric characters' => 'Име може садржати само бројеве или слова',
|
||||
'New database' => 'Нова база података',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
'about' => 'о програму',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => 'Ова акција ће ажурирати Минифлукс на најновију развојну верију, да ли сте сигурни?',
|
||||
'database' => 'база података',
|
||||
'Console' => 'Конзола',
|
||||
'Miniflux API' => 'АПИ Минифлукса',
|
||||
'menu' => 'мени',
|
||||
'Default' => 'Основна',
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
'Auto-Update URL' => 'URL za automatsko ažuriranje',
|
||||
'Update Miniflux' => 'Ažuriraj Miniflux',
|
||||
'Miniflux is updated!' => 'Miniflux je uspešno ažuriran !',
|
||||
'Unable to update Miniflux, check the console for errors.' => 'Neuspešno ažuriranje Minifluxa, proverite konzolu za spisak grešaka.',
|
||||
'Don\'t forget to backup your database' => 'Ne zaboravite da bekapujete bazu podataka',
|
||||
'The name must have only alpha-numeric characters' => 'Ime može sadržati samo brojeve ili slova',
|
||||
'New database' => 'Nova baza podataka',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
'about' => 'o programu',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => 'Ova akcija će ažurirati Miniflux na najnoviju razvojnu veriju, da li ste sigurni?',
|
||||
'database' => 'baza podataka',
|
||||
'Console' => 'Konzola',
|
||||
'Miniflux API' => 'API Minifluxa',
|
||||
'menu' => 'meni',
|
||||
'Default' => 'Osnovna',
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
'Auto-Update URL' => 'Otomatik güncelleme bağlantısı',
|
||||
'Update Miniflux' => 'Miniflux\'ı Güncelle',
|
||||
'Miniflux is updated!' => 'Miniflux güncellendi!',
|
||||
'Unable to update Miniflux, check the console for errors.' => 'Miniflux güncellenemiyor, hataları konsol üzerinden kontrol edin.',
|
||||
'Don\'t forget to backup your database' => 'Veritabanınızı yedeklemeyi unutmayın',
|
||||
'The name must have only alpha-numeric characters' => 'İsim yalnızca alfanümerik karakterler içermeli',
|
||||
'New database' => 'Yeni veritabanı',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
'about' => 'hakkında',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => 'Bu işlem Miniflux\'u en son yayınlanan geliştirme sürümüne güncelleyecektir, emin misiniz?',
|
||||
'database' => 'veritabanı',
|
||||
'Console' => 'Konsol',
|
||||
'Miniflux API' => 'Miniflux API',
|
||||
'menu' => 'menü',
|
||||
'Default' => 'Varsayılan',
|
||||
|
@ -157,7 +157,6 @@ return array(
|
||||
'Auto-Update URL' => '自动更新URL',
|
||||
'Update Miniflux' => '更新Miniflux',
|
||||
'Miniflux is updated!' => 'Miniflux已被更新!',
|
||||
'Unable to update Miniflux, check the console for errors.' => '无法更新Miniflux,检查控制台上的错误',
|
||||
'Don\'t forget to backup your database' => '不要忘记备份你的数据库',
|
||||
'The name must have only alpha-numeric characters' => '名字只能包含字母和数字',
|
||||
'New database' => '新数据库',
|
||||
@ -200,7 +199,6 @@ return array(
|
||||
'about' => '关于',
|
||||
'This action will update Miniflux with the last development version, are you sure?' => '这个操作将更新Miniflux到最新的开发版,你确认吗?',
|
||||
'database' => '数据库',
|
||||
'Console' => '控制台',
|
||||
'Miniflux API' => 'Miniflux API',
|
||||
'menu' => '菜单',
|
||||
'Default' => '默认',
|
||||
|
@ -48,8 +48,6 @@ function is_excluded_path($path, array $exclude_list)
|
||||
// Synchronize 2 directories (copy/remove files)
|
||||
function synchronize($source_directory, $destination_directory)
|
||||
{
|
||||
Config\debug('[SYNCHRONIZE] '.$source_directory.' to '.$destination_directory);
|
||||
|
||||
$src_files = get_files_list($source_directory);
|
||||
$dst_files = get_files_list($destination_directory);
|
||||
|
||||
@ -59,7 +57,6 @@ function synchronize($source_directory, $destination_directory)
|
||||
foreach ($remove_files as $file) {
|
||||
if ($file !== '.htaccess') {
|
||||
$destination_file = $destination_directory.DIRECTORY_SEPARATOR.$file;
|
||||
Config\debug('[REMOVE] '.$destination_file);
|
||||
|
||||
if (! @unlink($destination_file)) {
|
||||
return false;
|
||||
@ -72,8 +69,6 @@ function synchronize($source_directory, $destination_directory)
|
||||
$directory = $destination_directory.DIRECTORY_SEPARATOR.dirname($file);
|
||||
|
||||
if (! is_dir($directory)) {
|
||||
Config\debug('[MKDIR] '.$directory);
|
||||
|
||||
if (! @mkdir($directory, 0755, true)) {
|
||||
return false;
|
||||
}
|
||||
@ -82,8 +77,6 @@ function synchronize($source_directory, $destination_directory)
|
||||
$source_file = $source_directory.DIRECTORY_SEPARATOR.$file;
|
||||
$destination_file = $destination_directory.DIRECTORY_SEPARATOR.$file;
|
||||
|
||||
Config\debug('[COPY] '.$source_file.' to '.$destination_file);
|
||||
|
||||
if (! @copy($source_file, $destination_file)) {
|
||||
return false;
|
||||
}
|
||||
@ -96,9 +89,6 @@ function synchronize($source_directory, $destination_directory)
|
||||
function uncompress_archive($url, $download_directory = AUTO_UPDATE_DOWNLOAD_DIRECTORY, $archive_directory = AUTO_UPDATE_ARCHIVE_DIRECTORY)
|
||||
{
|
||||
$archive_file = $download_directory.DIRECTORY_SEPARATOR.'update.zip';
|
||||
|
||||
Config\debug('[DOWNLOAD] '.$url);
|
||||
|
||||
if (($data = @file_get_contents($url)) === false) {
|
||||
return false;
|
||||
}
|
||||
@ -107,8 +97,6 @@ function uncompress_archive($url, $download_directory = AUTO_UPDATE_DOWNLOAD_DIR
|
||||
return false;
|
||||
}
|
||||
|
||||
Config\debug('[UNZIP] '.$archive_file);
|
||||
|
||||
$zip = new ZipArchive;
|
||||
|
||||
if (! $zip->open($archive_file)) {
|
||||
@ -124,8 +112,6 @@ function uncompress_archive($url, $download_directory = AUTO_UPDATE_DOWNLOAD_DIR
|
||||
// Remove all files for a given directory
|
||||
function cleanup_directory($directory)
|
||||
{
|
||||
Config\debug('[CLEANUP] '.$directory);
|
||||
|
||||
$dir = new DirectoryIterator($directory);
|
||||
|
||||
foreach ($dir as $fileinfo) {
|
||||
@ -133,7 +119,6 @@ function cleanup_directory($directory)
|
||||
$filename = $fileinfo->getRealPath();
|
||||
|
||||
if ($fileinfo->isFile()) {
|
||||
Config\debug('[REMOVE] '.$filename);
|
||||
@unlink($filename);
|
||||
} else {
|
||||
cleanup_directory($filename);
|
||||
@ -165,14 +150,10 @@ function find_archive_root($base_directory = AUTO_UPDATE_ARCHIVE_DIRECTORY)
|
||||
}
|
||||
|
||||
if (empty($directory)) {
|
||||
Config\debug('[FIND ARCHIVE] No directory found');
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = $base_directory.DIRECTORY_SEPARATOR.$directory;
|
||||
Config\debug('[FIND ARCHIVE] '.$path);
|
||||
|
||||
return $path;
|
||||
return $base_directory.DIRECTORY_SEPARATOR.$directory;
|
||||
}
|
||||
|
||||
// Check if everything is setup correctly
|
||||
|
@ -2,60 +2,68 @@
|
||||
|
||||
namespace Miniflux\Model\Bookmark;
|
||||
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model;
|
||||
use PicoDb\Database;
|
||||
use Miniflux\Handler\Service;
|
||||
use Miniflux\Model\Config;
|
||||
|
||||
function count_items($feed_ids = array())
|
||||
function count_bookmarked_items($user_id, array $feed_ids = array())
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->table(Model\Item\TABLE)
|
||||
->eq('bookmark', 1)
|
||||
->eq('user_id', $user_id)
|
||||
->in('feed_id', $feed_ids)
|
||||
->in('status', array('read', 'unread'))
|
||||
->in('status', array(Model\Item\STATUS_READ, Model\Item\STATUS_UNREAD))
|
||||
->count();
|
||||
}
|
||||
|
||||
function get_all_items($offset = null, $limit = null, $feed_ids = array())
|
||||
function get_bookmarked_items($user_id, $offset = null, $limit = null, array $feed_ids = array())
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->table(Model\Item\TABLE)
|
||||
->columns(
|
||||
'items.id',
|
||||
'items.checksum',
|
||||
'items.title',
|
||||
'items.updated',
|
||||
'items.url',
|
||||
'items.enclosure',
|
||||
'items.enclosure_url',
|
||||
'items.enclosure_type',
|
||||
'items.bookmark',
|
||||
'items.status',
|
||||
'items.content',
|
||||
'items.feed_id',
|
||||
'items.language',
|
||||
'items.rtl',
|
||||
'items.author',
|
||||
'feeds.site_url',
|
||||
'feeds.title AS feed_title',
|
||||
'feeds.rtl'
|
||||
'feeds.title AS feed_title'
|
||||
)
|
||||
->join('feeds', 'id', 'feed_id')
|
||||
->in('feed_id', $feed_ids)
|
||||
->in('status', array('read', 'unread'))
|
||||
->eq('bookmark', 1)
|
||||
->orderBy('updated', Config\get('items_sorting_direction'))
|
||||
->join(Model\Feed\TABLE, 'id', 'feed_id')
|
||||
->eq('items.user_id', $user_id)
|
||||
->in('items.feed_id', $feed_ids)
|
||||
->neq('items.status', Model\Item\STATUS_REMOVED)
|
||||
->eq('items.bookmark', 1)
|
||||
->orderBy('items.updated', Helper\config('items_sorting_direction'))
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
function set_flag($id, $value)
|
||||
function get_bookmarked_item_ids($user_id)
|
||||
{
|
||||
if ($value == 1) {
|
||||
Service\sync($id);
|
||||
return Database::getInstance('db')
|
||||
->table(Model\Item\TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('bookmark', 1)
|
||||
->findAllByColumn('id');
|
||||
}
|
||||
|
||||
function set_flag($user_id, $item_id, $value)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('id', $id)
|
||||
->in('status', array('read', 'unread'))
|
||||
->save(array('bookmark' => $value));
|
||||
->table(Model\Item\TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('id', $item_id)
|
||||
->update(array('bookmark' => (int) $value));
|
||||
}
|
||||
|
@ -3,46 +3,12 @@
|
||||
namespace Miniflux\Model\Config;
|
||||
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Translator;
|
||||
use Miniflux\Model;
|
||||
use DirectoryIterator;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use PicoDb\Database;
|
||||
use PicoFeed\Config\Config as ReaderConfig;
|
||||
use PicoFeed\Logging\Logger;
|
||||
|
||||
const HTTP_USER_AGENT = 'Miniflux (https://miniflux.net)';
|
||||
|
||||
// Get PicoFeed config
|
||||
function get_reader_config()
|
||||
{
|
||||
$config = new ReaderConfig;
|
||||
$config->setTimezone(get('timezone'));
|
||||
|
||||
// Client
|
||||
$config->setClientTimeout(HTTP_TIMEOUT);
|
||||
$config->setClientUserAgent(HTTP_USER_AGENT);
|
||||
$config->setMaxBodySize(HTTP_MAX_RESPONSE_SIZE);
|
||||
|
||||
// Grabber
|
||||
$config->setGrabberRulesFolder(RULES_DIRECTORY);
|
||||
|
||||
// Proxy
|
||||
$config->setProxyHostname(PROXY_HOSTNAME);
|
||||
$config->setProxyPort(PROXY_PORT);
|
||||
$config->setProxyUsername(PROXY_USERNAME);
|
||||
$config->setProxyPassword(PROXY_PASSWORD);
|
||||
|
||||
// Filter
|
||||
$config->setFilterIframeWhitelist(get_iframe_whitelist());
|
||||
|
||||
if ((bool) get('debug_mode')) {
|
||||
Logger::enable();
|
||||
}
|
||||
|
||||
// Parser
|
||||
$config->setParserHashAlgo('crc32b');
|
||||
|
||||
return $config;
|
||||
}
|
||||
const TABLE = 'user_settings';
|
||||
|
||||
function get_iframe_whitelist()
|
||||
{
|
||||
@ -56,39 +22,21 @@ function get_iframe_whitelist()
|
||||
);
|
||||
}
|
||||
|
||||
// Send a debug message to the console
|
||||
function debug($line)
|
||||
{
|
||||
Logger::setMessage($line);
|
||||
write_debug();
|
||||
}
|
||||
|
||||
// Write PicoFeed debug output to a file
|
||||
function write_debug()
|
||||
{
|
||||
if ((bool) get('debug_mode')) {
|
||||
file_put_contents(DEBUG_FILENAME, implode(PHP_EOL, Logger::getMessages()));
|
||||
}
|
||||
}
|
||||
|
||||
// Get available timezone
|
||||
function get_timezones()
|
||||
{
|
||||
$timezones = timezone_identifiers_list();
|
||||
return array_combine(array_values($timezones), $timezones);
|
||||
}
|
||||
|
||||
// Returns true if the language is RTL
|
||||
function is_language_rtl()
|
||||
{
|
||||
$languages = array(
|
||||
'ar_AR'
|
||||
);
|
||||
|
||||
return in_array(get('language'), $languages);
|
||||
return in_array(Helper\config('language'), $languages);
|
||||
}
|
||||
|
||||
// Get all supported languages
|
||||
function get_languages()
|
||||
{
|
||||
return array(
|
||||
@ -109,7 +57,6 @@ function get_languages()
|
||||
);
|
||||
}
|
||||
|
||||
// Get all skins
|
||||
function get_themes()
|
||||
{
|
||||
$themes = array(
|
||||
@ -129,7 +76,6 @@ function get_themes()
|
||||
return $themes;
|
||||
}
|
||||
|
||||
// Sorting direction choices for items
|
||||
function get_sorting_directions()
|
||||
{
|
||||
return array(
|
||||
@ -138,26 +84,23 @@ function get_sorting_directions()
|
||||
);
|
||||
}
|
||||
|
||||
// Display summaries or full contents on lists
|
||||
function get_display_mode()
|
||||
{
|
||||
return array(
|
||||
'titles' => t('Titles'),
|
||||
'summaries' => t('Summaries'),
|
||||
'full' => t('Full contents')
|
||||
'full' => t('Full contents'),
|
||||
);
|
||||
}
|
||||
|
||||
// Item title links to original or full contents
|
||||
function get_item_title_link()
|
||||
{
|
||||
return array(
|
||||
'original' => t('Original'),
|
||||
'full' => t('Full contents')
|
||||
'full' => t('Full contents'),
|
||||
);
|
||||
}
|
||||
|
||||
// Autoflush choices for read items
|
||||
function get_autoflush_read_options()
|
||||
{
|
||||
return array(
|
||||
@ -166,11 +109,10 @@ function get_autoflush_read_options()
|
||||
'1' => t('After %d day', 1),
|
||||
'5' => t('After %d day', 5),
|
||||
'15' => t('After %d day', 15),
|
||||
'30' => t('After %d day', 30)
|
||||
'30' => t('After %d day', 30),
|
||||
);
|
||||
}
|
||||
|
||||
// Autoflush choices for unread items
|
||||
function get_autoflush_unread_options()
|
||||
{
|
||||
return array(
|
||||
@ -182,7 +124,6 @@ function get_autoflush_unread_options()
|
||||
);
|
||||
}
|
||||
|
||||
// Number of items per pages
|
||||
function get_paging_options()
|
||||
{
|
||||
return array(
|
||||
@ -197,7 +138,6 @@ function get_paging_options()
|
||||
);
|
||||
}
|
||||
|
||||
// Get redirect options when there is nothing to read
|
||||
function get_nothing_to_read_redirections()
|
||||
{
|
||||
return array(
|
||||
@ -207,74 +147,87 @@ function get_nothing_to_read_redirections()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Regenerate tokens for the API and bookmark feed
|
||||
function new_tokens()
|
||||
function get_default_values()
|
||||
{
|
||||
$values = array(
|
||||
'api_token' => Helper\generate_token(),
|
||||
'feed_token' => Helper\generate_token(),
|
||||
'bookmarklet_token' => Helper\generate_token(),
|
||||
'fever_token' => substr(Helper\generate_token(), 0, 8),
|
||||
return array(
|
||||
'language' => 'en_US',
|
||||
'timezone' => 'UTC',
|
||||
'theme' => 'original',
|
||||
'autoflush' => 15,
|
||||
'autoflush_unread' => 45,
|
||||
'frontend_updatecheck_interval' => 10,
|
||||
'favicons' => 1,
|
||||
'nocontent' => 0,
|
||||
'image_proxy' => 0,
|
||||
'original_marks_read' => 1,
|
||||
'instapaper_enabled' => 0,
|
||||
'pinboard_enabled' => 0,
|
||||
'pinboard_tags' => 'miniflux',
|
||||
'items_per_page' => 100,
|
||||
'items_display_mode' => 'summaries',
|
||||
'items_sorting_direction' => 'desc',
|
||||
'redirect_nothing_to_read' => 'feeds',
|
||||
'item_title_link' => 'full',
|
||||
);
|
||||
|
||||
return Database::getInstance('db')->hashtable('settings')->put($values);
|
||||
}
|
||||
|
||||
// Get a config value from the DB or from the session
|
||||
function get($name)
|
||||
function get_all($user_id)
|
||||
{
|
||||
if (! isset($_SESSION)) {
|
||||
return current(Database::getInstance('db')->hashtable('settings')->get($name));
|
||||
} else {
|
||||
if (! isset($_SESSION['config'][$name])) {
|
||||
$_SESSION['config'] = get_all();
|
||||
$settings = Database::getInstance('db')
|
||||
->hashtable(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->getAll('key', 'value');
|
||||
|
||||
if (empty($settings)) {
|
||||
save_defaults($user_id);
|
||||
$settings = Database::getInstance('db')
|
||||
->hashtable(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->getAll('key', 'value');
|
||||
}
|
||||
|
||||
if (isset($_SESSION['config'][$name])) {
|
||||
return $_SESSION['config'][$name];
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get all config parameters
|
||||
function get_all()
|
||||
function save_defaults($user_id)
|
||||
{
|
||||
$config = Database::getInstance('db')->hashtable('settings')->get();
|
||||
unset($config['password']);
|
||||
return $config;
|
||||
return save($user_id, get_default_values());
|
||||
}
|
||||
|
||||
// Save config into the database and update the session
|
||||
function save(array $values)
|
||||
function save($user_id, array $values)
|
||||
{
|
||||
// Update the password if needed
|
||||
if (! empty($values['password'])) {
|
||||
$values['password'] = password_hash($values['password'], PASSWORD_BCRYPT);
|
||||
} else {
|
||||
unset($values['password']);
|
||||
}
|
||||
$db = Database::getInstance('db');
|
||||
$results = array();
|
||||
$db->startTransaction();
|
||||
|
||||
unset($values['confirmation']);
|
||||
|
||||
// If the user does not want content of feeds, remove it in previous ones
|
||||
if (isset($values['nocontent']) && (bool) $values['nocontent']) {
|
||||
Database::getInstance('db')->table('items')->update(array('content' => ''));
|
||||
$db
|
||||
->table(Model\Item\TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->update(array('content' => ''));
|
||||
}
|
||||
|
||||
if (Database::getInstance('db')->hashtable('settings')->put($values)) {
|
||||
reload();
|
||||
return true;
|
||||
foreach ($values as $key => $value) {
|
||||
if ($db->table(TABLE)->eq('user_id', $user_id)->eq('key', $key)->exists()) {
|
||||
$results[] = $db->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('key', $key)
|
||||
->update(array('value' => $value));
|
||||
} else {
|
||||
$results[] = $db->table(TABLE)->insert(array(
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'user_id' => $user_id,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array(false, $results, true)) {
|
||||
$db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reload the cache in session
|
||||
function reload()
|
||||
{
|
||||
$_SESSION['config'] = get_all();
|
||||
Translator\load(get('language'));
|
||||
$db->closeTransaction();
|
||||
SessionStorage::getInstance()->flushConfig();
|
||||
return true;
|
||||
}
|
||||
|
@ -1,102 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Model\Database;
|
||||
|
||||
use DirectoryIterator;
|
||||
use Miniflux\Schema;
|
||||
use Miniflux\Model\Config;
|
||||
|
||||
// Create a new database for a new user
|
||||
function create($filename, $username, $password)
|
||||
{
|
||||
$filename = DATA_DIRECTORY.DIRECTORY_SEPARATOR.$filename;
|
||||
|
||||
if (ENABLE_MULTIPLE_DB && ! file_exists($filename)) {
|
||||
$db = new \PicoDb\Database(array(
|
||||
'driver' => 'sqlite',
|
||||
'filename' => $filename,
|
||||
));
|
||||
|
||||
if ($db->schema('\Miniflux\Schema')->check(Schema\VERSION)) {
|
||||
$credentials = array(
|
||||
'username' => $username,
|
||||
'password' => password_hash($password, PASSWORD_BCRYPT)
|
||||
);
|
||||
|
||||
$db->hashtable('settings')->put($credentials);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get or set the current database
|
||||
function select($filename = '')
|
||||
{
|
||||
static $current_filename = DB_FILENAME;
|
||||
|
||||
// function gets called with a filename at least once the database
|
||||
// connection is established
|
||||
if (! empty($filename)) {
|
||||
if (ENABLE_MULTIPLE_DB && in_array($filename, get_all())) {
|
||||
$current_filename = $filename;
|
||||
|
||||
// unset the authenticated flag if the database is changed
|
||||
if (empty($_SESSION['database']) || $_SESSION['database'] !== $filename) {
|
||||
if (isset($_SESSION)) {
|
||||
unset($_SESSION['loggedin']);
|
||||
}
|
||||
|
||||
$_SESSION['database'] = $filename;
|
||||
$_SESSION['config'] = Config\get_all();
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $current_filename;
|
||||
}
|
||||
|
||||
// Get database path
|
||||
function get_path()
|
||||
{
|
||||
return DATA_DIRECTORY.DIRECTORY_SEPARATOR.select();
|
||||
}
|
||||
|
||||
// Get the list of available databases
|
||||
function get_all()
|
||||
{
|
||||
$listing = array();
|
||||
|
||||
$dir = new DirectoryIterator(DATA_DIRECTORY);
|
||||
|
||||
foreach ($dir as $fileinfo) {
|
||||
$filename = $fileinfo->getFilename();
|
||||
if (preg_match('/sqlite$/', $filename)) {
|
||||
$listing[] = $filename;
|
||||
}
|
||||
}
|
||||
|
||||
return $listing;
|
||||
}
|
||||
|
||||
// Get the formated db list
|
||||
function get_list()
|
||||
{
|
||||
$listing = array();
|
||||
|
||||
foreach (get_all() as $filename) {
|
||||
if ($filename === DB_FILENAME) {
|
||||
$label = t('Default database');
|
||||
} else {
|
||||
$label = ucfirst(substr($filename, 0, -7));
|
||||
}
|
||||
|
||||
$listing[$filename] = $label;
|
||||
}
|
||||
|
||||
return $listing;
|
||||
}
|
@ -2,44 +2,38 @@
|
||||
|
||||
namespace Miniflux\Model\Favicon;
|
||||
|
||||
use Miniflux\Model\Config;
|
||||
use Miniflux\Model\Group;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model;
|
||||
use PicoDb\Database;
|
||||
use PicoFeed\Reader\Favicon;
|
||||
|
||||
// Create a favicons
|
||||
const TABLE = 'favicons';
|
||||
const JOIN_TABLE = 'favicons_feeds';
|
||||
|
||||
function create_feed_favicon($feed_id, $site_url, $icon_link)
|
||||
{
|
||||
if (has_favicon($feed_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$favicon = fetch($feed_id, $site_url, $icon_link);
|
||||
|
||||
$favicon = fetch_favicon($feed_id, $site_url, $icon_link);
|
||||
if ($favicon === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$favicon_id = store($favicon->getType(), $favicon->getContent());
|
||||
|
||||
$favicon_id = store_favicon($favicon->getType(), $favicon->getContent());
|
||||
if ($favicon_id === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Database::getInstance('db')
|
||||
->table('favicons_feeds')
|
||||
->table(JOIN_TABLE)
|
||||
->save(array(
|
||||
'feed_id' => $feed_id,
|
||||
'favicon_id' => $favicon_id
|
||||
));
|
||||
}
|
||||
|
||||
// Download a favicon
|
||||
function fetch($feed_id, $site_url, $icon_link)
|
||||
function fetch_favicon($feed_id, $site_url, $icon_link)
|
||||
{
|
||||
if (Config\get('favicons') == 1 && ! has_favicon($feed_id)) {
|
||||
$favicon = new Favicon;
|
||||
if (Helper\bool_config('favicons') && ! has_favicon($feed_id)) {
|
||||
$favicon = new Favicon();
|
||||
$favicon->find($site_url, $icon_link);
|
||||
return $favicon;
|
||||
}
|
||||
@ -47,145 +41,125 @@ function fetch($feed_id, $site_url, $icon_link)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the favicon (only if it does not exist yet)
|
||||
function store($type, $icon)
|
||||
function store_favicon($mime_type, $blob)
|
||||
{
|
||||
if ($icon === '') {
|
||||
if (empty($blob)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hash = sha1($icon);
|
||||
|
||||
$hash = sha1($blob);
|
||||
$favicon_id = get_favicon_id($hash);
|
||||
|
||||
if ($favicon_id) {
|
||||
return $favicon_id;
|
||||
}
|
||||
|
||||
$file = $hash.Helper\favicon_extension($type);
|
||||
|
||||
if (file_put_contents(FAVICON_DIRECTORY.DIRECTORY_SEPARATOR.$file, $icon) === false) {
|
||||
$file = $hash.Helper\favicon_extension($mime_type);
|
||||
if (file_put_contents(FAVICON_DIRECTORY.DIRECTORY_SEPARATOR.$file, $blob) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$saved = Database::getInstance('db')
|
||||
->table('favicons')
|
||||
->save(array(
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->persist(array(
|
||||
'hash' => $hash,
|
||||
'type' => $type
|
||||
'type' => $mime_type
|
||||
));
|
||||
|
||||
if ($saved === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return get_favicon_id($hash);
|
||||
function get_favicon_data_url($filename, $mime_type)
|
||||
{
|
||||
$blob = base64_encode(file_get_contents(FAVICON_DIRECTORY.DIRECTORY_SEPARATOR.$filename));
|
||||
return sprintf('data:%s;base64,%s', $mime_type, $blob);
|
||||
}
|
||||
|
||||
function get_favicon_id($hash)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('favicons')
|
||||
->table(TABLE)
|
||||
->eq('hash', $hash)
|
||||
->findOneColumn('id');
|
||||
}
|
||||
|
||||
// Delete the favicon
|
||||
function delete_favicon($favicon)
|
||||
function delete_favicon(array $favicon)
|
||||
{
|
||||
unlink(FAVICON_DIRECTORY.DIRECTORY_SEPARATOR.$favicon['hash'].Helper\favicon_extension($favicon['type']));
|
||||
|
||||
Database::getInstance('db')
|
||||
->table('favicons')
|
||||
->table(TABLE)
|
||||
->eq('hash', $favicon['hash'])
|
||||
->remove();
|
||||
}
|
||||
|
||||
// Purge orphaned favicons from database
|
||||
function purge_favicons()
|
||||
function has_favicon($feed_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table(JOIN_TABLE)
|
||||
->eq('feed_id', $feed_id)
|
||||
->exists();
|
||||
}
|
||||
|
||||
function get_favicons_by_feed_ids(array $feed_ids)
|
||||
{
|
||||
$result = array();
|
||||
|
||||
if (! Helper\bool_config('favicons')) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$favicons = Database::getInstance('db')
|
||||
->table('favicons')
|
||||
->table(TABLE)
|
||||
->columns(
|
||||
'favicons.type',
|
||||
'favicons.hash',
|
||||
'favicons_feeds.feed_id'
|
||||
)
|
||||
->join('favicons_feeds', 'favicon_id', 'id')
|
||||
->isNull('favicons_feeds.feed_id')
|
||||
->in('favicons_feeds.feed_id', $feed_ids)
|
||||
->findAll();
|
||||
|
||||
foreach ($favicons as $favicon) {
|
||||
delete_favicon($favicon);
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if the feed has a favicon
|
||||
function has_favicon($feed_id)
|
||||
{
|
||||
return Database::getInstance('db')->table('favicons_feeds')->eq('feed_id', $feed_id)->count() === 1;
|
||||
}
|
||||
|
||||
// Get favicons for those feeds
|
||||
function get_favicons(array $feed_ids)
|
||||
{
|
||||
if (Config\get('favicons') == 0) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ($feed_ids as $feed_id) {
|
||||
$result[$feed_id] = Database::getInstance('db')
|
||||
->table('favicons')
|
||||
->columns(
|
||||
'favicons.type',
|
||||
'favicons.hash'
|
||||
)
|
||||
->join('favicons_feeds', 'favicon_id', 'id')
|
||||
->eq('favicons_feeds.feed_id', $feed_id)
|
||||
->findOne();
|
||||
$result[$favicon['feed_id']] = $favicon;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Get all favicons for a list of items
|
||||
function get_item_favicons(array $items)
|
||||
function get_items_favicons(array $items)
|
||||
{
|
||||
$feed_ids = array();
|
||||
|
||||
foreach ($items as $item) {
|
||||
$feed_ids[$item['feed_id']] = $item['feed_id'];
|
||||
$feed_ids[] = $item['feed_id'];
|
||||
}
|
||||
|
||||
return get_favicons($feed_ids);
|
||||
return get_favicons_by_feed_ids(array_unique($feed_ids));
|
||||
}
|
||||
|
||||
// Get all favicons
|
||||
function get_all_favicons()
|
||||
function get_feeds_favicons(array $feeds)
|
||||
{
|
||||
if (Config\get('favicons') == 0) {
|
||||
return array();
|
||||
$feed_ids = array();
|
||||
|
||||
foreach ($feeds as $feed) {
|
||||
$feed_ids[] = $feed['id'];
|
||||
}
|
||||
|
||||
$result = Database::getInstance('db')
|
||||
->table('favicons')
|
||||
->columns(
|
||||
'favicons_feeds.feed_id',
|
||||
'favicons.type',
|
||||
'favicons.hash'
|
||||
)
|
||||
->join('favicons_feeds', 'favicon_id', 'id')
|
||||
return get_favicons_by_feed_ids($feed_ids);
|
||||
}
|
||||
|
||||
function get_favicons_with_data_url($user_id)
|
||||
{
|
||||
$favicons = Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->columns(JOIN_TABLE.'.feed_id', TABLE.'.file', TABLE.'.type')
|
||||
->join(JOIN_TABLE, 'favicon_id', 'id', TABLE)
|
||||
->join(Model\Feed\TABLE, 'id', 'feed_id')
|
||||
->eq(Model\Feed\TABLE.'.user_id', $user_id)
|
||||
->findAll();
|
||||
|
||||
$map = array();
|
||||
|
||||
foreach ($result as $row) {
|
||||
$map[$row['feed_id']] = array(
|
||||
"type" => $row['type'],
|
||||
"hash" => $row['hash']
|
||||
);
|
||||
foreach ($favicons as &$favicon) {
|
||||
$favicon['url'] = get_favicon_data_url($favicon['file'], $favicon['mime_type']);
|
||||
}
|
||||
|
||||
return $map;
|
||||
return $favicons;
|
||||
}
|
||||
|
@ -2,251 +2,65 @@
|
||||
|
||||
namespace Miniflux\Model\Feed;
|
||||
|
||||
use UnexpectedValueException;
|
||||
use Miniflux\Model\Config;
|
||||
use Miniflux\Model\Item;
|
||||
use Miniflux\Model\Group;
|
||||
use Miniflux\Model\Favicon;
|
||||
use Miniflux\Helper;
|
||||
use PicoDb\Database;
|
||||
use PicoFeed\Reader\Reader;
|
||||
use PicoFeed\PicoFeedException;
|
||||
use PicoFeed\Serialization\SubscriptionListParser;
|
||||
use PicoFeed\Parser\Feed;
|
||||
|
||||
const LIMIT_ALL = -1;
|
||||
const STATUS_ACTIVE = 1;
|
||||
const STATUS_INACTIVE = 0;
|
||||
const TABLE = 'feeds';
|
||||
|
||||
// Update feed information
|
||||
function update(array $values)
|
||||
function create($user_id, Feed $feed, $etag, $last_modified, $rtl = false, $scraper = false, $cloak_referrer = false)
|
||||
{
|
||||
Database::getInstance('db')->startTransaction();
|
||||
|
||||
$result = Database::getInstance('db')
|
||||
->table('feeds')
|
||||
->eq('id', $values['id'])
|
||||
->save(array(
|
||||
'title' => $values['title'],
|
||||
'site_url' => $values['site_url'],
|
||||
'feed_url' => $values['feed_url'],
|
||||
'enabled' => $values['enabled'],
|
||||
'rtl' => $values['rtl'],
|
||||
'download_content' => $values['download_content'],
|
||||
'cloak_referrer' => $values['cloak_referrer'],
|
||||
'parsing_error' => 0,
|
||||
));
|
||||
|
||||
if ($result) {
|
||||
if (! Group\update_feed_groups($values['id'], $values['feed_group_ids'], $values['create_group'])) {
|
||||
Database::getInstance('db')->cancelTransaction();
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
|
||||
Database::getInstance('db')->closeTransaction();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Import OPML file
|
||||
function import_opml($content)
|
||||
{
|
||||
$subscriptionList = SubscriptionListParser::create($content)->parse();
|
||||
|
||||
$db = Database::getInstance('db');
|
||||
$db->startTransaction();
|
||||
|
||||
foreach ($subscriptionList->subscriptions as $subscription) {
|
||||
if (! $db->table('feeds')->eq('feed_url', $subscription->getFeedUrl())->exists()) {
|
||||
$db->table('feeds')->insert(array(
|
||||
'title' => $subscription->getTitle(),
|
||||
'site_url' => $subscription->getSiteUrl(),
|
||||
'feed_url' => $subscription->getFeedUrl(),
|
||||
));
|
||||
|
||||
if ($subscription->getCategory() !== '') {
|
||||
$feed_id = $db->getLastId();
|
||||
$group_id = Group\get_group_id($subscription->getCategory());
|
||||
|
||||
if (empty($group_id)) {
|
||||
$group_id = Group\create($subscription->getCategory());
|
||||
}
|
||||
|
||||
Group\add($feed_id, array($group_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$db->closeTransaction();
|
||||
Config\write_debug();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add a new feed from an URL
|
||||
function create($url, $enable_grabber = false, $force_rtl = false, $cloak_referrer = false, $group_ids = array(), $create_group = '')
|
||||
{
|
||||
$feed_id = false;
|
||||
|
||||
$db = Database::getInstance('db');
|
||||
|
||||
// Discover the feed
|
||||
$reader = new Reader(Config\get_reader_config());
|
||||
$resource = $reader->discover($url);
|
||||
|
||||
// Feed already there
|
||||
if ($db->table('feeds')->eq('feed_url', $resource->getUrl())->count()) {
|
||||
throw new UnexpectedValueException;
|
||||
if ($db->table('feeds')->eq('user_id', $user_id)->eq('feed_url', $feed->getFeedUrl())->exists()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parse the feed
|
||||
$parser = $reader->getParser(
|
||||
$resource->getUrl(),
|
||||
$resource->getContent(),
|
||||
$resource->getEncoding()
|
||||
);
|
||||
|
||||
if ($enable_grabber) {
|
||||
$parser->enableContentGrabber();
|
||||
}
|
||||
|
||||
$feed = $parser->execute();
|
||||
|
||||
// Save the feed
|
||||
$result = $db->table('feeds')->save(array(
|
||||
$feed_id = $db
|
||||
->table(TABLE)
|
||||
->persist(array(
|
||||
'user_id' => $user_id,
|
||||
'title' => $feed->getTitle(),
|
||||
'site_url' => $feed->getSiteUrl(),
|
||||
'feed_url' => $feed->getFeedUrl(),
|
||||
'download_content' => $enable_grabber ? 1 : 0,
|
||||
'rtl' => $force_rtl ? 1 : 0,
|
||||
'last_modified' => $resource->getLastModified(),
|
||||
'download_content' => $scraper ? 1 : 0,
|
||||
'rtl' => $rtl ? 1 : 0,
|
||||
'etag' => $etag,
|
||||
'last_modified' => $last_modified,
|
||||
'last_checked' => time(),
|
||||
'etag' => $resource->getEtag(),
|
||||
'cloak_referrer' => $cloak_referrer ? 1 : 0,
|
||||
));
|
||||
|
||||
if ($result) {
|
||||
$feed_id = $db->getLastId();
|
||||
|
||||
Group\update_feed_groups($feed_id, $group_ids, $create_group);
|
||||
Item\update_all($feed_id, $feed->getItems());
|
||||
Favicon\create_feed_favicon($feed_id, $feed->getSiteUrl(), $feed->getIcon());
|
||||
if ($feed_id !== false) {
|
||||
Item\update_feed_items($user_id, $feed_id, $feed->getItems(), $rtl);
|
||||
}
|
||||
|
||||
return $feed_id;
|
||||
}
|
||||
|
||||
// Refresh all feeds
|
||||
function refresh_all($limit = LIMIT_ALL)
|
||||
{
|
||||
foreach (get_ids($limit) as $feed_id) {
|
||||
refresh($feed_id);
|
||||
}
|
||||
|
||||
// Auto-vacuum for people using the cronjob
|
||||
Database::getInstance('db')->getConnection()->exec('VACUUM');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Refresh one feed
|
||||
function refresh($feed_id)
|
||||
{
|
||||
try {
|
||||
$feed = get($feed_id);
|
||||
|
||||
if (empty($feed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$reader = new Reader(Config\get_reader_config());
|
||||
|
||||
$resource = $reader->download(
|
||||
$feed['feed_url'],
|
||||
$feed['last_modified'],
|
||||
$feed['etag']
|
||||
);
|
||||
|
||||
// Update the `last_checked` column each time, HTTP cache or not
|
||||
update_last_checked($feed_id);
|
||||
|
||||
// Feed modified
|
||||
if ($resource->isModified()) {
|
||||
$parser = $reader->getParser(
|
||||
$resource->getUrl(),
|
||||
$resource->getContent(),
|
||||
$resource->getEncoding()
|
||||
);
|
||||
|
||||
if ($feed['download_content']) {
|
||||
$parser->enableContentGrabber();
|
||||
|
||||
// Don't fetch previous items, only new one
|
||||
$parser->setGrabberIgnoreUrls(
|
||||
Database::getInstance('db')->table('items')->eq('feed_id', $feed_id)->findAllByColumn('url')
|
||||
);
|
||||
}
|
||||
|
||||
$feed = $parser->execute();
|
||||
|
||||
update_cache($feed_id, $resource->getLastModified(), $resource->getEtag());
|
||||
|
||||
Item\update_all($feed_id, $feed->getItems());
|
||||
Favicon\create_feed_favicon($feed_id, $feed->getSiteUrl(), $feed->getIcon());
|
||||
}
|
||||
|
||||
update_parsing_error($feed_id, 0);
|
||||
Config\write_debug();
|
||||
|
||||
return true;
|
||||
} catch (PicoFeedException $e) {
|
||||
}
|
||||
|
||||
update_parsing_error($feed_id, 1);
|
||||
Config\write_debug();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the list of feeds ID to refresh
|
||||
function get_ids($limit = LIMIT_ALL)
|
||||
{
|
||||
$query = Database::getInstance('db')->table('feeds')->eq('enabled', 1)->asc('last_checked');
|
||||
|
||||
if ($limit !== LIMIT_ALL) {
|
||||
$query->limit((int) $limit);
|
||||
}
|
||||
|
||||
return $query->findAllByColumn('id');
|
||||
}
|
||||
|
||||
// get number of feeds with errors
|
||||
function count_failed_feeds()
|
||||
function get_feeds($user_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('feeds')
|
||||
->eq('parsing_error', '1')
|
||||
->count();
|
||||
}
|
||||
|
||||
// Get all feeds
|
||||
function get_all()
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('feeds')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->asc('title')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
// Get all feeds with the number unread/total items in the order failed, working, disabled
|
||||
function get_all_item_counts()
|
||||
function get_feeds_with_items_count($user_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('feeds')
|
||||
->table(TABLE)
|
||||
->columns(
|
||||
'feeds.*',
|
||||
'SUM(CASE WHEN items.status IN ("unread") THEN 1 ELSE 0 END) as "items_unread"',
|
||||
'SUM(CASE WHEN items.status IN ("read", "unread") THEN 1 ELSE 0 END) as "items_total"'
|
||||
)
|
||||
->join('items', 'feed_id', 'id')
|
||||
->eq('feeds.user_id', $user_id)
|
||||
->groupBy('feeds.id')
|
||||
->desc('feeds.parsing_error')
|
||||
->desc('feeds.enabled')
|
||||
@ -254,98 +68,94 @@ function get_all_item_counts()
|
||||
->findAll();
|
||||
}
|
||||
|
||||
// Get unread/total count for one feed
|
||||
function count_items($feed_id)
|
||||
function get_feed_ids($user_id, $limit = null)
|
||||
{
|
||||
$counts = Database::getInstance('db')
|
||||
->table('items')
|
||||
->columns('status', 'count(*) as item_count')
|
||||
->in('status', array('read', 'unread'))
|
||||
->eq('feed_id', $feed_id)
|
||||
->groupBy('status')
|
||||
->findAll();
|
||||
$query = Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('enabled', STATUS_ACTIVE)
|
||||
->asc('last_checked')
|
||||
->asc('id');
|
||||
|
||||
$result = array(
|
||||
'items_unread' => 0,
|
||||
'items_total' => 0,
|
||||
);
|
||||
|
||||
foreach ($counts as &$count) {
|
||||
if ($count['status'] === 'unread') {
|
||||
$result['items_unread'] = (int) $count['item_count'];
|
||||
if ($limit !== null) {
|
||||
$query->limit($limit);
|
||||
}
|
||||
|
||||
$result['items_total'] += $count['item_count'];
|
||||
return $query->findAllByColumn('id');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Get one feed
|
||||
function get($feed_id)
|
||||
function get_feed($user_id, $feed_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('feeds')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('id', $feed_id)
|
||||
->findOne();
|
||||
}
|
||||
|
||||
// Update parsing error column
|
||||
function update_parsing_error($feed_id, $value)
|
||||
function update_feed($user_id, $feed_id, array $values)
|
||||
{
|
||||
Database::getInstance('db')->table('feeds')->eq('id', $feed_id)->save(array('parsing_error' => $value));
|
||||
}
|
||||
$db = Database::getInstance('db');
|
||||
$db->startTransaction();
|
||||
|
||||
// Update last check date
|
||||
function update_last_checked($feed_id)
|
||||
{
|
||||
Database::getInstance('db')
|
||||
$feed = $values;
|
||||
unset($feed['id']);
|
||||
unset($feed['group_name']);
|
||||
unset($feed['feed_group_ids']);
|
||||
|
||||
$result = Database::getInstance('db')
|
||||
->table('feeds')
|
||||
->eq('user_id', $user_id)
|
||||
->eq('id', $feed_id)
|
||||
->save(array(
|
||||
'last_checked' => time()
|
||||
));
|
||||
->save($feed);
|
||||
|
||||
if ($result) {
|
||||
if (isset($values['feed_group_ids']) && isset($values['group_name']) &&
|
||||
! Group\update_feed_groups($user_id, $values['id'], $values['feed_group_ids'], $values['group_name'])) {
|
||||
$db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update Etag and last Modified columns
|
||||
function update_cache($feed_id, $last_modified, $etag)
|
||||
$db->closeTransaction();
|
||||
return true;
|
||||
}
|
||||
|
||||
$db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
function change_feed_status($user_id, $feed_id, $status = STATUS_ACTIVE)
|
||||
{
|
||||
Database::getInstance('db')
|
||||
->table('feeds')
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('id', $feed_id)
|
||||
->save(array(
|
||||
'last_modified' => $last_modified,
|
||||
'etag' => $etag
|
||||
));
|
||||
->save((array('enabled' => $status)));
|
||||
}
|
||||
|
||||
// Remove one feed
|
||||
function remove($feed_id)
|
||||
function remove_feed($user_id, $feed_id)
|
||||
{
|
||||
Group\remove_all($feed_id);
|
||||
|
||||
// Items are removed by a sql constraint
|
||||
$result = Database::getInstance('db')->table('feeds')->eq('id', $feed_id)->remove();
|
||||
Favicon\purge_favicons();
|
||||
return $result;
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('id', $feed_id)
|
||||
->remove();
|
||||
}
|
||||
|
||||
// Remove all feeds
|
||||
function remove_all()
|
||||
function count_failed_feeds($user_id)
|
||||
{
|
||||
$result = Database::getInstance('db')->table('feeds')->remove();
|
||||
Favicon\purge_favicons();
|
||||
return $result;
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('parsing_error', 1)
|
||||
->count();
|
||||
}
|
||||
|
||||
// Enable a feed (activate refresh)
|
||||
function enable($feed_id)
|
||||
function count_feeds($user_id)
|
||||
{
|
||||
return Database::getInstance('db')->table('feeds')->eq('id', $feed_id)->save((array('enabled' => 1)));
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->count();
|
||||
}
|
||||
|
||||
// Disable feed
|
||||
function disable($feed_id)
|
||||
{
|
||||
return Database::getInstance('db')->table('feeds')->eq('id', $feed_id)->save((array('enabled' => 0)));
|
||||
}
|
||||
|
@ -4,77 +4,47 @@ namespace Miniflux\Model\Group;
|
||||
|
||||
use PicoDb\Database;
|
||||
|
||||
/**
|
||||
* Get all groups
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function get_all()
|
||||
const TABLE = 'groups';
|
||||
const JOIN_TABLE = 'feeds_groups';
|
||||
|
||||
function get_all($user_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('groups')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->orderBy('title')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assoc array of group ids with assigned feeds ids
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function get_map()
|
||||
function get_groups_feed_ids($user_id)
|
||||
{
|
||||
$result = Database::getInstance('db')
|
||||
->table('feeds_groups')
|
||||
$result = array();
|
||||
$rows = Database::getInstance('db')
|
||||
->table(JOIN_TABLE)
|
||||
->columns('feed_id', 'group_id')
|
||||
->join(TABLE, 'id', 'group_id')
|
||||
->eq('user_id', $user_id)
|
||||
->findAll();
|
||||
|
||||
// TODO: add PDO::FETCH_COLUMN|PDO::FETCH_GROUP to picodb and use it instead
|
||||
// of the following lines
|
||||
$map = array();
|
||||
|
||||
foreach ($result as $row) {
|
||||
foreach ($rows as $row) {
|
||||
$group_id = $row['group_id'];
|
||||
$feed_id = $row['feed_id'];
|
||||
|
||||
if (isset($map[$group_id])) {
|
||||
$map[$group_id][] = $feed_id;
|
||||
if (isset($result[$group_id])) {
|
||||
$result[$group_id][] = $feed_id;
|
||||
} else {
|
||||
$map[$group_id] = array($feed_id);
|
||||
$result[$group_id] = array($feed_id);
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assoc array of feeds ids with assigned groups ids
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function get_feeds_map()
|
||||
{
|
||||
$result = Database::getInstance('db')
|
||||
->table('feeds_groups')
|
||||
->findAll();
|
||||
$map = array();
|
||||
foreach ($result as $row) {
|
||||
$map[$row['feed_id']][] = $row['group_id'];
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all groups assigned to feed
|
||||
*
|
||||
* @param integer $feed_id id of the feed
|
||||
* @return array
|
||||
*/
|
||||
function get_feed_group_ids($feed_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('groups')
|
||||
->join('feeds_groups', 'group_id', 'id')
|
||||
->table(TABLE)
|
||||
->join(JOIN_TABLE, 'group_id', 'id')
|
||||
->eq('feed_id', $feed_id)
|
||||
->findAllByColumn('id');
|
||||
}
|
||||
@ -82,84 +52,77 @@ function get_feed_group_ids($feed_id)
|
||||
function get_feed_groups($feed_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('groups')
|
||||
->table(TABLE)
|
||||
->columns('groups.id', 'groups.title')
|
||||
->join('feeds_groups', 'group_id', 'id')
|
||||
->join(JOIN_TABLE, 'group_id', 'id')
|
||||
->eq('feed_id', $feed_id)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the id of a group
|
||||
*
|
||||
* @param string $title group name
|
||||
* @return mixed group id or false if not found
|
||||
*/
|
||||
function get_group_id($title)
|
||||
function get_group_id_from_title($user_id, $title)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('groups')
|
||||
->eq('user_id', $user_id)
|
||||
->eq('title', $title)
|
||||
->findOneColumn('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all feed ids assigned to a group
|
||||
*
|
||||
* @param integer $group_id
|
||||
* @return array
|
||||
*/
|
||||
function get_feeds_by_group($group_id)
|
||||
function get_feed_ids_by_group($group_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('feeds_groups')
|
||||
->table(JOIN_TABLE)
|
||||
->eq('group_id', $group_id)
|
||||
->findAllByColumn('feed_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a group to the Database
|
||||
*
|
||||
* Returns either the id of the new group or the id of an existing group with
|
||||
* the same name
|
||||
*
|
||||
* @param string $title group name
|
||||
* @return mixed id of the created group or false on error
|
||||
*/
|
||||
function create($title)
|
||||
function create_group($user_id, $title)
|
||||
{
|
||||
$data = array('title' => $title);
|
||||
$group_id = get_group_id_from_title($user_id, $title);
|
||||
|
||||
// check if the group already exists
|
||||
$group_id = get_group_id($title);
|
||||
|
||||
// create group if missing
|
||||
if ($group_id === false) {
|
||||
Database::getInstance('db')
|
||||
->table('groups')
|
||||
->insert($data);
|
||||
|
||||
$group_id = get_group_id($title);
|
||||
$group_id = Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->persist(array('title' => $title, 'user_id' => $user_id));
|
||||
}
|
||||
|
||||
return $group_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add groups to feed
|
||||
*
|
||||
* @param integer $feed_id feed id
|
||||
* @param array $group_ids array of group ids
|
||||
* @return boolean true on success, false on error
|
||||
*/
|
||||
function add($feed_id, array $group_ids)
|
||||
function update_feed_groups($user_id, $feed_id, array $group_ids, $group_name = '')
|
||||
{
|
||||
if ($group_name !== '') {
|
||||
$group_id = create_group($user_id, $group_name);
|
||||
if ($group_id === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! in_array($group_id, $group_ids)) {
|
||||
$group_ids[] = $group_id;
|
||||
}
|
||||
}
|
||||
|
||||
$assigned = get_feed_group_ids($feed_id);
|
||||
$superfluous = array_diff($assigned, $group_ids);
|
||||
$missing = array_diff($group_ids, $assigned);
|
||||
|
||||
if (! empty($superfluous) && ! dissociate_feed_groups($feed_id, $superfluous)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! empty($missing) && ! associate_feed_groups($feed_id, $missing)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function associate_feed_groups($feed_id, array $group_ids)
|
||||
{
|
||||
foreach ($group_ids as $group_id) {
|
||||
$data = array('feed_id' => $feed_id, 'group_id' => $group_id);
|
||||
|
||||
$result = Database::getInstance('db')
|
||||
->table('feeds_groups')
|
||||
->insert($data);
|
||||
->table(JOIN_TABLE)
|
||||
->insert(array('feed_id' => $feed_id, 'group_id' => $group_id));
|
||||
|
||||
if ($result === false) {
|
||||
return false;
|
||||
@ -169,104 +132,15 @@ function add($feed_id, array $group_ids)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove groups from feed
|
||||
*
|
||||
* @param integer $feed_id id of the feed
|
||||
* @param array $group_ids array of group ids
|
||||
* @return boolean true on success, false on error
|
||||
*/
|
||||
function remove($feed_id, array $group_ids)
|
||||
function dissociate_feed_groups($feed_id, array $group_ids)
|
||||
{
|
||||
$result = Database::getInstance('db')
|
||||
->table('feeds_groups')
|
||||
if (empty($group_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Database::getInstance('db')
|
||||
->table(JOIN_TABLE)
|
||||
->eq('feed_id', $feed_id)
|
||||
->in('group_id', $group_ids)
|
||||
->remove();
|
||||
|
||||
// remove empty groups
|
||||
if ($result) {
|
||||
purge_groups();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all groups from feed
|
||||
*
|
||||
* @param integer $feed_id id of the feed
|
||||
* @return boolean true on success, false on error
|
||||
*/
|
||||
function remove_all($feed_id)
|
||||
{
|
||||
$result = Database::getInstance('db')
|
||||
->table('feeds_groups')
|
||||
->eq('feed_id', $feed_id)
|
||||
->remove();
|
||||
|
||||
// remove empty groups
|
||||
if ($result) {
|
||||
purge_groups();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge orphaned groups from database
|
||||
*/
|
||||
function purge_groups()
|
||||
{
|
||||
$groups = Database::getInstance('db')
|
||||
->table('groups')
|
||||
->join('feeds_groups', 'group_id', 'id')
|
||||
->isNull('feed_id')
|
||||
->findAllByColumn('id');
|
||||
|
||||
if (! empty($groups)) {
|
||||
Database::getInstance('db')
|
||||
->table('groups')
|
||||
->in('id', $groups)
|
||||
->remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update feed group associations
|
||||
*
|
||||
* @param integer $feed_id id of the feed to update
|
||||
* @param array $group_ids valid groups ids for feed
|
||||
* @param string $create_group group to create and assign to feed
|
||||
* @return boolean
|
||||
*/
|
||||
function update_feed_groups($feed_id, array $group_ids, $create_group = '')
|
||||
{
|
||||
if ($create_group !== '') {
|
||||
$id = create($create_group);
|
||||
|
||||
if ($id === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! in_array($id, $group_ids)) {
|
||||
$group_ids[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
$assigned = get_feed_group_ids($feed_id);
|
||||
$superfluous = array_diff($assigned, $group_ids);
|
||||
$missing = array_diff($group_ids, $assigned);
|
||||
|
||||
// remove no longer assigned groups from feed
|
||||
if (! empty($superfluous) && ! remove($feed_id, $superfluous)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// add requested groups to feed
|
||||
if (! empty($missing) && ! add($feed_id, $missing)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3,157 +3,177 @@
|
||||
namespace Miniflux\Model\Item;
|
||||
|
||||
use PicoDb\Database;
|
||||
use PicoFeed\Logging\Logger;
|
||||
use Miniflux\Handler\Service;
|
||||
use Miniflux\Model\Config;
|
||||
use Miniflux\Model\Feed;
|
||||
use Miniflux\Model\Group;
|
||||
use Miniflux\Handler;
|
||||
use Miniflux\Helper;
|
||||
use PicoFeed\Parser\Parser;
|
||||
|
||||
// Get all items without filtering
|
||||
function get_all()
|
||||
const TABLE = 'items';
|
||||
const STATUS_UNREAD = 'unread';
|
||||
const STATUS_READ = 'read';
|
||||
const STATUS_REMOVED = 'removed';
|
||||
|
||||
function change_item_status($user_id, $item_id, $status)
|
||||
{
|
||||
if (! in_array($status, array(STATUS_READ, STATUS_UNREAD, STATUS_REMOVED))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('id', $item_id)
|
||||
->save(array('status' => $status));
|
||||
}
|
||||
|
||||
function change_items_status($user_id, $current_status, $new_status)
|
||||
{
|
||||
if (! in_array($new_status, array(STATUS_READ, STATUS_UNREAD, STATUS_REMOVED))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('status', $current_status)
|
||||
->save(array('status' => $new_status));
|
||||
}
|
||||
|
||||
function change_item_ids_status($user_id, array $item_ids, $status)
|
||||
{
|
||||
if (! in_array($status, array(STATUS_READ, STATUS_UNREAD, STATUS_REMOVED))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($item_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->in('id', $item_ids)
|
||||
->save(array('status' => $status));
|
||||
}
|
||||
|
||||
function update_feed_items($user_id, $feed_id, array $items, $rtl = false)
|
||||
{
|
||||
$items_in_feed = array();
|
||||
$db = Database::getInstance('db');
|
||||
$db->startTransaction();
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item->getId() && $item->getUrl()) {
|
||||
$item_id = get_item_id_from_checksum($feed_id, $item->getId());
|
||||
$values = array(
|
||||
'title' => $item->getTitle(),
|
||||
'url' => $item->getUrl(),
|
||||
'updated' => $item->getDate()->getTimestamp(),
|
||||
'author' => $item->getAuthor(),
|
||||
'content' => Helper\bool_config('nocontent') ? '' : $item->getContent(),
|
||||
'enclosure_url' => $item->getEnclosureUrl(),
|
||||
'enclosure_type' => $item->getEnclosureType(),
|
||||
'language' => $item->getLanguage(),
|
||||
'rtl' => $rtl || Parser::isLanguageRTL($item->getLanguage()) ? 1 : 0,
|
||||
);
|
||||
|
||||
if ($item_id > 0) {
|
||||
$db
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('feed_id', $feed_id)
|
||||
->eq('id', $item_id)
|
||||
->update($values);
|
||||
} else {
|
||||
$values['checksum'] = $item->getId();
|
||||
$values['user_id'] = $user_id;
|
||||
$values['feed_id'] = $feed_id;
|
||||
$values['status'] = STATUS_UNREAD;
|
||||
$item_id = $db->table(TABLE)->persist($values);
|
||||
}
|
||||
|
||||
$items_in_feed[] = $item_id;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_feed_items($feed_id, $items_in_feed);
|
||||
$db->closeTransaction();
|
||||
}
|
||||
|
||||
function cleanup_feed_items($feed_id, array $items_in_feed)
|
||||
{
|
||||
if (! empty($items_in_feed)) {
|
||||
$db = Database::getInstance('db');
|
||||
|
||||
$removed_items = $db
|
||||
->table(TABLE)
|
||||
->columns('id')
|
||||
->notIn('id', $items_in_feed)
|
||||
->eq('status', STATUS_REMOVED)
|
||||
->eq('feed_id', $feed_id)
|
||||
->desc('updated')
|
||||
->findAllByColumn('id');
|
||||
|
||||
// Keep a buffer of 2 items
|
||||
// It's workaround for buggy feeds (cache issue with some Wordpress plugins)
|
||||
if (is_array($removed_items)) {
|
||||
$items_to_remove = array_slice($removed_items, 2);
|
||||
|
||||
if (! empty($items_to_remove)) {
|
||||
// Handle the case when there is a huge number of items to remove
|
||||
// Sqlite have a limit of 1000 sql variables by default
|
||||
// Avoid the error message "too many SQL variables"
|
||||
// We remove old items by batch of 500 items
|
||||
$chunks = array_chunk($items_to_remove, 500);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$db->table(TABLE)
|
||||
->in('id', $chunk)
|
||||
->eq('status', STATUS_REMOVED)
|
||||
->eq('feed_id', $feed_id)
|
||||
->remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function get_item_id_from_checksum($feed_id, $checksum)
|
||||
{
|
||||
return (int) Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('feed_id', $feed_id)
|
||||
->eq('checksum', $checksum)
|
||||
->findOneColumn('id');
|
||||
}
|
||||
|
||||
function get_item($user_id, $item_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->columns(
|
||||
'items.id',
|
||||
'items.title',
|
||||
'items.updated',
|
||||
'items.url',
|
||||
'items.enclosure',
|
||||
'items.enclosure_type',
|
||||
'items.bookmark',
|
||||
'items.feed_id',
|
||||
'items.status',
|
||||
'items.content',
|
||||
'items.language',
|
||||
'feeds.site_url',
|
||||
'feeds.title AS feed_title',
|
||||
'feeds.rtl'
|
||||
)
|
||||
->join('feeds', 'id', 'feed_id')
|
||||
->in('status', array('read', 'unread'))
|
||||
->orderBy('updated', 'desc')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
// Get everthing since date (timestamp)
|
||||
function get_all_since($timestamp)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->columns(
|
||||
'items.id',
|
||||
'items.title',
|
||||
'items.updated',
|
||||
'items.url',
|
||||
'items.enclosure',
|
||||
'items.enclosure_type',
|
||||
'items.bookmark',
|
||||
'items.feed_id',
|
||||
'items.status',
|
||||
'items.content',
|
||||
'items.language',
|
||||
'feeds.site_url',
|
||||
'feeds.title AS feed_title',
|
||||
'feeds.rtl'
|
||||
)
|
||||
->join('feeds', 'id', 'feed_id')
|
||||
->in('status', array('read', 'unread'))
|
||||
->gte('updated', $timestamp)
|
||||
->orderBy('updated', 'desc')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
function get_latest_feeds_items()
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('feeds')
|
||||
->columns(
|
||||
'feeds.id',
|
||||
'MAX(items.updated) as updated',
|
||||
'items.status'
|
||||
)
|
||||
->join('items', 'feed_id', 'id')
|
||||
->groupBy('feeds.id')
|
||||
->orderBy('feeds.id')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
// Get a list of [item_id => status,...]
|
||||
function get_all_status()
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->hashtable('items')
|
||||
->in('status', array('read', 'unread'))
|
||||
->orderBy('updated', 'desc')
|
||||
->getAll('id', 'status');
|
||||
}
|
||||
|
||||
// Get all items by status
|
||||
function get_all_by_status($status, $feed_ids = array(), $offset = null, $limit = null, $order_column = 'updated', $order_direction = 'desc')
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->columns(
|
||||
'items.id',
|
||||
'items.title',
|
||||
'items.updated',
|
||||
'items.url',
|
||||
'items.enclosure',
|
||||
'items.enclosure_type',
|
||||
'items.bookmark',
|
||||
'items.feed_id',
|
||||
'items.status',
|
||||
'items.content',
|
||||
'items.language',
|
||||
'items.author',
|
||||
'feeds.site_url',
|
||||
'feeds.title AS feed_title',
|
||||
'feeds.rtl'
|
||||
)
|
||||
->join('feeds', 'id', 'feed_id')
|
||||
->eq('status', $status)
|
||||
->in('feed_id', $feed_ids)
|
||||
->orderBy($order_column, $order_direction)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
// Get the number of items per status
|
||||
function count_by_status($status, $feed_ids = array())
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('status', $status)
|
||||
->in('feed_id', $feed_ids)
|
||||
->count();
|
||||
}
|
||||
|
||||
// Get one item by id
|
||||
function get($id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('id', $id)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('id', $item_id)
|
||||
->findOne();
|
||||
}
|
||||
|
||||
// Get item naviguation (next/prev items)
|
||||
function get_nav($item, $status = array('unread'), $bookmark = array(1, 0), $feed_id = null, $group_id = null)
|
||||
function get_item_nav($user_id, array $item, $status = array(STATUS_UNREAD), $bookmark = array(1, 0), $feed_id = null, $group_id = null)
|
||||
{
|
||||
$query = Database::getInstance('db')
|
||||
->table('items')
|
||||
->table(TABLE)
|
||||
->columns('id', 'status', 'title', 'bookmark')
|
||||
->neq('status', 'removed')
|
||||
->orderBy('updated', Config\get('items_sorting_direction'));
|
||||
->neq('status', STATUS_REMOVED)
|
||||
->eq('user_id', $user_id)
|
||||
->orderBy('updated', Helper\config('items_sorting_direction'))
|
||||
->desc('id')
|
||||
;
|
||||
|
||||
if ($feed_id) {
|
||||
$query->eq('feed_id', $feed_id);
|
||||
}
|
||||
|
||||
if ($group_id) {
|
||||
$query->in('feed_id', Group\get_feeds_by_group($group_id));
|
||||
$query->in('feed_id', Group\get_feed_ids_by_group($group_id));
|
||||
}
|
||||
|
||||
$items = $query->findAll();
|
||||
@ -199,240 +219,149 @@ function get_nav($item, $status = array('unread'), $bookmark = array(1, 0), $fee
|
||||
);
|
||||
}
|
||||
|
||||
// Change item status to removed and clear content
|
||||
function set_removed($id)
|
||||
function get_items_by_status($user_id, $status, $feed_ids = array(), $offset = null, $limit = null, $order_column = 'updated', $order_direction = 'desc')
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('id', $id)
|
||||
->save(array('status' => 'removed', 'content' => ''));
|
||||
->columns(
|
||||
'items.id',
|
||||
'items.checksum',
|
||||
'items.title',
|
||||
'items.updated',
|
||||
'items.url',
|
||||
'items.enclosure_url',
|
||||
'items.enclosure_type',
|
||||
'items.bookmark',
|
||||
'items.feed_id',
|
||||
'items.status',
|
||||
'items.content',
|
||||
'items.language',
|
||||
'items.rtl',
|
||||
'items.author',
|
||||
'feeds.site_url',
|
||||
'feeds.title AS feed_title'
|
||||
)
|
||||
->join('feeds', 'id', 'feed_id')
|
||||
->eq('items.user_id', $user_id)
|
||||
->eq('items.status', $status)
|
||||
->in('items.feed_id', $feed_ids)
|
||||
->orderBy($order_column, $order_direction)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
// Change item status to read
|
||||
function set_read($id)
|
||||
function get_items($user_id, $since_id = null, array $item_ids = array(), $limit = 50)
|
||||
{
|
||||
$query = Database::getInstance('db')
|
||||
->table('items')
|
||||
->columns(
|
||||
'items.id',
|
||||
'items.checksum',
|
||||
'items.title',
|
||||
'items.updated',
|
||||
'items.url',
|
||||
'items.enclosure_url',
|
||||
'items.enclosure_type',
|
||||
'items.bookmark',
|
||||
'items.feed_id',
|
||||
'items.status',
|
||||
'items.content',
|
||||
'items.language',
|
||||
'items.rtl',
|
||||
'items.author',
|
||||
'feeds.site_url',
|
||||
'feeds.title AS feed_title'
|
||||
)
|
||||
->join('feeds', 'id', 'feed_id')
|
||||
->eq('items.user_id', $user_id)
|
||||
->neq('items.status', STATUS_REMOVED)
|
||||
->limit($limit)
|
||||
->asc('items.id');
|
||||
|
||||
if ($since_id !== null) {
|
||||
$query->gt('items.id', $since_id);
|
||||
} elseif (! empty($item_ids)) {
|
||||
$query->in('items.id', $item_ids);
|
||||
}
|
||||
|
||||
return $query->findAll();
|
||||
}
|
||||
|
||||
function get_item_ids_by_status($user_id, $status)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('id', $id)
|
||||
->save(array('status' => 'read'));
|
||||
}
|
||||
|
||||
// Change item status to unread
|
||||
function set_unread($id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('id', $id)
|
||||
->save(array('status' => 'unread'));
|
||||
}
|
||||
|
||||
// Change item status to "read", "unread" or "removed"
|
||||
function set_status($status, array $items)
|
||||
{
|
||||
if (! in_array($status, array('read', 'unread', 'removed'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->in('id', $items)
|
||||
->save(array('status' => $status));
|
||||
}
|
||||
|
||||
// Mark all unread items as read
|
||||
function mark_all_as_read()
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('status', 'unread')
|
||||
->save(array('status' => 'read'));
|
||||
}
|
||||
|
||||
// Mark all read items to removed
|
||||
function mark_all_as_removed()
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('status', 'read')
|
||||
->eq('bookmark', 0)
|
||||
->save(array('status' => 'removed', 'content' => ''));
|
||||
}
|
||||
|
||||
// Mark all read items to removed after X days
|
||||
function autoflush_read()
|
||||
{
|
||||
$autoflush = (int) Config\get('autoflush');
|
||||
|
||||
if ($autoflush > 0) {
|
||||
|
||||
// Mark read items removed after X days
|
||||
Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('bookmark', 0)
|
||||
->eq('status', 'read')
|
||||
->lt('updated', strtotime('-'.$autoflush.'day'))
|
||||
->save(array('status' => 'removed', 'content' => ''));
|
||||
} elseif ($autoflush === -1) {
|
||||
|
||||
// Mark read items removed immediately
|
||||
Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('bookmark', 0)
|
||||
->eq('status', 'read')
|
||||
->save(array('status' => 'removed', 'content' => ''));
|
||||
}
|
||||
}
|
||||
|
||||
// Mark all unread items to removed after X days
|
||||
function autoflush_unread()
|
||||
{
|
||||
$autoflush = (int) Config\get('autoflush_unread');
|
||||
|
||||
if ($autoflush > 0) {
|
||||
|
||||
// Mark read items removed after X days
|
||||
Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('bookmark', 0)
|
||||
->eq('status', 'unread')
|
||||
->lt('updated', strtotime('-'.$autoflush.'day'))
|
||||
->save(array('status' => 'removed', 'content' => ''));
|
||||
}
|
||||
}
|
||||
|
||||
// Update all items
|
||||
function update_all($feed_id, array $items)
|
||||
{
|
||||
$nocontent = (bool) Config\get('nocontent');
|
||||
|
||||
$items_in_feed = array();
|
||||
|
||||
$db = Database::getInstance('db');
|
||||
$db->startTransaction();
|
||||
|
||||
foreach ($items as $item) {
|
||||
Logger::setMessage('Item => '.$item->getId().' '.$item->getUrl());
|
||||
|
||||
// Item parsed correctly?
|
||||
if ($item->getId() && $item->getUrl()) {
|
||||
Logger::setMessage('Item parsed correctly');
|
||||
|
||||
// Get item record in database, if any
|
||||
$itemrec = $db
|
||||
->table('items')
|
||||
->columns('enclosure')
|
||||
->eq('id', $item->getId())
|
||||
->findOne();
|
||||
|
||||
// Insert a new item
|
||||
if ($itemrec === null) {
|
||||
Logger::setMessage('Item added to the database');
|
||||
|
||||
$db->table('items')->save(array(
|
||||
'id' => $item->getId(),
|
||||
'title' => $item->getTitle(),
|
||||
'url' => $item->getUrl(),
|
||||
'updated' => $item->getDate()->getTimestamp(),
|
||||
'author' => $item->getAuthor(),
|
||||
'content' => $nocontent ? '' : $item->getContent(),
|
||||
'status' => 'unread',
|
||||
'feed_id' => $feed_id,
|
||||
'enclosure' => $item->getEnclosureUrl(),
|
||||
'enclosure_type' => $item->getEnclosureType(),
|
||||
'language' => $item->getLanguage(),
|
||||
));
|
||||
} elseif (! $itemrec['enclosure'] && $item->getEnclosureUrl()) {
|
||||
Logger::setMessage('Update item enclosure');
|
||||
|
||||
$db->table('items')->eq('id', $item->getId())->save(array(
|
||||
'status' => 'unread',
|
||||
'enclosure' => $item->getEnclosureUrl(),
|
||||
'enclosure_type' => $item->getEnclosureType(),
|
||||
));
|
||||
} else {
|
||||
Logger::setMessage('Item already in the database');
|
||||
}
|
||||
|
||||
// Items inside this feed
|
||||
$items_in_feed[] = $item->id;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup old items
|
||||
cleanup($feed_id, $items_in_feed);
|
||||
|
||||
$db->closeTransaction();
|
||||
}
|
||||
|
||||
// Remove from the database items marked as "removed"
|
||||
// and not present inside the feed
|
||||
function cleanup($feed_id, array $items_in_feed)
|
||||
{
|
||||
if (! empty($items_in_feed)) {
|
||||
$db = Database::getInstance('db');
|
||||
|
||||
$removed_items = $db
|
||||
->table('items')
|
||||
->columns('id')
|
||||
->notIn('id', $items_in_feed)
|
||||
->eq('status', 'removed')
|
||||
->eq('feed_id', $feed_id)
|
||||
->desc('updated')
|
||||
->eq('user_id', $user_id)
|
||||
->eq('status', $status)
|
||||
->findAllByColumn('id');
|
||||
|
||||
// Keep a buffer of 2 items
|
||||
// It's workaround for buggy feeds (cache issue with some Wordpress plugins)
|
||||
if (is_array($removed_items)) {
|
||||
$items_to_remove = array_slice($removed_items, 2);
|
||||
|
||||
if (! empty($items_to_remove)) {
|
||||
$nb_items = count($items_to_remove);
|
||||
Logger::setMessage('There is '.$nb_items.' items to remove');
|
||||
|
||||
// Handle the case when there is a huge number of items to remove
|
||||
// Sqlite have a limit of 1000 sql variables by default
|
||||
// Avoid the error message "too many SQL variables"
|
||||
// We remove old items by batch of 500 items
|
||||
$chunks = array_chunk($items_to_remove, 500);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$db->table('items')
|
||||
->in('id', $chunk)
|
||||
->eq('status', 'removed')
|
||||
->eq('feed_id', $feed_id)
|
||||
->remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Download item content
|
||||
function download_contents($item_id)
|
||||
function get_latest_feeds_items($user_id)
|
||||
{
|
||||
$item = get($item_id);
|
||||
$content = Handler\Scraper\download_contents($item['url']);
|
||||
return Database::getInstance('db')
|
||||
->table(Feed\TABLE)
|
||||
->columns(
|
||||
'feeds.id',
|
||||
'MAX(items.updated) as updated',
|
||||
'items.status'
|
||||
)
|
||||
->join(TABLE, 'feed_id', 'id')
|
||||
->eq('feeds.user_id', $user_id)
|
||||
->groupBy('feeds.id')
|
||||
->orderBy('feeds.id')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
if (! empty($content)) {
|
||||
if (! Config\get('nocontent')) {
|
||||
Database::getInstance('db')
|
||||
function count_by_status($user_id, $status, $feed_ids = array())
|
||||
{
|
||||
$query = Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('id', $item['id'])
|
||||
->save(array('content' => $content));
|
||||
->eq('user_id', $user_id)
|
||||
->in('feed_id', $feed_ids);
|
||||
|
||||
if (is_array($status)) {
|
||||
$query->in('status', $status);
|
||||
} else {
|
||||
$query->eq('status', $status);
|
||||
}
|
||||
|
||||
Config\write_debug();
|
||||
|
||||
return array(
|
||||
'result' => true,
|
||||
'content' => $content
|
||||
);
|
||||
return $query->count();
|
||||
}
|
||||
|
||||
Config\write_debug();
|
||||
function autoflush_read($user_id)
|
||||
{
|
||||
$autoflush = Helper\int_config('autoflush');
|
||||
|
||||
return array(
|
||||
'result' => false,
|
||||
'content' => ''
|
||||
);
|
||||
if ($autoflush > 0) {
|
||||
Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('bookmark', 0)
|
||||
->eq('status', STATUS_READ)
|
||||
->lt('updated', strtotime('-'.$autoflush.'day'))
|
||||
->save(array('status' => STATUS_REMOVED, 'content' => ''));
|
||||
} elseif ($autoflush === -1) {
|
||||
Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('bookmark', 0)
|
||||
->eq('status', STATUS_READ)
|
||||
->save(array('status' => STATUS_REMOVED, 'content' => ''));
|
||||
}
|
||||
}
|
||||
|
||||
function autoflush_unread($user_id)
|
||||
{
|
||||
$autoflush = Helper\int_config('autoflush_unread');
|
||||
|
||||
if ($autoflush > 0) {
|
||||
Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('bookmark', 0)
|
||||
->eq('status', STATUS_UNREAD)
|
||||
->lt('updated', strtotime('-'.$autoflush.'day'))
|
||||
->save(array('status' => STATUS_REMOVED, 'content' => ''));
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,48 @@
|
||||
|
||||
namespace Miniflux\Model\ItemFeed;
|
||||
|
||||
use Miniflux\Model\Feed;
|
||||
use Miniflux\Model\Item;
|
||||
use PicoDb\Database;
|
||||
|
||||
function count_items($feed_id)
|
||||
function count_items_by_status($user_id, $feed_id)
|
||||
{
|
||||
$counts = Database::getInstance('db')
|
||||
->table(Item\TABLE)
|
||||
->columns('status', 'count(*) as item_count')
|
||||
->in('status', array(Item\STATUS_READ, Item\STATUS_UNREAD))
|
||||
->eq('user_id', $user_id)
|
||||
->eq('feed_id', $feed_id)
|
||||
->groupBy('status')
|
||||
->findAll();
|
||||
|
||||
$result = array(
|
||||
'items_unread' => 0,
|
||||
'items_total' => 0,
|
||||
);
|
||||
|
||||
foreach ($counts as &$count) {
|
||||
if ($count['status'] === Item\STATUS_UNREAD) {
|
||||
$result['items_unread'] = (int) $count['item_count'];
|
||||
}
|
||||
|
||||
$result['items_total'] += $count['item_count'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function count_items($user_id, $feed_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->table(Item\TABLE)
|
||||
->eq('feed_id', $feed_id)
|
||||
->in('status', array('unread', 'read'))
|
||||
->eq('user_id', $user_id)
|
||||
->in('status', array(Item\STATUS_READ, Item\STATUS_UNREAD))
|
||||
->count();
|
||||
}
|
||||
|
||||
function get_all_items($feed_id, $offset = null, $limit = null, $order_column = 'updated', $order_direction = 'desc')
|
||||
function get_all_items($user_id, $feed_id, $offset = null, $limit = null, $order_column = 'updated', $order_direction = 'desc')
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
@ -22,31 +52,39 @@ function get_all_items($feed_id, $offset = null, $limit = null, $order_column =
|
||||
'items.title',
|
||||
'items.updated',
|
||||
'items.url',
|
||||
'items.enclosure',
|
||||
'items.enclosure_url',
|
||||
'items.enclosure_type',
|
||||
'items.feed_id',
|
||||
'items.status',
|
||||
'items.content',
|
||||
'items.bookmark',
|
||||
'items.language',
|
||||
'items.rtl',
|
||||
'items.author',
|
||||
'feeds.site_url',
|
||||
'feeds.rtl'
|
||||
'feeds.title AS feed_title'
|
||||
)
|
||||
->join('feeds', 'id', 'feed_id')
|
||||
->in('status', array('unread', 'read'))
|
||||
->eq('feed_id', $feed_id)
|
||||
->join(Feed\TABLE, 'id', 'feed_id')
|
||||
->in('status', array(Item\STATUS_UNREAD, Item\STATUS_READ))
|
||||
->eq('items.feed_id', $feed_id)
|
||||
->eq('items.user_id', $user_id)
|
||||
->orderBy($order_column, $order_direction)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
function mark_all_as_read($feed_id)
|
||||
function change_items_status($user_id, $feed_id, $current_status, $new_status, $before = null)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('status', 'unread')
|
||||
$query = Database::getInstance('db')
|
||||
->table(Item\TABLE)
|
||||
->eq('status', $current_status)
|
||||
->eq('feed_id', $feed_id)
|
||||
->update(array('status' => 'read'));
|
||||
->eq('user_id', $user_id);
|
||||
|
||||
if ($before !== null) {
|
||||
$query->lte('updated', $before);
|
||||
}
|
||||
|
||||
return $query->update(array('status' => $new_status));
|
||||
}
|
||||
|
@ -2,28 +2,27 @@
|
||||
|
||||
namespace Miniflux\Model\ItemGroup;
|
||||
|
||||
use PicoDb\Database;
|
||||
use Miniflux\Model\Item;
|
||||
use Miniflux\Model\Group;
|
||||
use PicoDb\Database;
|
||||
|
||||
function mark_all_as_read($group_id)
|
||||
function change_items_status($user_id, $group_id, $current_status, $new_status, $before = null)
|
||||
{
|
||||
$feed_ids = Group\get_feeds_by_group($group_id);
|
||||
$feed_ids = Group\get_feed_ids_by_group($group_id);
|
||||
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('status', 'unread')
|
||||
->in('feed_id', $feed_ids)
|
||||
->update(array('status' => 'read'));
|
||||
if (empty($feed_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function mark_all_as_removed($group_id)
|
||||
{
|
||||
$feed_ids = Group\get_feeds_by_group($group_id);
|
||||
$query = Database::getInstance('db')
|
||||
->table(Item\TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('status', $current_status)
|
||||
->in('feed_id', $feed_ids);
|
||||
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('status', 'read')
|
||||
->eq('bookmark', 0)
|
||||
->in('feed_id', $feed_ids)
|
||||
->save(array('status' => 'removed', 'content' => ''));
|
||||
if ($before !== null) {
|
||||
$query->lte('updated', $before);
|
||||
}
|
||||
|
||||
return $query->update(array('status' => $new_status));
|
||||
}
|
||||
|
@ -2,24 +2,16 @@
|
||||
|
||||
namespace Miniflux\Model\RememberMe;
|
||||
|
||||
use PicoDb\Database;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model\Config;
|
||||
use Miniflux\Model\Database as DatabaseModel;
|
||||
use Miniflux\Model\User;
|
||||
use PicoDb\Database;
|
||||
|
||||
const TABLE = 'remember_me';
|
||||
const COOKIE_NAME = '_R_';
|
||||
const EXPIRATION = 5184000;
|
||||
|
||||
/**
|
||||
* Get a remember me record
|
||||
*
|
||||
* @access public
|
||||
* @param string $token
|
||||
* @param string $sequence
|
||||
* @return mixed
|
||||
*/
|
||||
function find($token, $sequence)
|
||||
function get_record($token, $sequence)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
@ -29,46 +21,16 @@ function find($token, $sequence)
|
||||
->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all sessions
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
function get_all()
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->desc('date_creation')
|
||||
->columns('id', 'ip', 'user_agent', 'date_creation', 'expiration')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user with the cookie
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
function authenticate()
|
||||
{
|
||||
$credentials = read_cookie();
|
||||
|
||||
if ($credentials !== false) {
|
||||
$record = find($credentials['token'], $credentials['sequence']);
|
||||
$record = get_record($credentials['token'], $credentials['sequence']);
|
||||
|
||||
if ($record) {
|
||||
|
||||
// Update the sequence
|
||||
write_cookie(
|
||||
$record['token'],
|
||||
update($record['token']),
|
||||
$record['expiration']
|
||||
);
|
||||
|
||||
// mark user as sucessfull logged in
|
||||
$_SESSION['loggedin'] = true;
|
||||
|
||||
$user = User\get_user_by_id($record['user_id']);
|
||||
SessionStorage::getInstance()->setUser($user);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -76,35 +38,6 @@ function authenticate()
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the database and the cookie with a new sequence
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function refresh()
|
||||
{
|
||||
$credentials = read_cookie();
|
||||
|
||||
if ($credentials !== false) {
|
||||
$record = find($credentials['token'], $credentials['sequence']);
|
||||
|
||||
if ($record) {
|
||||
|
||||
// Update the sequence
|
||||
write_cookie(
|
||||
$record['token'],
|
||||
update($record['token']),
|
||||
$record['expiration']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the current RememberMe session and the cookie
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function destroy()
|
||||
{
|
||||
$credentials = read_cookie();
|
||||
@ -119,19 +52,9 @@ function destroy()
|
||||
delete_cookie();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RememberMe session
|
||||
*
|
||||
* @access public
|
||||
* @param integer $dbname Database name
|
||||
* @param integer $username Username
|
||||
* @param string $ip IP Address
|
||||
* @param string $user_agent User Agent
|
||||
* @return array
|
||||
*/
|
||||
function create($dbname, $username, $ip, $user_agent)
|
||||
function create($user_id, $ip, $user_agent)
|
||||
{
|
||||
$token = hash('sha256', $dbname.$username.$user_agent.$ip.Helper\generate_token());
|
||||
$token = hash('sha256', $user_id.$user_agent.$ip.Helper\generate_token());
|
||||
$sequence = Helper\generate_token();
|
||||
$expiration = time() + EXPIRATION;
|
||||
|
||||
@ -140,7 +63,7 @@ function create($dbname, $username, $ip, $user_agent)
|
||||
Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->insert(array(
|
||||
'username' => $username,
|
||||
'user_id' => $user_id,
|
||||
'ip' => $ip,
|
||||
'user_agent' => $user_agent,
|
||||
'token' => $token,
|
||||
@ -156,12 +79,6 @@ function create($dbname, $username, $ip, $user_agent)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove old sessions
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
function cleanup()
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
@ -170,13 +87,6 @@ function cleanup()
|
||||
->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new sequence token and update the database
|
||||
*
|
||||
* @access public
|
||||
* @param string $token Session token
|
||||
* @return string
|
||||
*/
|
||||
function update($token)
|
||||
{
|
||||
$new_sequence = Helper\generate_token();
|
||||
@ -189,33 +99,14 @@ function update($token)
|
||||
return $new_sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the cookie
|
||||
*
|
||||
* @access public
|
||||
* @param string $token Session token
|
||||
* @param string $sequence Sequence token
|
||||
* @return string
|
||||
*/
|
||||
function encode_cookie($token, $sequence)
|
||||
{
|
||||
return implode('|', array(base64_encode(DatabaseModel\select()), $token, $sequence));
|
||||
return implode('|', array($token, $sequence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the value of a cookie
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Raw cookie data
|
||||
* @return array
|
||||
*/
|
||||
function decode_cookie($value)
|
||||
{
|
||||
@list($database, $token, $sequence) = explode('|', $value);
|
||||
|
||||
if (ENABLE_MULTIPLE_DB && ! DatabaseModel\select(base64_decode($database))) {
|
||||
return false;
|
||||
}
|
||||
@list($token, $sequence) = explode('|', $value);
|
||||
|
||||
return array(
|
||||
'token' => $token,
|
||||
@ -223,25 +114,11 @@ function decode_cookie($value)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the current user has a RememberMe cookie
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
function has_cookie()
|
||||
{
|
||||
return ! empty($_COOKIE[COOKIE_NAME]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write and encode the cookie
|
||||
*
|
||||
* @access public
|
||||
* @param string $token Session token
|
||||
* @param string $sequence Sequence token
|
||||
* @param string $expiration Cookie expiration
|
||||
*/
|
||||
function write_cookie($token, $sequence, $expiration)
|
||||
{
|
||||
setcookie(
|
||||
@ -255,12 +132,6 @@ function write_cookie($token, $sequence, $expiration)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and decode the cookie
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
function read_cookie()
|
||||
{
|
||||
if (empty($_COOKIE[COOKIE_NAME])) {
|
||||
@ -270,11 +141,6 @@ function read_cookie()
|
||||
return decode_cookie($_COOKIE[COOKIE_NAME]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the cookie
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function delete_cookie()
|
||||
{
|
||||
setcookie(
|
||||
|
@ -1,43 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Miniflux\Model\Search;
|
||||
namespace Miniflux\Model\ItemSearch;
|
||||
|
||||
use PicoDb\Database;
|
||||
use Miniflux\Model\Feed;
|
||||
use Miniflux\Model\Item;
|
||||
|
||||
function count_items($text)
|
||||
function count_items($user_id, $text)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->neq('status', 'removed')
|
||||
->table(Item\TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->neq('status', Item\STATUS_REMOVED)
|
||||
->ilike('title', '%' . $text . '%')
|
||||
->count();
|
||||
}
|
||||
|
||||
function get_all_items($text, $offset = null, $limit = null)
|
||||
function get_all_items($user_id, $text, $offset = null, $limit = null)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table('items')
|
||||
->table(Item\TABLE)
|
||||
->columns(
|
||||
'items.id',
|
||||
'items.title',
|
||||
'items.updated',
|
||||
'items.url',
|
||||
'items.enclosure',
|
||||
'items.enclosure_url',
|
||||
'items.enclosure_type',
|
||||
'items.bookmark',
|
||||
'items.feed_id',
|
||||
'items.status',
|
||||
'items.content',
|
||||
'items.language',
|
||||
'items.rtl',
|
||||
'items.author',
|
||||
'feeds.site_url',
|
||||
'feeds.title AS feed_title',
|
||||
'feeds.rtl'
|
||||
'feeds.title AS feed_title'
|
||||
)
|
||||
->join('feeds', 'id', 'feed_id')
|
||||
->neq('status', 'removed')
|
||||
->join(Feed\TABLE, 'id', 'feed_id')
|
||||
->eq('items.user_id', $user_id)
|
||||
->neq('items.status', Item\STATUS_REMOVED)
|
||||
->ilike('items.title', '%' . $text . '%')
|
||||
->orderBy('updated', 'desc')
|
||||
->orderBy('items.updated', 'desc')
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->findAll();
|
||||
|
@ -3,37 +3,142 @@
|
||||
namespace Miniflux\Model\User;
|
||||
|
||||
use PicoDb\Database;
|
||||
use Miniflux\Session;
|
||||
use Miniflux\Request;
|
||||
use Miniflux\Model\Config;
|
||||
use Miniflux\Model\RememberMe;
|
||||
use Miniflux\Model\Database as DatabaseModel;
|
||||
use Miniflux\Helper;
|
||||
|
||||
// Check if the user is logged in
|
||||
function is_loggedin()
|
||||
const TABLE = 'users';
|
||||
|
||||
function create_user($username, $password, $is_admin = false)
|
||||
{
|
||||
return ! empty($_SESSION['loggedin']);
|
||||
list($fever_token, $fever_api_key) = generate_fever_api_key($username);
|
||||
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->persist(array(
|
||||
'username' => $username,
|
||||
'password' => password_hash($password, PASSWORD_BCRYPT),
|
||||
'is_admin' => (int) $is_admin,
|
||||
'api_token' => Helper\generate_token(),
|
||||
'bookmarklet_token' => Helper\generate_token(),
|
||||
'cronjob_token' => Helper\generate_token(),
|
||||
'feed_token' => Helper\generate_token(),
|
||||
'fever_token' => $fever_token,
|
||||
'fever_api_key' => $fever_api_key,
|
||||
));
|
||||
}
|
||||
|
||||
// Destroy the session and the rememberMe cookie
|
||||
function logout()
|
||||
function update_user($user_id, $username, $password = null, $is_admin = null)
|
||||
{
|
||||
RememberMe\destroy();
|
||||
Session\close();
|
||||
$user = get_user_by_id($user_id);
|
||||
$values = array();
|
||||
|
||||
if ($user['username'] !== $username) {
|
||||
list($fever_token, $fever_api_key) = generate_fever_api_key($user['username']);
|
||||
|
||||
$values['username'] = $username;
|
||||
$values['fever_token'] = $fever_token;
|
||||
$values['fever_api_key'] = $fever_api_key;
|
||||
}
|
||||
|
||||
// Get the credentials from the current selected database
|
||||
function get_credentials()
|
||||
if ($password !== null) {
|
||||
$values['password'] = password_hash($password, PASSWORD_BCRYPT);
|
||||
}
|
||||
|
||||
if ($is_admin !== null) {
|
||||
$values['is_admin'] = (int) $is_admin;
|
||||
}
|
||||
|
||||
if (! empty($values)) {
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('id', $user_id)
|
||||
->update($values);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function regenerate_tokens($user_id)
|
||||
{
|
||||
$user = get_user_by_id($user_id);
|
||||
list($fever_token, $fever_api_key) = generate_fever_api_key($user['username']);
|
||||
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('id', $user_id)
|
||||
->update(array(
|
||||
'api_token' => Helper\generate_token(),
|
||||
'bookmarklet_token' => Helper\generate_token(),
|
||||
'cronjob_token' => Helper\generate_token(),
|
||||
'feed_token' => Helper\generate_token(),
|
||||
'fever_token' => $fever_token,
|
||||
'fever_api_key' => $fever_api_key,
|
||||
));
|
||||
}
|
||||
|
||||
function remove_user($user_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->hashtable('settings')
|
||||
->get('username', 'password');
|
||||
->table(TABLE)
|
||||
->eq('id', $user_id)
|
||||
->remove();
|
||||
}
|
||||
|
||||
// Set last login date
|
||||
function set_last_login()
|
||||
function generate_fever_api_key($username)
|
||||
{
|
||||
$token = Helper\generate_token();
|
||||
$api_key = md5($username . ':' . $token);
|
||||
return array($token, $api_key);
|
||||
}
|
||||
|
||||
function get_all_users()
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->hashtable('settings')
|
||||
->put(array('last_login' => time()));
|
||||
->table(TABLE)
|
||||
->columns('id', 'username', 'is_admin', 'last_login')
|
||||
->asc('username')
|
||||
->asc('id')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
function get_user_by_id($user_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('id', $user_id)
|
||||
->findOne();
|
||||
}
|
||||
|
||||
function get_user_by_id_without_password($user_id)
|
||||
{
|
||||
$user = Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('id', $user_id)
|
||||
->findOne();
|
||||
|
||||
unset($user['password']);
|
||||
return $user;
|
||||
}
|
||||
|
||||
function get_user_by_username($username)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('username', $username)
|
||||
->findOne();
|
||||
}
|
||||
|
||||
function get_user_by_token($key, $token)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq($key, $token)
|
||||
->findOne();
|
||||
}
|
||||
|
||||
function set_last_login_date($user_id)
|
||||
{
|
||||
return Database::getInstance('db')
|
||||
->table(TABLE)
|
||||
->eq('id', $user_id)
|
||||
->update(array('last_login' => time()));
|
||||
}
|
||||
|
@ -5,396 +5,129 @@ namespace Miniflux\Schema;
|
||||
use PDO;
|
||||
use Miniflux\Helper;
|
||||
|
||||
const VERSION = 44;
|
||||
const VERSION = 1;
|
||||
|
||||
|
||||
function version_44(PDO $pdo)
|
||||
function version_1(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('INSERT INTO settings ("key", "value") VALUES ("item_title_link", "full")');
|
||||
}
|
||||
|
||||
function version_43(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('DROP TABLE favicons');
|
||||
|
||||
$pdo->exec(
|
||||
'CREATE TABLE favicons (
|
||||
$pdo->exec('CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
hash TEXT UNIQUE,
|
||||
type TEXT
|
||||
)'
|
||||
);
|
||||
username TEXT UNIQUE,
|
||||
password TEXT NOT NULL,
|
||||
is_admin INTEGER DEFAULT 0,
|
||||
last_login INTEGER,
|
||||
api_token TEXT NOT NULL UNIQUE,
|
||||
bookmarklet_token TEXT NOT NULL UNIQUE,
|
||||
cronjob_token TEXT NOT NULL UNIQUE,
|
||||
feed_token TEXT NOT NULL UNIQUE,
|
||||
fever_token TEXT NOT NULL UNIQUE,
|
||||
fever_api_key TEXT NOT NULL UNIQUE
|
||||
)');
|
||||
|
||||
$pdo->exec('
|
||||
CREATE TABLE "favicons_feeds" (
|
||||
$pdo->exec('CREATE TABLE user_settings (
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"key" TEXT NOT NULL,
|
||||
"value" TEXT NOT NULL,
|
||||
PRIMARY KEY("user_id", "key"),
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$pdo->exec('CREATE TABLE feeds (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
feed_url TEXT NOT NULL,
|
||||
site_url TEXT,
|
||||
title TEXT COLLATE NOCASE,
|
||||
last_checked INTEGER DEFAULT 0,
|
||||
last_modified TEXT,
|
||||
etag TEXT,
|
||||
enabled INTEGER DEFAULT 1,
|
||||
download_content INTEGER DEFAULT 0,
|
||||
parsing_error INTEGER DEFAULT 0,
|
||||
rtl INTEGER DEFAULT 0,
|
||||
cloak_referrer INTEGER DEFAULT 0,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
UNIQUE(user_id, feed_url)
|
||||
)');
|
||||
|
||||
$pdo->exec('CREATE TABLE items (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
feed_id INTEGER NOT NULL,
|
||||
favicon_id INTEGER NOT NULL,
|
||||
PRIMARY KEY(feed_id, favicon_id),
|
||||
FOREIGN KEY(favicon_id) REFERENCES favicons(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE
|
||||
)
|
||||
');
|
||||
}
|
||||
checksum TEXT NOT NULL,
|
||||
status TEXT,
|
||||
bookmark INTEGER DEFAULT 0,
|
||||
url TEXT,
|
||||
title TEXT COLLATE NOCASE,
|
||||
author TEXT,
|
||||
content TEXT,
|
||||
updated INTEGER,
|
||||
enclosure_url TEXT,
|
||||
enclosure_type TEXT,
|
||||
language TEXT,
|
||||
rtl INTEGER DEFAULT 0,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE,
|
||||
UNIQUE(feed_id, checksum)
|
||||
)');
|
||||
|
||||
function version_42(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('DROP TABLE favicons');
|
||||
|
||||
$pdo->exec(
|
||||
'CREATE TABLE favicons (
|
||||
feed_id INTEGER UNIQUE,
|
||||
link TEXT,
|
||||
file TEXT,
|
||||
type TEXT,
|
||||
FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE
|
||||
)'
|
||||
);
|
||||
}
|
||||
|
||||
function version_41(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('
|
||||
CREATE TABLE "groups" (
|
||||
$pdo->exec('CREATE TABLE "groups" (
|
||||
id INTEGER PRIMARY KEY,
|
||||
title TEXT
|
||||
)
|
||||
');
|
||||
user_id INTEGER NOT NULL,
|
||||
title TEXT COLLATE NOCASE,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
UNIQUE(user_id, title)
|
||||
)');
|
||||
|
||||
$pdo->exec('
|
||||
CREATE TABLE "feeds_groups" (
|
||||
$pdo->exec('CREATE TABLE "feeds_groups" (
|
||||
feed_id INTEGER NOT NULL,
|
||||
group_id INTEGER NOT NULL,
|
||||
PRIMARY KEY(feed_id, group_id),
|
||||
FOREIGN KEY(group_id) REFERENCES groups(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE
|
||||
)
|
||||
');
|
||||
}
|
||||
)');
|
||||
|
||||
function version_40(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('UPDATE settings SET "value"="https://github.com/miniflux/miniflux/archive/master.zip" WHERE "key"="auto_update_url"');
|
||||
}
|
||||
|
||||
function version_39(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE feeds ADD COLUMN cloak_referrer INTEGER DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_38(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('INSERT INTO settings ("key", "value") VALUES ("original_marks_read", 1)');
|
||||
}
|
||||
|
||||
function version_37(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('INSERT INTO settings ("key", "value") VALUES ("debug_mode", 0)');
|
||||
}
|
||||
|
||||
function version_36(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('INSERT INTO settings ("key", "value") VALUES ("frontend_updatecheck_interval", 10)');
|
||||
}
|
||||
|
||||
function version_35(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('DELETE FROM favicons WHERE icon = ""');
|
||||
|
||||
$pdo->exec('
|
||||
CREATE TABLE settings (
|
||||
"key" TEXT NOT NULL UNIQUE,
|
||||
"value" TEXT Default NULL,
|
||||
PRIMARY KEY(key)
|
||||
)
|
||||
');
|
||||
|
||||
$pdo->exec("
|
||||
INSERT INTO settings (key,value)
|
||||
SELECT 'username', username FROM config UNION
|
||||
SELECT 'password', password FROM config UNION
|
||||
SELECT 'language', language FROM config UNION
|
||||
SELECT 'autoflush', autoflush FROM config UNION
|
||||
SELECT 'nocontent', nocontent FROM config UNION
|
||||
SELECT 'items_per_page', items_per_page FROM config UNION
|
||||
SELECT 'theme', theme FROM config UNION
|
||||
SELECT 'api_token', api_token FROM config UNION
|
||||
SELECT 'feed_token', feed_token FROM config UNION
|
||||
SELECT 'items_sorting_direction', items_sorting_direction FROM config UNION
|
||||
SELECT 'redirect_nothing_to_read', redirect_nothing_to_read FROM config UNION
|
||||
SELECT 'timezone', timezone FROM config UNION
|
||||
SELECT 'auto_update_url', auto_update_url FROM config UNION
|
||||
SELECT 'bookmarklet_token', bookmarklet_token FROM config UNION
|
||||
SELECT 'items_display_mode', items_display_mode FROM config UNION
|
||||
SELECT 'fever_token', fever_token FROM config UNION
|
||||
SELECT 'autoflush_unread', autoflush_unread FROM config UNION
|
||||
SELECT 'pinboard_enabled', pinboard_enabled FROM config UNION
|
||||
SELECT 'pinboard_token', pinboard_token FROM config UNION
|
||||
SELECT 'pinboard_tags', pinboard_tags FROM config UNION
|
||||
SELECT 'instapaper_enabled', instapaper_enabled FROM config UNION
|
||||
SELECT 'instapaper_username', instapaper_username FROM config UNION
|
||||
SELECT 'instapaper_password', instapaper_password FROM config UNION
|
||||
SELECT 'image_proxy', image_proxy FROM config UNION
|
||||
SELECT 'favicons', favicons FROM config
|
||||
");
|
||||
|
||||
$pdo->exec('DROP TABLE config');
|
||||
}
|
||||
|
||||
function version_34(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN favicons INTEGER DEFAULT 0');
|
||||
|
||||
$pdo->exec(
|
||||
'CREATE TABLE favicons (
|
||||
feed_id INTEGER UNIQUE,
|
||||
link TEXT,
|
||||
icon TEXT,
|
||||
FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE
|
||||
)'
|
||||
);
|
||||
}
|
||||
|
||||
function version_33(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN image_proxy INTEGER DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_32(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN instapaper_enabled INTEGER DEFAULT 0');
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN instapaper_username TEXT');
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN instapaper_password TEXT');
|
||||
}
|
||||
|
||||
function version_31(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN pinboard_enabled INTEGER DEFAULT 0');
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN pinboard_token TEXT');
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN pinboard_tags TEXT DEFAULT "miniflux"');
|
||||
}
|
||||
|
||||
function version_30(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN autoflush_unread INTEGER DEFAULT 45');
|
||||
}
|
||||
|
||||
function version_29(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN fever_token INTEGER DEFAULT "'.substr(Helper\generate_token(), 0, 8).'"');
|
||||
}
|
||||
|
||||
function version_28(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE feeds ADD COLUMN rtl INTEGER DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_27(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN items_display_mode TEXT DEFAULT "summaries"');
|
||||
}
|
||||
|
||||
function version_26(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN bookmarklet_token TEXT DEFAULT "'.Helper\generate_token().'"');
|
||||
}
|
||||
|
||||
function version_25(PDO $pdo)
|
||||
{
|
||||
$pdo->exec(
|
||||
'CREATE TABLE remember_me (
|
||||
$pdo->exec('CREATE TABLE favicons (
|
||||
id INTEGER PRIMARY KEY,
|
||||
username TEXT,
|
||||
hash TEXT UNIQUE,
|
||||
type TEXT
|
||||
)');
|
||||
|
||||
$pdo->exec('CREATE TABLE "favicons_feeds" (
|
||||
feed_id INTEGER NOT NULL,
|
||||
favicon_id INTEGER NOT NULL,
|
||||
PRIMARY KEY(feed_id, favicon_id),
|
||||
FOREIGN KEY(favicon_id) REFERENCES favicons(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$pdo->exec('CREATE TABLE remember_me (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
ip TEXT,
|
||||
user_agent TEXT,
|
||||
token TEXT,
|
||||
sequence TEXT,
|
||||
expiration INTEGER,
|
||||
date_creation INTEGER
|
||||
)'
|
||||
);
|
||||
}
|
||||
date_creation INTEGER,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
function version_24(PDO $pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN auto_update_url TEXT DEFAULT 'https://github.com/fguillot/miniflux/archive/master.zip'");
|
||||
}
|
||||
|
||||
function version_23(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE items ADD COLUMN language TEXT');
|
||||
}
|
||||
|
||||
function version_22(PDO $pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN timezone TEXT DEFAULT 'UTC'");
|
||||
}
|
||||
|
||||
function version_21(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE items ADD COLUMN enclosure TEXT');
|
||||
$pdo->exec('ALTER TABLE items ADD COLUMN enclosure_type TEXT');
|
||||
}
|
||||
|
||||
function version_20(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN redirect_nothing_to_read TEXT DEFAULT "feeds"');
|
||||
}
|
||||
|
||||
function version_19(PDO $pdo)
|
||||
{
|
||||
$rq = $pdo->prepare('SELECT autoflush FROM config');
|
||||
$rq->execute();
|
||||
$value = (int) $rq->fetchColumn();
|
||||
|
||||
// Change default value of autoflush to 15 days to avoid very large database
|
||||
if ($value <= 0) {
|
||||
$rq = $pdo->prepare('UPDATE config SET autoflush=?');
|
||||
$rq->execute(array(15));
|
||||
}
|
||||
}
|
||||
|
||||
function version_18(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE feeds ADD COLUMN parsing_error INTEGER DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_17(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN items_sorting_direction TEXT DEFAULT "desc"');
|
||||
}
|
||||
|
||||
function version_16(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN auth_google_token TEXT DEFAULT ""');
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN auth_mozilla_token TEXT DEFAULT ""');
|
||||
}
|
||||
|
||||
function version_15(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE feeds ADD COLUMN download_content INTEGER DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_14(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN feed_token TEXT DEFAULT "'.Helper\generate_token().'"');
|
||||
}
|
||||
|
||||
function version_13(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE feeds ADD COLUMN enabled INTEGER DEFAULT 1');
|
||||
}
|
||||
|
||||
function version_12(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN api_token TEXT DEFAULT "'.Helper\generate_token().'"');
|
||||
}
|
||||
|
||||
function version_11(PDO $pdo)
|
||||
{
|
||||
$fever_token = Helper\generate_token();
|
||||
$rq = $pdo->prepare('
|
||||
SELECT
|
||||
items.id, items.url AS item_url, feeds.site_url
|
||||
FROM items
|
||||
LEFT JOIN feeds ON feeds.id=items.feed_id
|
||||
INSERT INTO users
|
||||
(username, password, is_admin, api_token, bookmarklet_token, cronjob_token, feed_token, fever_token, fever_api_key)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
');
|
||||
|
||||
$rq->execute();
|
||||
|
||||
$items = $rq->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item['id'] !== $item['item_url']) {
|
||||
$id = hash('crc32b', $item['id'].$item['site_url']);
|
||||
} else {
|
||||
$id = hash('crc32b', $item['item_url'].$item['site_url']);
|
||||
}
|
||||
|
||||
$rq = $pdo->prepare('UPDATE items SET id=? WHERE id=?');
|
||||
$rq->execute(array($id, $item['id']));
|
||||
}
|
||||
}
|
||||
|
||||
function version_10(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN theme TEXT DEFAULT "original"');
|
||||
}
|
||||
|
||||
function version_9(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN items_per_page INTEGER DEFAULT 100');
|
||||
}
|
||||
|
||||
function version_8(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE items ADD COLUMN bookmark INTEGER DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_7(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN nocontent INTEGER DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_6(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE config ADD COLUMN autoflush INTEGER DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_5(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE feeds ADD COLUMN last_checked INTEGER');
|
||||
}
|
||||
|
||||
function version_4(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('CREATE INDEX idx_status ON items(status)');
|
||||
}
|
||||
|
||||
function version_3(PDO $pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN language TEXT DEFAULT 'en_US'");
|
||||
}
|
||||
|
||||
function version_2(PDO $pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE feeds ADD COLUMN last_modified TEXT');
|
||||
$pdo->exec('ALTER TABLE feeds ADD COLUMN etag TEXT');
|
||||
}
|
||||
|
||||
function version_1(PDO $pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE config (
|
||||
username TEXT DEFAULT 'admin',
|
||||
password TEXT
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
INSERT INTO config
|
||||
(password)
|
||||
VALUES ('".\password_hash('admin', PASSWORD_BCRYPT)."')
|
||||
");
|
||||
|
||||
$pdo->exec('
|
||||
CREATE TABLE feeds (
|
||||
id INTEGER PRIMARY KEY,
|
||||
site_url TEXT,
|
||||
feed_url TEXT UNIQUE,
|
||||
title TEXT COLLATE NOCASE
|
||||
)
|
||||
');
|
||||
|
||||
$pdo->exec('
|
||||
CREATE TABLE items (
|
||||
id TEXT PRIMARY KEY,
|
||||
url TEXT,
|
||||
title TEXT,
|
||||
author TEXT,
|
||||
content TEXT,
|
||||
updated INTEGER,
|
||||
status TEXT,
|
||||
feed_id INTEGER,
|
||||
FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE
|
||||
)
|
||||
');
|
||||
$rq->execute(array(
|
||||
'admin',
|
||||
password_hash('admin', PASSWORD_BCRYPT),
|
||||
'1',
|
||||
Helper\generate_token(),
|
||||
Helper\generate_token(),
|
||||
Helper\generate_token(),
|
||||
Helper\generate_token(),
|
||||
$fever_token,
|
||||
md5('admin:'.$fever_token),
|
||||
));
|
||||
}
|
||||
|
@ -3,9 +3,12 @@
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="?action=config"><?php echo t('general') ?></a></li>
|
||||
<li><a href="?action=profile"><?php echo t('profile') ?></a></li>
|
||||
<?php if (Miniflux\Helper\is_admin()): ?>
|
||||
<li><a href="?action=users"><?php echo t('users') ?></a></li>
|
||||
<?php endif ?>
|
||||
<li><a href="?action=services"><?php echo t('external services') ?></a></li>
|
||||
<li><a href="?action=api"><?php echo t('api') ?></a></li>
|
||||
<li><a href="?action=database"><?php echo t('database') ?></a></li>
|
||||
<li><a href="?action=help"><?php echo t('help') ?></a></li>
|
||||
<li class="active"><a href="?action=about"><?php echo t('about') ?></a></li>
|
||||
</ul>
|
||||
@ -16,24 +19,26 @@
|
||||
<h3><?php echo t('Bookmarks') ?></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="<?php echo Miniflux\Helper\get_current_base_url(), '?action=bookmark-feed&database=', urlencode($db_name), '&token=', urlencode($config['feed_token']) ?>" target="_blank"><?php echo t('Bookmark RSS Feed') ?></a>
|
||||
<a href="<?php echo Miniflux\Helper\get_current_base_url(), '?action=bookmark-feed&token=', urlencode($user['feed_token']) ?>" target="_blank"><?php echo t('Bookmark RSS Feed') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<h3><?php echo t('Bookmarklet') ?></h3>
|
||||
<a class="bookmarklet" href="javascript:location.href='<?php echo Miniflux\Helper\get_current_base_url() ?>?action=subscribe&token=<?php echo urlencode($config['bookmarklet_token']) ?>&url='+encodeURIComponent(location.href)"><?php echo t('Subscribe with Miniflux') ?></a> (<?php echo t('Drag and drop this link to your bookmarks') ?>)
|
||||
<input type="text" class="auto-select" readonly="readonly" value="javascript:location.href='<?php echo Miniflux\Helper\get_current_base_url() ?>?action=subscribe&token=<?php echo urlencode($config['bookmarklet_token']) ?>&url='+encodeURIComponent(location.href)"/>
|
||||
<a class="bookmarklet" href="javascript:location.href='<?php echo Miniflux\Helper\get_current_base_url() ?>?action=subscribe&token=<?php echo urlencode($user['bookmarklet_token']) ?>&url='+encodeURIComponent(location.href)"><?php echo t('Subscribe with Miniflux') ?></a> (<?php echo t('Drag and drop this link to your bookmarks') ?>)
|
||||
<input type="text" class="auto-select" readonly="readonly" value="javascript:location.href='<?php echo Miniflux\Helper\get_current_base_url() ?>?action=subscribe&token=<?php echo urlencode($user['bookmarklet_token']) ?>&url='+encodeURIComponent(location.href)"/>
|
||||
</div>
|
||||
<?php if (ENABLE_CRONJOB_HTTP_ACCESS): ?>
|
||||
<div class="panel panel-default">
|
||||
<h3><?php echo t('Cronjob URL') ?></h3>
|
||||
<input type="text" class="auto-select" readonly="readonly" value="<?php echo Miniflux\Helper\get_current_base_url(), 'cronjob.php?token=', urlencode($user['cronjob_token']) ?>">
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="panel panel-default">
|
||||
<h3><?php echo t('About') ?></h3>
|
||||
<ul>
|
||||
<?php if (! empty($config['last_login'])): ?>
|
||||
<li><?php echo t('Last login:') ?> <strong><?php echo date('Y-m-d H:i', $config['last_login']) ?></strong></li>
|
||||
<?php endif ?>
|
||||
<li><?php echo t('Miniflux version:') ?> <strong><?php echo APP_VERSION ?></strong></li>
|
||||
<li><?php echo t('Official website:') ?> <a href="https://miniflux.net" rel="noreferrer" target="_blank">https://miniflux.net</a></li>
|
||||
<li><a href="?action=console"><?php echo t('Console') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -11,7 +11,6 @@
|
||||
</div>
|
||||
|
||||
<form method="post" action="?action=subscribe" autocomplete="off">
|
||||
|
||||
<?php echo Miniflux\Helper\form_hidden('csrf', $values) ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Website or Feed URL'), 'url') ?>
|
||||
@ -23,13 +22,13 @@
|
||||
|
||||
<p class="form-help"><?php echo t('Downloading full content is slower because Miniflux grab the content from the original website. You should use that for subscriptions that display only a summary. This feature doesn\'t work with all websites.') ?></p>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Groups'), 'groups'); ?>
|
||||
<?php echo Miniflux\Helper\form_label(t('Groups'), 'group_name'); ?>
|
||||
|
||||
<div id="grouplist">
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<?php echo Miniflux\Helper\form_checkbox('feed_group_ids[]', $group['title'], $group['id'], in_array($group['id'], $values['feed_group_ids']), 'hide') ?>
|
||||
<?php endforeach ?>
|
||||
<?php echo Miniflux\Helper\form_text('create_group', $values, array(), array('placeholder="'.t('add a new group').'"')) ?>
|
||||
<?php echo Miniflux\Helper\form_text('group_name', $values, array(), array('placeholder="'.t('add a new group').'"')) ?>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
|
@ -3,9 +3,12 @@
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="?action=config"><?php echo t('general') ?></a></li>
|
||||
<li><a href="?action=profile"><?php echo t('profile') ?></a></li>
|
||||
<?php if (Miniflux\Helper\is_admin()): ?>
|
||||
<li><a href="?action=users"><?php echo t('users') ?></a></li>
|
||||
<?php endif ?>
|
||||
<li><a href="?action=services"><?php echo t('external services') ?></a></li>
|
||||
<li class="active"><a href="?action=api"><?php echo t('api') ?></a></li>
|
||||
<li><a href="?action=database"><?php echo t('database') ?></a></li>
|
||||
<li><a href="?action=help"><?php echo t('help') ?></a></li>
|
||||
<li><a href="?action=about"><?php echo t('about') ?></a></li>
|
||||
</ul>
|
||||
@ -16,16 +19,16 @@
|
||||
<h3 id="fever"><?php echo t('Fever API') ?></h3>
|
||||
<ul>
|
||||
<li><?php echo t('API endpoint:') ?> <strong><?php echo Miniflux\Helper\get_current_base_url(), 'fever/' ?></strong></li>
|
||||
<li><?php echo t('API username:') ?> <strong><?php echo Miniflux\Helper\escape($config['username']) ?></strong></li>
|
||||
<li><?php echo t('API token:') ?> <strong><?php echo Miniflux\Helper\escape($config['fever_token']) ?></strong></li>
|
||||
<li><?php echo t('API username:') ?> <strong><?php echo Miniflux\Helper\escape($user['username']) ?></strong></li>
|
||||
<li><?php echo t('API token:') ?> <strong><?php echo Miniflux\Helper\escape($user['fever_token']) ?></strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<h3 id="api"><?php echo t('Miniflux API') ?></h3>
|
||||
<ul>
|
||||
<li><?php echo t('API endpoint:') ?> <strong><?php echo Miniflux\Helper\get_current_base_url(), 'jsonrpc.php' ?></strong></li>
|
||||
<li><?php echo t('API username:') ?> <strong><?php echo Miniflux\Helper\escape($config['username']) ?></strong></li>
|
||||
<li><?php echo t('API token:') ?> <strong><?php echo Miniflux\Helper\escape($config['api_token']) ?></strong></li>
|
||||
<li><?php echo t('API username:') ?> <strong><?php echo Miniflux\Helper\escape($user['username']) ?></strong></li>
|
||||
<li><?php echo t('API token:') ?> <strong><?php echo Miniflux\Helper\escape($user['api_token']) ?></strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -3,9 +3,12 @@
|
||||
<nav>
|
||||
<ul>
|
||||
<li class="active"><a href="?action=config"><?php echo t('general') ?></a></li>
|
||||
<li><a href="?action=profile"><?php echo t('profile') ?></a></li>
|
||||
<?php if (Miniflux\Helper\is_admin()): ?>
|
||||
<li><a href="?action=users"><?php echo t('users') ?></a></li>
|
||||
<?php endif ?>
|
||||
<li><a href="?action=services"><?php echo t('external services') ?></a></li>
|
||||
<li><a href="?action=api"><?php echo t('api') ?></a></li>
|
||||
<li><a href="?action=database"><?php echo t('database') ?></a></li>
|
||||
<li><a href="?action=help"><?php echo t('help') ?></a></li>
|
||||
<li><a href="?action=about"><?php echo t('about') ?></a></li>
|
||||
</ul>
|
||||
@ -13,19 +16,7 @@
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?action=config" autocomplete="off" id="config-form">
|
||||
|
||||
<h3><?php echo t('Authentication') ?></h3>
|
||||
<div class="options">
|
||||
<?php echo Miniflux\Helper\form_hidden('csrf', $values) ?>
|
||||
<?php echo Miniflux\Helper\form_label(t('Username'), 'username') ?>
|
||||
<?php echo Miniflux\Helper\form_text('username', $values, $errors, array('required')) ?><br/>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Password'), 'password') ?>
|
||||
<?php echo Miniflux\Helper\form_password('password', $values, $errors) ?><br/>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Confirmation'), 'confirmation') ?>
|
||||
<?php echo Miniflux\Helper\form_password('confirmation', $values, $errors) ?><br/>
|
||||
</div>
|
||||
|
||||
<h3><?php echo t('Application') ?></h3>
|
||||
<div class="options">
|
||||
@ -40,11 +31,9 @@
|
||||
|
||||
<?php if (ENABLE_AUTO_UPDATE): ?>
|
||||
<?php echo Miniflux\Helper\form_label(t('Auto-Update URL'), 'auto_update_url') ?>
|
||||
<?php echo Miniflux\Helper\form_text('auto_update_url', $values, $errors, array('required')) ?><br/>
|
||||
<?php echo Miniflux\Helper\form_text('auto_update_url', $values, $errors) ?><br/>
|
||||
<?php endif ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_checkbox('debug_mode', t('Enable debug mode'), 1, isset($values['debug_mode']) && $values['debug_mode'] == 1) ?><br/>
|
||||
|
||||
<?php echo Miniflux\Helper\form_checkbox('image_proxy', t('Enable image proxy'), 1, isset($values['image_proxy']) && $values['image_proxy'] == 1) ?>
|
||||
<div class="form-help"><?php echo t('Avoid mixed content warnings with HTTPS') ?></div>
|
||||
</div>
|
||||
|
10
app/templates/confirm_remove_user.php
Normal file
10
app/templates/confirm_remove_user.php
Normal file
@ -0,0 +1,10 @@
|
||||
<div class="page-header">
|
||||
<h2><?php echo t('Confirmation') ?></h2>
|
||||
</div>
|
||||
|
||||
<p class="alert alert-info"><?php echo t('Do you really want to remove this user: "%s"?', Miniflux\Helper\escape($user['username'])) ?></p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?action=remove-user&user_id=<?php echo $user['id'] ?>&csrf=<?php echo $csrf_token ?>" class="btn btn-red"><?php echo t('Remove') ?></a>
|
||||
<?php echo t('or') ?> <a href="?action=users"><?php echo t('cancel') ?></a>
|
||||
</div>
|
@ -1,13 +0,0 @@
|
||||
<div class="page-header">
|
||||
<h2><?php echo t('Console') ?></h2>
|
||||
<ul>
|
||||
<li><a href="?action=console"><?php echo t('refresh') ?></a></li>
|
||||
<li><a href="?action=flush-console"><?php echo t('flush messages') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php if (empty($content)): ?>
|
||||
<p class="alert alert-info"><?php echo t('Nothing to show. Enable the debug mode to see log messages.') ?></p>
|
||||
<?php else: ?>
|
||||
<pre id="console"><code><?php echo Miniflux\Helper\escape($content) ?></code></pre>
|
||||
<?php endif ?>
|
@ -17,11 +17,6 @@
|
||||
<li><?php echo t('Database size:') ?> <strong><?php echo Miniflux\Helper\format_bytes($db_size) ?></strong></li>
|
||||
<li><a href="?action=optimize-db&csrf=<?php echo $csrf ?>"><?php echo t('Optimize the database') ?></a> <?php echo t('(VACUUM command)') ?></li>
|
||||
<li><a href="?action=download-db&csrf=<?php echo $csrf ?>"><?php echo t('Download the entire database') ?></a> <?php echo t('(Gzip compressed Sqlite file)') ?></li>
|
||||
<?php if (ENABLE_MULTIPLE_DB): ?>
|
||||
<li>
|
||||
<a href="?action=new-db"><?php echo t('Add a new database (new user)') ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -29,13 +29,13 @@
|
||||
|
||||
<?php echo Miniflux\Helper\form_checkbox('enabled', t('Activated'), 1, $values['enabled']) ?><br />
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Groups'), 'groups'); ?>
|
||||
<?php echo Miniflux\Helper\form_label(t('Groups'), 'group_name'); ?>
|
||||
|
||||
<div id="grouplist">
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<?php echo Miniflux\Helper\form_checkbox('feed_group_ids[]', $group['title'], $group['id'], in_array($group['id'], $values['feed_group_ids']), 'hide') ?>
|
||||
<?php endforeach ?>
|
||||
<?php echo Miniflux\Helper\form_text('create_group', $values, array(), array('placeholder="'.t('add a new group').'"')) ?>
|
||||
<?php echo Miniflux\Helper\form_text('group_name', $values, array(), array('placeholder="'.t('add a new group').'"')) ?>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
|
39
app/templates/edit_user.php
Normal file
39
app/templates/edit_user.php
Normal file
@ -0,0 +1,39 @@
|
||||
<div class="page-header">
|
||||
<h2><?php echo $title ?></h2>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="?action=config"><?php echo t('general') ?></a></li>
|
||||
<li><a href="?action=profile"><?php echo t('profile') ?></a></li>
|
||||
<?php if (Miniflux\Helper\is_admin()): ?>
|
||||
<li class="active"><a href="?action=users"><?php echo t('users') ?></a></li>
|
||||
<?php endif ?>
|
||||
<li><a href="?action=services"><?php echo t('external services') ?></a></li>
|
||||
<li><a href="?action=api"><?php echo t('api') ?></a></li>
|
||||
<li><a href="?action=help"><?php echo t('help') ?></a></li>
|
||||
<li><a href="?action=about"><?php echo t('about') ?></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?action=edit-user" autocomplete="off" id="config-form">
|
||||
<div class="options">
|
||||
<?php echo Miniflux\Helper\form_hidden('csrf', $values) ?>
|
||||
<?php echo Miniflux\Helper\form_hidden('id', $values) ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Username'), 'username') ?>
|
||||
<?php echo Miniflux\Helper\form_text('username', $values, $errors, array('required')) ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Password'), 'password') ?>
|
||||
<?php echo Miniflux\Helper\form_password('password', $values, $errors) ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Confirmation'), 'confirmation') ?>
|
||||
<?php echo Miniflux\Helper\form_password('confirmation', $values, $errors) ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_checkbox('is_admin', t('Administrator'), 1, isset($values['is_admin']) && $values['is_admin'] == 1) ?>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?php echo t('Save') ?>" class="btn btn-blue">
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
@ -24,7 +24,7 @@
|
||||
|
||||
<?php if ($feed['parsing_error']): ?>
|
||||
<p class="alert alert-error">
|
||||
<?php echo tne('An error occurred during the last check. Refresh the feed manually and check the %sconsole%s for errors afterwards!','<a href="?action=console">','</a>') ?>
|
||||
<?php echo t('An error occurred during the last check. You could enable the debug mode to have more information.') ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
<?php else: ?>
|
||||
|
||||
<?php if ($nb_failed_feeds > 0): ?>
|
||||
<p class="alert alert-error"><?php echo tne('An error occurred during the last check. Refresh the feed manually and check the %sconsole%s for errors afterwards!', '<a href="?action=console">', '</a>') ?></p>
|
||||
<p class="alert alert-error"><?php echo t('An error occurred during the last check. You could enable the debug mode to have more information.') ?></p>
|
||||
<?php elseif ($nothing_to_read): ?>
|
||||
<p class="alert alert-info"><?php echo tne('Nothing to read, do you want to %supdate your subscriptions%s?','<a href="?action=refresh-all" data-action="refresh-all">','</a>') ?></p>
|
||||
<?php endif ?>
|
||||
|
@ -3,9 +3,12 @@
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="?action=config"><?php echo t('general') ?></a></li>
|
||||
<li><a href="?action=profile"><?php echo t('profile') ?></a></li>
|
||||
<?php if (Miniflux\Helper\is_admin()): ?>
|
||||
<li><a href="?action=users"><?php echo t('users') ?></a></li>
|
||||
<?php endif ?>
|
||||
<li><a href="?action=services"><?php echo t('external services') ?></a></li>
|
||||
<li><a href="?action=api"><?php echo t('api') ?></a></li>
|
||||
<li><a href="?action=database"><?php echo t('database') ?></a></li>
|
||||
<li class="active"><a href="?action=help"><?php echo t('help') ?></a></li>
|
||||
<li><a href="?action=about"><?php echo t('about') ?></a></li>
|
||||
</ul>
|
||||
|
@ -6,7 +6,7 @@
|
||||
data-item-bookmark="<?php echo $item['bookmark'] ?>"
|
||||
<?php echo $hide ? 'data-hide="true"' : '' ?>
|
||||
>
|
||||
<h2 <?php echo Miniflux\Helper\is_rtl($item) ? 'dir="rtl"' : 'dir="ltr"' ?>>
|
||||
<h2 <?php echo Miniflux\Helper\rtl($item) ?>>
|
||||
<span class="item-icons">
|
||||
<a
|
||||
class="bookmark-icon"
|
||||
@ -70,16 +70,16 @@
|
||||
<a href="<?php echo $item['url'] ?>" class="original" rel="noreferrer" target="_blank" <?php echo ($original_marks_read) ? 'data-action="mark-read"' : '' ?>><?php echo t('original link') ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($item['enclosure']): ?>
|
||||
<?php if ($item['enclosure_url']): ?>
|
||||
<li>
|
||||
<?php if (strpos($item['enclosure_type'], 'video/') === 0): ?>
|
||||
<a href="<?php echo $item['enclosure'] ?>" class="video-enclosure" rel="noreferrer" target="_blank"><?php echo t('attachment') ?></a>
|
||||
<a href="<?php echo $item['enclosure_url'] ?>" class="video-enclosure" rel="noreferrer" target="_blank"><?php echo t('attachment') ?></a>
|
||||
<?php elseif(strpos($item['enclosure_type'], 'audio/') === 0): ?>
|
||||
<a href="<?php echo $item['enclosure'] ?>" class="audio-enclosure" rel="noreferrer" target="_blank"><?php echo t('attachment') ?></a>
|
||||
<a href="<?php echo $item['enclosure_url'] ?>" class="audio-enclosure" rel="noreferrer" target="_blank"><?php echo t('attachment') ?></a>
|
||||
<?php elseif(strpos($item['enclosure_type'], 'image/') === 0): ?>
|
||||
<a href="<?php echo $item['enclosure'] ?>" class="image-enclosure" rel="noreferrer" target="_blank"><?php echo t('attachment') ?></a>
|
||||
<a href="<?php echo $item['enclosure_url'] ?>" class="image-enclosure" rel="noreferrer" target="_blank"><?php echo t('attachment') ?></a>
|
||||
<?php else: ?>
|
||||
<a href="<?php echo $item['enclosure'] ?>" class="enclosure" rel="noreferrer" target="_blank"><?php echo t('attachment') ?></a>
|
||||
<a href="<?php echo $item['enclosure_url'] ?>" class="enclosure" rel="noreferrer" target="_blank"><?php echo t('attachment') ?></a>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
@ -87,9 +87,9 @@
|
||||
<?php echo Miniflux\Template\load('status_links', array('item' => $item, 'menu' => $menu, 'offset' => $offset)) ?>
|
||||
</ul>
|
||||
<?php if ($display_mode === 'full'): ?>
|
||||
<div class="preview-full-content" <?php echo Miniflux\Helper\is_rtl($item) ? 'dir="rtl"' : 'dir="ltr"' ?>><?php echo $item['content'] ?></div>
|
||||
<div class="preview-full-content" <?php echo Miniflux\Helper\rtl($item) ?>><?php echo $item['content'] ?></div>
|
||||
<?php elseif ($display_mode === 'summaries'): ?>
|
||||
<p class="preview" <?php echo Miniflux\Helper\is_rtl($item) ? 'dir="rtl"' : 'dir="ltr"' ?>><?php echo Miniflux\Helper\escape(Miniflux\Helper\summary(strip_tags($item['content']), 50, 300)) ?></p>
|
||||
<p class="preview" <?php echo Miniflux\Helper\rtl($item) ?>><?php echo Miniflux\Helper\escape(Miniflux\Helper\summary(strip_tags($item['content']), 50, 300)) ?></p>
|
||||
<?php else: ?>
|
||||
<p class="no-preview"></p>
|
||||
<?php endif ?>
|
||||
|
@ -39,16 +39,6 @@
|
||||
|
||||
<?php echo Miniflux\Helper\form_checkbox('remember_me', t('Remember Me'), 1) ?><br/>
|
||||
|
||||
<?php if (ENABLE_MULTIPLE_DB && count($databases) > 1): ?>
|
||||
<div id="database-selector">
|
||||
<h4><?php echo t('Select another database') ?></h4>
|
||||
<?php foreach ($databases as $filename => $dbname): ?>
|
||||
<?php echo Miniflux\Helper\form_radio('database', $dbname, $filename, ($current_database === $filename)) ?>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?php echo t('Sign in') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
|
@ -1,36 +0,0 @@
|
||||
<div class="page-header">
|
||||
<h2><?php echo t('New database') ?></h2>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="?action=config"><?php echo t('general') ?></a></li>
|
||||
<li><a href="?action=services"><?php echo t('external services') ?></a></li>
|
||||
<li><a href="?action=api"><?php echo t('api') ?></a></li>
|
||||
<li class="active"><a href="?action=database"><?php echo t('database') ?></a></li>
|
||||
<li><a href="?action=help"><?php echo t('help') ?></a></li>
|
||||
<li><a href="?action=about"><?php echo t('about') ?></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<form method="post" action="?action=new-db" autocomplete="off">
|
||||
|
||||
<?php echo Miniflux\Helper\form_hidden('csrf', $values) ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Database name'), 'name') ?>
|
||||
<?php echo Miniflux\Helper\form_text('name', $values, $errors, array('required', 'autofocus')) ?>
|
||||
<p class="form-help"><?php echo t('The name must have only alpha-numeric characters') ?></p>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Username'), 'username') ?>
|
||||
<?php echo Miniflux\Helper\form_text('username', $values, $errors, array('required')) ?><br/>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Password'), 'password') ?>
|
||||
<?php echo Miniflux\Helper\form_password('password', $values, $errors, array('required')) ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Confirmation'), 'confirmation') ?>
|
||||
<?php echo Miniflux\Helper\form_password('confirmation', $values, $errors, array('required')) ?><br/>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-blue"><?php echo t('Create') ?></button>
|
||||
<?php echo t('or') ?> <a href="?action=config"><?php echo t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
37
app/templates/new_user.php
Normal file
37
app/templates/new_user.php
Normal file
@ -0,0 +1,37 @@
|
||||
<div class="page-header">
|
||||
<h2><?php echo $title ?></h2>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="?action=config"><?php echo t('general') ?></a></li>
|
||||
<li><a href="?action=profile"><?php echo t('profile') ?></a></li>
|
||||
<?php if (Miniflux\Helper\is_admin()): ?>
|
||||
<li class="active"><a href="?action=users"><?php echo t('users') ?></a></li>
|
||||
<?php endif ?>
|
||||
<li><a href="?action=services"><?php echo t('external services') ?></a></li>
|
||||
<li><a href="?action=api"><?php echo t('api') ?></a></li>
|
||||
<li><a href="?action=help"><?php echo t('help') ?></a></li>
|
||||
<li><a href="?action=about"><?php echo t('about') ?></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?action=new-user" autocomplete="off" id="config-form">
|
||||
<div class="options">
|
||||
<?php echo Miniflux\Helper\form_hidden('csrf', $values) ?>
|
||||
<?php echo Miniflux\Helper\form_label(t('Username'), 'username') ?>
|
||||
<?php echo Miniflux\Helper\form_text('username', $values, $errors, array('required', 'autofocus')) ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Password'), 'password') ?>
|
||||
<?php echo Miniflux\Helper\form_password('password', $values, $errors) ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Confirmation'), 'confirmation') ?>
|
||||
<?php echo Miniflux\Helper\form_password('confirmation', $values, $errors) ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_checkbox('is_admin', t('Administrator'), 1, isset($values['is_admin']) && $values['is_admin'] == 1) ?>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?php echo t('Save') ?>" class="btn btn-blue">
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
39
app/templates/profile.php
Normal file
39
app/templates/profile.php
Normal file
@ -0,0 +1,39 @@
|
||||
<div class="page-header">
|
||||
<h2><?php echo $title ?></h2>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="?action=config"><?php echo t('general') ?></a></li>
|
||||
<li class="active"><a href="?action=profile"><?php echo t('profile') ?></a></li>
|
||||
<?php if (Miniflux\Helper\is_admin()): ?>
|
||||
<li><a href="?action=users"><?php echo t('users') ?></a></li>
|
||||
<?php endif ?>
|
||||
<li><a href="?action=services"><?php echo t('external services') ?></a></li>
|
||||
<li><a href="?action=api"><?php echo t('api') ?></a></li>
|
||||
<li><a href="?action=help"><?php echo t('help') ?></a></li>
|
||||
<li><a href="?action=about"><?php echo t('about') ?></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?action=profile" autocomplete="off" id="config-form">
|
||||
|
||||
<h3><?php echo t('Authentication') ?></h3>
|
||||
<div class="options">
|
||||
<?php echo Miniflux\Helper\form_hidden('csrf', $values) ?>
|
||||
<?php echo Miniflux\Helper\form_hidden('id', $values) ?>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Username'), 'username') ?>
|
||||
<?php echo Miniflux\Helper\form_text('username', $values, $errors, array('required')) ?><br/>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Password'), 'password') ?>
|
||||
<?php echo Miniflux\Helper\form_password('password', $values, $errors) ?><br/>
|
||||
|
||||
<?php echo Miniflux\Helper\form_label(t('Confirmation'), 'confirmation') ?>
|
||||
<?php echo Miniflux\Helper\form_password('confirmation', $values, $errors) ?><br/>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?php echo t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
@ -3,9 +3,12 @@
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="?action=config"><?php echo t('general') ?></a></li>
|
||||
<li><a href="?action=profile"><?php echo t('profile') ?></a></li>
|
||||
<?php if (Miniflux\Helper\is_admin()): ?>
|
||||
<li><a href="?action=users"><?php echo t('users') ?></a></li>
|
||||
<?php endif ?>
|
||||
<li class="active"><a href="?action=services"><?php echo t('external services') ?></a></li>
|
||||
<li><a href="?action=api"><?php echo t('api') ?></a></li>
|
||||
<li><a href="?action=database"><?php echo t('database') ?></a></li>
|
||||
<li><a href="?action=help"><?php echo t('help') ?></a></li>
|
||||
<li><a href="?action=about"><?php echo t('about') ?></a></li>
|
||||
</ul>
|
||||
|
@ -29,7 +29,7 @@
|
||||
</nav>
|
||||
<?php endif ?>
|
||||
|
||||
<h1 <?php echo Miniflux\Helper\is_rtl($item + array('rtl' => $feed['rtl'])) ? 'dir="rtl"' : 'dir="ltr"' ?>>
|
||||
<h1 <?php echo Miniflux\Helper\rtl($item) ?>>
|
||||
<a href="<?php echo $item['url'] ?>" rel="noreferrer" target="_blank" class="original"><?php echo Miniflux\Helper\escape($item['title']) ?></a>
|
||||
</h1>
|
||||
|
||||
@ -54,9 +54,9 @@
|
||||
<li class="hide-mobile">
|
||||
<span title="<?php echo dt('%e %B %Y %k:%M', $item['updated']) ?>"><?php echo Miniflux\Helper\relative_time($item['updated']) ?></span>
|
||||
</li>
|
||||
<?php if ($item['enclosure']): ?>
|
||||
<?php if ($item['enclosure_url']): ?>
|
||||
<li>
|
||||
<a href="<?php echo $item['enclosure'] ?>" rel="noreferrer" target="_blank"><?php echo t('attachment') ?></a>
|
||||
<a href="<?php echo $item['enclosure_url'] ?>" rel="noreferrer" target="_blank"><?php echo t('attachment') ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li class="hide-mobile">
|
||||
@ -74,24 +74,24 @@
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
|
||||
<div id="item-content" <?php echo Miniflux\Helper\is_rtl($item + array('rtl' => $feed['rtl'])) ? 'dir="rtl"' : 'dir="ltr"' ?>>
|
||||
<div id="item-content" <?php echo Miniflux\Helper\rtl($item) ?>>
|
||||
|
||||
<?php if ($item['enclosure']): ?>
|
||||
<?php if ($item['enclosure_url']): ?>
|
||||
<?php if (strpos($item['enclosure_type'], 'audio') !== false): ?>
|
||||
<div id="item-content-enclosure">
|
||||
<audio controls>
|
||||
<source src="<?php echo $item['enclosure'] ?>" type="<?php echo $item['enclosure_type'] ?>">
|
||||
<source src="<?php echo $item['enclosure_url'] ?>" type="<?php echo $item['enclosure_type'] ?>">
|
||||
</audio>
|
||||
</div>
|
||||
<?php elseif (strpos($item['enclosure_type'], 'video') !== false): ?>
|
||||
<div id="item-content-enclosure">
|
||||
<video controls>
|
||||
<source src="<?php echo $item['enclosure'] ?>" type="<?php echo $item['enclosure_type'] ?>">
|
||||
<source src="<?php echo $item['enclosure_url'] ?>" type="<?php echo $item['enclosure_type'] ?>">
|
||||
</video>
|
||||
</div>
|
||||
<?php elseif (strpos($item['enclosure_type'], 'image') !== false && $item['content'] === ''): ?>
|
||||
<div id="item-content-enclosure">
|
||||
<img src="<?php echo $item['enclosure'] ?>" alt="enclosure"/>
|
||||
<img src="<?php echo $item['enclosure_url'] ?>" alt="enclosure"/>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
<?php echo Miniflux\Template\load('search_form') ?>
|
||||
|
||||
<div class="page-header">
|
||||
<h2><?php echo t('Unread') ?><span id="page-counter"><?php echo isset($nb_items) ? $nb_items : '' ?></span></h2>
|
||||
<?php if (!empty($groups)): ?>
|
||||
|
40
app/templates/users.php
Normal file
40
app/templates/users.php
Normal file
@ -0,0 +1,40 @@
|
||||
<div class="page-header">
|
||||
<h2><?php echo $title ?></h2>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="?action=config"><?php echo t('general') ?></a></li>
|
||||
<li><a href="?action=profile"><?php echo t('profile') ?></a></li>
|
||||
<?php if (Miniflux\Helper\is_admin()): ?>
|
||||
<li class="active"><a href="?action=users"><?php echo t('users') ?></a></li>
|
||||
<?php endif ?>
|
||||
<li><a href="?action=services"><?php echo t('external services') ?></a></li>
|
||||
<li><a href="?action=api"><?php echo t('api') ?></a></li>
|
||||
<li><a href="?action=help"><?php echo t('help') ?></a></li>
|
||||
<li><a href="?action=about"><?php echo t('about') ?></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<section>
|
||||
<p><a href="?action=new-user"><?php echo t('New User') ?></a><br><br></p>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?php echo t('Username') ?></th>
|
||||
<th><?php echo t('Administrator') ?></th>
|
||||
<th><?php echo t('Action') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo Miniflux\Helper\escape($user['username']) ?>
|
||||
</td>
|
||||
<td><?php echo $user['is_admin'] ? t('Yes') : t('No') ?></td>
|
||||
<td>
|
||||
<?php if (Miniflux\Helper\get_user_id() != $user['id']): ?>
|
||||
<a href="?action=edit-user&user_id=<?php echo $user['id'] ?>"><?php echo t('Edit') ?></a> -
|
||||
<a href="?action=confirm-remove-user&user_id=<?php echo $user['id'] ?>"><?php echo t('Remove') ?></a>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</section>
|
@ -8,31 +8,17 @@ use SimpleValidator\Validators;
|
||||
function validate_modification(array $values)
|
||||
{
|
||||
$rules = array(
|
||||
new Validators\Required('username', t('The user name is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is 50 characters'), 50),
|
||||
new Validators\Required('autoflush', t('Value required')),
|
||||
new Validators\Required('autoflush_unread', t('Value required')),
|
||||
new Validators\Required('items_per_page', t('Value required')),
|
||||
new Validators\Integer('items_per_page', t('Must be an integer')),
|
||||
new Validators\Required('theme', t('Value required')),
|
||||
new Validators\Integer('frontend_updatecheck_interval', t('Must be an integer')),
|
||||
new Validators\Integer('debug_mode', t('Must be an integer')),
|
||||
new Validators\Integer('nocontent', t('Must be an integer')),
|
||||
new Validators\Integer('favicons', t('Must be an integer')),
|
||||
new Validators\Integer('original_marks_read', t('Must be an integer')),
|
||||
);
|
||||
|
||||
if (ENABLE_AUTO_UPDATE) {
|
||||
$rules[] = new Validators\Required('auto_update_url', t('Value required'));
|
||||
}
|
||||
|
||||
if (! empty($values['password'])) {
|
||||
$rules[] = new Validators\Required('password', t('The password is required'));
|
||||
$rules[] = new Validators\MinLength('password', t('The minimum length is 6 characters'), 6);
|
||||
$rules[] = new Validators\Required('confirmation', t('The confirmation is required'));
|
||||
$rules[] = new Validators\Equals('password', 'confirmation', t('Passwords don\'t match'));
|
||||
}
|
||||
|
||||
$v = new Validator($values, $rules);
|
||||
|
||||
return array(
|
||||
|
@ -2,25 +2,41 @@
|
||||
|
||||
namespace Miniflux\Validator\User;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use Miniflux\Model\Config;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use Miniflux\Model\User as UserModel;
|
||||
use Miniflux\Model\Database as DatabaseModel;
|
||||
use Miniflux\Model\RememberMe;
|
||||
use Miniflux\Request;
|
||||
use PicoDb\Database;
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
function validate_modification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('The user id required')),
|
||||
new Validators\Required('username', t('The user name is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is 50 characters'), 50),
|
||||
new Validators\MinLength('password', t('The minimum length is 6 characters'), 6),
|
||||
new Validators\Equals('password', 'confirmation', t('Passwords don\'t match')),
|
||||
new Validators\Unique('username', t('The username must be unique'), Database::getInstance('db')->getConnection(), 'users', 'id'),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
function validate_creation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('name', t('The database name is required')),
|
||||
new Validators\AlphaNumeric('name', t('The name must have only alpha-numeric characters')),
|
||||
new Validators\Required('username', t('The user name is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is 50 characters'), 50),
|
||||
new Validators\Required('password', t('The password is required')),
|
||||
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 don\'t match')),
|
||||
new Validators\Unique('username', t('The username must be unique'), Database::getInstance('db')->getConnection(), 'users', 'id'),
|
||||
));
|
||||
|
||||
return array(
|
||||
@ -41,16 +57,15 @@ function validate_login(array $values)
|
||||
$errors = $v->getErrors();
|
||||
|
||||
if ($result) {
|
||||
$credentials = UserModel\get_credentials();
|
||||
$user = UserModel\get_user_by_username($values['username']);
|
||||
|
||||
if ($credentials && $credentials['username'] === $values['username'] && password_verify($values['password'], $credentials['password'])) {
|
||||
UserModel\set_last_login();
|
||||
$_SESSION['loggedin'] = true;
|
||||
$_SESSION['config'] = Config\get_all();
|
||||
if (! empty($user) && password_verify($values['password'], $user['password'])) {
|
||||
SessionStorage::getInstance()->setUser($user);
|
||||
UserModel\set_last_login_date($user['id']);
|
||||
|
||||
// Setup the remember me feature
|
||||
if (! empty($values['remember_me'])) {
|
||||
$cookie = RememberMe\create(DatabaseModel\select(), $values['username'], Request\get_ip_address(), Request\get_user_agent());
|
||||
$cookie = RememberMe\create($user['id'], Request\get_ip_address(), Request\get_user_agent());
|
||||
RememberMe\write_cookie($cookie['token'], $cookie['sequence'], $cookie['expiration']);
|
||||
}
|
||||
} else {
|
||||
|
@ -13,7 +13,7 @@
|
||||
},
|
||||
"require": {
|
||||
"fguillot/simple-validator": "v1.0.0",
|
||||
"fguillot/json-rpc": "v1.2.1",
|
||||
"fguillot/json-rpc": "v1.2.3",
|
||||
"fguillot/picodb": "v1.0.14 ",
|
||||
"fguillot/picofeed": "v0.1.25",
|
||||
"pda/pheanstalk": "v3.1.0",
|
||||
@ -28,6 +28,7 @@
|
||||
"files": [
|
||||
"app/schemas/sqlite.php",
|
||||
"app/helpers/app.php",
|
||||
"app/helpers/config.php",
|
||||
"app/helpers/csrf.php",
|
||||
"app/helpers/favicon.php",
|
||||
"app/helpers/form.php",
|
||||
@ -40,6 +41,8 @@
|
||||
"app/core/template.php",
|
||||
"app/handlers/scraper.php",
|
||||
"app/handlers/service.php",
|
||||
"app/handlers/feed.php",
|
||||
"app/handlers/item.php",
|
||||
"app/handlers/opml.php",
|
||||
"app/handlers/proxy.php",
|
||||
"app/models/config.php",
|
||||
@ -51,7 +54,6 @@
|
||||
"app/models/item_group.php",
|
||||
"app/models/bookmark.php",
|
||||
"app/models/auto_update.php",
|
||||
"app/models/database.php",
|
||||
"app/models/remember_me.php",
|
||||
"app/models/group.php",
|
||||
"app/models/favicon.php",
|
||||
|
@ -18,8 +18,8 @@ define('FAVICON_URL_PATH', 'data/favicons');
|
||||
// DB_FILENAME => default value is db.sqlite (default database filename)
|
||||
define('DB_FILENAME', 'db.sqlite');
|
||||
|
||||
// ENABLE_MULTIPLE_DB => default value is true (multiple users support)
|
||||
define('ENABLE_MULTIPLE_DB', true);
|
||||
// Enable/disable debug mode
|
||||
define('DEBUG_MODE', false);
|
||||
|
||||
// DEBUG_FILENAME => default is data/debug.log
|
||||
define('DEBUG_FILENAME', DATA_DIRECTORY.'/debug.log');
|
||||
@ -48,3 +48,6 @@ define('ENABLE_AUTO_UPDATE', true);
|
||||
// SUBSCRIPTION_CONCURRENT_REQUESTS => number of concurrent feeds to refresh at once
|
||||
// Reduce this number on systems with limited processing power
|
||||
define('SUBSCRIPTION_CONCURRENT_REQUESTS', 5);
|
||||
|
||||
// Allow the cronjob to be accessible from the browser
|
||||
define('ENABLE_CRONJOB_HTTP_ACCESS', true);
|
||||
|
36
cronjob.php
36
cronjob.php
@ -2,6 +2,7 @@
|
||||
|
||||
require __DIR__.'/app/common.php';
|
||||
|
||||
use Miniflux\Handler;
|
||||
use Miniflux\Model;
|
||||
|
||||
if (php_sapi_name() === 'cli') {
|
||||
@ -9,29 +10,30 @@ if (php_sapi_name() === 'cli') {
|
||||
'limit::',
|
||||
'call-interval::',
|
||||
'update-interval::',
|
||||
'database::',
|
||||
));
|
||||
} else {
|
||||
$token = isset($_GET['token']) ? $_GET['token'] : '';
|
||||
$user = Model\User\get_user_by_token('cronjob_token', $token);
|
||||
|
||||
if (empty($user) || !ENABLE_CRONJOB_HTTP_ACCESS) {
|
||||
die('Access Denied');
|
||||
}
|
||||
else {
|
||||
|
||||
$options = $_GET;
|
||||
}
|
||||
|
||||
if (! empty($options['database'])) {
|
||||
if (! Model\Database\select($options['database'])) {
|
||||
die("Database ".$options['database']." not found\r\n");
|
||||
}
|
||||
}
|
||||
$limit = get_cli_option('limit', $options);
|
||||
$update_interval = get_cli_option('update-interval', $options);
|
||||
$call_interval = get_cli_option('call-interval', $options);
|
||||
|
||||
$limit = ! empty($options['limit']) && ctype_digit($options['limit']) ? (int) $options['limit'] : Model\Feed\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 === Model\Feed\LIMIT_ALL && $update_interval >= $call_interval) {
|
||||
$feeds_count = PicoDb\Database::getInstance('db')->table('feeds')->count();
|
||||
foreach (Model\User\get_all_users() as $user) {
|
||||
if ($update_interval !== null && $call_interval !== null && $limit === null && $update_interval >= $call_interval) {
|
||||
$feeds_count = Model\Feed\count_feeds($user['id']);
|
||||
$limit = ceil($feeds_count / ($update_interval / $call_interval));
|
||||
}
|
||||
|
||||
Model\Feed\refresh_all($limit);
|
||||
Model\Item\autoflush_read();
|
||||
Model\Item\autoflush_unread();
|
||||
Model\Config\write_debug();
|
||||
Handler\Feed\update_feeds($user['id'], $limit);
|
||||
Model\Item\autoflush_read($user['id']);
|
||||
Model\Item\autoflush_unread($user['id']);
|
||||
Miniflux\Helper\write_debug_file();
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
|
||||
RewriteBase /
|
||||
|
||||
# only if the requested file does not exists
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
|
||||
# Store the current location in an environment variable CWD
|
||||
RewriteCond $0#%{REQUEST_URI} ([^#]*)#(.*)\1$
|
||||
RewriteRule ^.*$ - [E=CWD:%2]
|
||||
|
||||
# Just by prefixing the environment variable, we can safely rewrite anything now
|
||||
RewriteRule ^([^/]*) %{ENV:CWD}index.php?database=$1 [QSA,L]
|
||||
</IfModule>
|
198
fever/index.php
198
fever/index.php
@ -4,9 +4,10 @@ require __DIR__.'/../app/common.php';
|
||||
|
||||
use Miniflux\Handler;
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Model\Feed;
|
||||
use Miniflux\Model\Group;
|
||||
use PicoDb\Database;
|
||||
|
||||
register_shutdown_function(function () {
|
||||
Miniflux\Helper\write_debug_file();
|
||||
});
|
||||
|
||||
// Route handler
|
||||
function route($name, Closure $callback = null)
|
||||
@ -31,37 +32,27 @@ function response(array $response)
|
||||
// Fever authentication
|
||||
function auth()
|
||||
{
|
||||
if (! empty($_GET['database'])) {
|
||||
// Return unauthorized if the requested database could not be found
|
||||
if (! Model\Database\select($_GET['database'])) {
|
||||
return array(
|
||||
'api_version' => 3,
|
||||
'auth' => 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$credentials = Database::getInstance('db')->hashtable('settings')->get('username', 'fever_token');
|
||||
$api_key = md5($credentials['username'].':'.$credentials['fever_token']);
|
||||
$api_key = isset($_POST['api_key']) && ctype_alnum($_POST['api_key']) ? $_POST['api_key'] : null;
|
||||
$user = Model\User\get_user_by_token('fever_api_key', $api_key);
|
||||
$authenticated = $user !== null;
|
||||
|
||||
$response = array(
|
||||
'api_version' => 3,
|
||||
'auth' => (int) (isset($_POST['api_key']) && (strcasecmp($_POST['api_key'], $api_key) === 0)),
|
||||
'auth' => (int) $authenticated,
|
||||
'last_refreshed_on_time' => time(),
|
||||
);
|
||||
|
||||
return $response;
|
||||
return array($user, $authenticated, $response);
|
||||
}
|
||||
|
||||
// Call: ?api&groups
|
||||
route('groups', function () {
|
||||
list($user, $authenticated, $response) = auth();
|
||||
|
||||
$response = auth();
|
||||
|
||||
if ($response['auth']) {
|
||||
$response['groups'] = Group\get_all();
|
||||
if ($authenticated) {
|
||||
$response['groups'] = Model\Group\get_all($user['id']);
|
||||
$response['feeds_groups'] = array();
|
||||
$group_map = Group\get_map();
|
||||
$group_map = Model\Group\get_groups_feed_ids($user['id']);
|
||||
|
||||
foreach ($group_map as $group_id => $feed_ids) {
|
||||
$response['feeds_groups'][] = array(
|
||||
@ -76,14 +67,13 @@ route('groups', function () {
|
||||
|
||||
// Call: ?api&feeds
|
||||
route('feeds', function () {
|
||||
list($user, $authenticated, $response) = auth();
|
||||
|
||||
$response = auth();
|
||||
|
||||
if ($response['auth']) {
|
||||
if ($authenticated) {
|
||||
$response['feeds'] = array();
|
||||
$response['feeds_groups'] = array();
|
||||
|
||||
$feeds = Feed\get_all();
|
||||
$feeds = Model\Feed\get_feeds($user['id']);
|
||||
|
||||
foreach ($feeds as $feed) {
|
||||
$response['feeds'][] = array(
|
||||
@ -97,7 +87,7 @@ route('feeds', function () {
|
||||
);
|
||||
}
|
||||
|
||||
$group_map = Group\get_map();
|
||||
$group_map = Model\Group\get_groups_feed_ids($user['id']);
|
||||
foreach ($group_map as $group_id => $feed_ids) {
|
||||
$response['feeds_groups'][] = array(
|
||||
'group_id' => $group_id,
|
||||
@ -111,24 +101,16 @@ route('feeds', function () {
|
||||
|
||||
// Call: ?api&favicons
|
||||
route('favicons', function () {
|
||||
list($user, $authenticated, $response) = auth();
|
||||
|
||||
$response = auth();
|
||||
|
||||
if ($response['auth']) {
|
||||
$favicons = Database::getInstance('db')
|
||||
->table('favicons')
|
||||
->columns(
|
||||
'feed_id',
|
||||
'file',
|
||||
'type'
|
||||
)
|
||||
->findAll();
|
||||
|
||||
if ($authenticated) {
|
||||
$favicons = Model\Favicon\get_favicons_with_data_url($user['id']);
|
||||
$response['favicons'] = array();
|
||||
|
||||
foreach ($favicons as $favicon) {
|
||||
$response['favicons'][] = array(
|
||||
'id' => (int) $favicon['feed_id'],
|
||||
'data' => 'data:'.$favicon['type'].';base64,'.base64_encode(file_get_contents(FAVICON_DIRECTORY.DIRECTORY_SEPARATOR.$favicon['file']))
|
||||
'data' => $favicon['url'],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -138,39 +120,17 @@ route('favicons', function () {
|
||||
|
||||
// Call: ?api&items
|
||||
route('items', function () {
|
||||
list($user, $authenticated, $response) = auth();
|
||||
|
||||
$response = auth();
|
||||
|
||||
if ($response['auth']) {
|
||||
$query = Database::getInstance('db')
|
||||
->table('items')
|
||||
->columns(
|
||||
'rowid',
|
||||
'feed_id',
|
||||
'title',
|
||||
'author',
|
||||
'content',
|
||||
'url',
|
||||
'updated',
|
||||
'status',
|
||||
'bookmark'
|
||||
)
|
||||
->limit(50)
|
||||
->neq('status', 'removed');
|
||||
|
||||
if (isset($_GET['since_id']) && is_numeric($_GET['since_id'])) {
|
||||
$items = $query->gt('rowid', $_GET['since_id'])
|
||||
->asc('rowid');
|
||||
} elseif (! empty($_GET['with_ids'])) {
|
||||
$query->in('rowid', explode(',', $_GET['with_ids']));
|
||||
}
|
||||
|
||||
$items = $query->findAll();
|
||||
if ($authenticated) {
|
||||
$since_id = isset($_GET['since_id']) && ctype_digit($_GET['since_id']) ? $_GET['since_id'] : null;
|
||||
$item_ids = ! empty($_GET['with_ids']) ? explode(',', $_GET['with_ids']) : array();
|
||||
$items = Model\Item\get_items($user['id'], $since_id, $item_ids);
|
||||
$response['items'] = array();
|
||||
|
||||
foreach ($items as $item) {
|
||||
$response['items'][] = array(
|
||||
'id' => (int) $item['rowid'],
|
||||
'id' => (int) $item['id'],
|
||||
'feed_id' => (int) $item['feed_id'],
|
||||
'title' => $item['title'],
|
||||
'author' => $item['author'],
|
||||
@ -182,10 +142,10 @@ route('items', function () {
|
||||
);
|
||||
}
|
||||
|
||||
$response['total_items'] = Database::getInstance('db')
|
||||
->table('items')
|
||||
->neq('status', 'removed')
|
||||
->count();
|
||||
$response['total_items'] = Model\Item\count_by_status(
|
||||
$user['id'],
|
||||
array(Model\Item\STATUS_READ, Model\Item\STATUS_UNREAD)
|
||||
);
|
||||
}
|
||||
|
||||
response($response);
|
||||
@ -193,10 +153,9 @@ route('items', function () {
|
||||
|
||||
// Call: ?api&links
|
||||
route('links', function () {
|
||||
list(, $authenticated, $response) = auth();
|
||||
|
||||
$response = auth();
|
||||
|
||||
if ($response['auth']) {
|
||||
if ($authenticated) {
|
||||
$response['links'] = array();
|
||||
}
|
||||
|
||||
@ -205,15 +164,10 @@ route('links', function () {
|
||||
|
||||
// Call: ?api&unread_item_ids
|
||||
route('unread_item_ids', function () {
|
||||
list($user, $authenticated, $response) = auth();
|
||||
|
||||
$response = auth();
|
||||
|
||||
if ($response['auth']) {
|
||||
$item_ids = Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('status', 'unread')
|
||||
->findAllByColumn('rowid');
|
||||
|
||||
if ($authenticated) {
|
||||
$item_ids = Model\Item\get_item_ids_by_status($user['id'], Model\Item\STATUS_UNREAD);
|
||||
$response['unread_item_ids'] = implode(',', $item_ids);
|
||||
}
|
||||
|
||||
@ -222,15 +176,10 @@ route('unread_item_ids', function () {
|
||||
|
||||
// Call: ?api&saved_item_ids
|
||||
route('saved_item_ids', function () {
|
||||
list($user, $authenticated, $response) = auth();
|
||||
|
||||
$response = auth();
|
||||
|
||||
if ($response['auth']) {
|
||||
$item_ids = Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('bookmark', 1)
|
||||
->findAllByColumn('rowid');
|
||||
|
||||
if ($authenticated) {
|
||||
$item_ids = Model\Bookmark\get_bookmarked_item_ids($user['id']);
|
||||
$response['saved_item_ids'] = implode(',', $item_ids);
|
||||
}
|
||||
|
||||
@ -239,30 +188,20 @@ route('saved_item_ids', function () {
|
||||
|
||||
// handle write items
|
||||
route('write_items', function () {
|
||||
list($user, $authenticated, $response) = auth();
|
||||
|
||||
$response = auth();
|
||||
|
||||
if ($response['auth']) {
|
||||
$query = Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('rowid', $_POST['id']);
|
||||
if ($authenticated && ctype_digit($_POST['id'])) {
|
||||
$item_id = $_POST['id'];
|
||||
|
||||
if ($_POST['as'] === 'saved') {
|
||||
$query->update(array('bookmark' => 1));
|
||||
|
||||
// Send bookmark to third-party services if enabled
|
||||
$item_id = Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('rowid', $_POST['id'])
|
||||
->findOneColumn('id');
|
||||
|
||||
Handler\Service\sync($item_id);
|
||||
Model\Bookmark\set_flag($user['id'], $item_id, 1);
|
||||
Handler\Service\sync($user['id'], $item_id);
|
||||
} elseif ($_POST['as'] === 'unsaved') {
|
||||
$query->update(array('bookmark' => 0));
|
||||
Model\Bookmark\set_flag($user['id'], $item_id, 0);
|
||||
} elseif ($_POST['as'] === 'read') {
|
||||
$query->update(array('status' => 'read'));
|
||||
Model\Item\change_item_status($user['id'], $item_id, Model\Item\STATUS_READ);
|
||||
} elseif ($_POST['as'] === 'unread') {
|
||||
$query->update(array('status' => 'unread'));
|
||||
Model\Item\change_item_status($user['id'], $item_id, Model\Item\STATUS_UNREAD);
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,15 +210,16 @@ route('write_items', function () {
|
||||
|
||||
// handle write feeds
|
||||
route('write_feeds', function () {
|
||||
list($user, $authenticated, $response) = auth();
|
||||
|
||||
$response = auth();
|
||||
|
||||
if ($response['auth']) {
|
||||
Database::getInstance('db')
|
||||
->table('items')
|
||||
->eq('feed_id', $_POST['id'])
|
||||
->lte('updated', $_POST['before'])
|
||||
->update(array('status' => 'read'));
|
||||
if ($authenticated && ctype_digit($_POST['id']) && ctype_digit($_POST['before'])) {
|
||||
Model\ItemFeed\change_items_status(
|
||||
$user['id'],
|
||||
$_POST['id'],
|
||||
Model\Item\STATUS_UNREAD,
|
||||
Model\Item\STATUS_READ,
|
||||
$_POST['before']
|
||||
);
|
||||
}
|
||||
|
||||
response($response);
|
||||
@ -287,19 +227,16 @@ route('write_feeds', function () {
|
||||
|
||||
// handle write groups
|
||||
route('write_groups', function () {
|
||||
list($user, $authenticated, $response) = auth();
|
||||
|
||||
$response = auth();
|
||||
|
||||
if ($response['auth']) {
|
||||
$db = Database::getInstance('db')
|
||||
->table('items')
|
||||
->lte('updated', $_POST['before']);
|
||||
|
||||
if ($_POST['id'] > 0) {
|
||||
$db->in('feed_id', Model\Group\get_feeds_by_group($_POST['id']));
|
||||
}
|
||||
|
||||
$db->update(array('status' => 'read'));
|
||||
if ($authenticated && ctype_digit($_POST['id']) && ctype_digit($_POST['before'])) {
|
||||
Model\ItemGroup\change_items_status(
|
||||
$user['id'],
|
||||
$_POST['id'],
|
||||
Model\Item\STATUS_UNREAD,
|
||||
Model\Item\STATUS_READ,
|
||||
$_POST['before']
|
||||
);
|
||||
}
|
||||
|
||||
response($response);
|
||||
@ -320,4 +257,5 @@ if (! empty($_POST['mark']) && ! empty($_POST['as'])
|
||||
}
|
||||
}
|
||||
|
||||
response(auth());
|
||||
list(, , $response) = auth();
|
||||
response($response);
|
||||
|
22
index.php
22
index.php
@ -5,17 +5,27 @@ require __DIR__.'/app/common.php';
|
||||
use Miniflux\Router;
|
||||
use Miniflux\Response;
|
||||
|
||||
register_shutdown_function(function () {
|
||||
Miniflux\Helper\write_debug_file();
|
||||
});
|
||||
|
||||
Router\bootstrap(
|
||||
__DIR__.'/app/controllers',
|
||||
'common',
|
||||
'console',
|
||||
'user',
|
||||
'config',
|
||||
'item',
|
||||
'history',
|
||||
'about',
|
||||
'api',
|
||||
'auth',
|
||||
'bookmark',
|
||||
'config',
|
||||
'feed',
|
||||
'search'
|
||||
'help',
|
||||
'history',
|
||||
'item',
|
||||
'opml',
|
||||
'profile',
|
||||
'search',
|
||||
'services',
|
||||
'users'
|
||||
);
|
||||
|
||||
Router\notfound(function() {
|
||||
|
250
jsonrpc.php
250
jsonrpc.php
@ -2,229 +2,129 @@
|
||||
|
||||
require __DIR__.'/app/common.php';
|
||||
|
||||
use JsonRPC\Exception\AuthenticationFailureException;
|
||||
use JsonRPC\MiddlewareInterface;
|
||||
use JsonRPC\Server;
|
||||
use Miniflux\Handler;
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
|
||||
class AuthMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function execute($username, $password, $procedureName)
|
||||
{
|
||||
$user = Model\User\get_user_by_token('api_token', $password);
|
||||
if (empty($user)) {
|
||||
throw new AuthenticationFailureException('Wrong credentials!');
|
||||
}
|
||||
|
||||
SessionStorage::getInstance()->setUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
$server = new Server();
|
||||
|
||||
$server->authentication(array(
|
||||
Model\Config\get('username') => Model\Config\get('api_token')
|
||||
));
|
||||
|
||||
$server->getMiddlewareHandler()->withMiddleware(new AuthMiddleware());
|
||||
$procedureHandler = $server->getProcedureHandler();
|
||||
|
||||
// Get version
|
||||
$procedureHandler->withCallback('app.version', function () {
|
||||
$procedureHandler->withCallback('getVersion', function () {
|
||||
return array('version' => APP_VERSION);
|
||||
});
|
||||
|
||||
// Get all feeds
|
||||
$procedureHandler->withCallback('feed.list', function () {
|
||||
$feeds = Model\Feed\get_all();
|
||||
if (empty($feeds)) {
|
||||
return array();
|
||||
}
|
||||
$procedureHandler->withCallback('getFeeds', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
$feeds = Model\Feed\get_feeds($user_id);
|
||||
|
||||
$groups = Model\Group\get_feeds_map();
|
||||
foreach ($feeds as &$feed) {
|
||||
$feed_id = $feed['id'];
|
||||
$feed['feed_group_ids'] = array();
|
||||
if (isset($groups[$feed_id])) {
|
||||
$feed['feed_group_ids'] = $groups[$feed_id];
|
||||
}
|
||||
$feed['groups'] = Model\Group\get_feed_groups($feed['id']);
|
||||
}
|
||||
|
||||
return $feeds;
|
||||
});
|
||||
|
||||
// Get one feed
|
||||
$procedureHandler->withCallback('feed.info', function ($feed_id) {
|
||||
$result = Model\Feed\get($feed_id);
|
||||
$result['feed_group_ids'] = Model\Group\get_feed_group_ids($feed_id);
|
||||
return $result;
|
||||
$procedureHandler->withCallback('getFeed', function ($feed_id) {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Model\Feed\get_feed($user_id, $feed_id);
|
||||
});
|
||||
|
||||
// Add a new feed
|
||||
$procedureHandler->withCallback('feed.create', function($url) {
|
||||
try {
|
||||
$result = Model\Feed\create($url);
|
||||
} catch (Exception $e) {
|
||||
$result = false;
|
||||
$procedureHandler->withCallback('createFeed', function ($url) {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
list($feed_id,) = Handler\Feed\create_feed($user_id, $url);
|
||||
|
||||
if ($feed_id > 0) {
|
||||
return $feed_id;
|
||||
}
|
||||
|
||||
Model\Config\write_debug();
|
||||
|
||||
return $result;
|
||||
return false;
|
||||
});
|
||||
|
||||
// Delete a feed
|
||||
$procedureHandler->withCallback('feed.delete', function($feed_id) {
|
||||
return Model\Feed\remove($feed_id);
|
||||
$procedureHandler->withCallback('deleteFeed', function ($feed_id) {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Model\Feed\remove_feed($user_id, $feed_id);
|
||||
});
|
||||
|
||||
// Delete all feeds
|
||||
$procedureHandler->withCallback('feed.delete_all', function() {
|
||||
return Model\Feed\remove_all();
|
||||
// Refresh a feed
|
||||
$procedureHandler->withCallback('refreshFeed', function ($feed_id) {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Handler\Feed\update_feed($user_id, $feed_id);
|
||||
});
|
||||
|
||||
// Enable a feed
|
||||
$procedureHandler->withCallback('feed.enable', function($feed_id) {
|
||||
return Model\Feed\enable($feed_id);
|
||||
});
|
||||
|
||||
// Disable a feed
|
||||
$procedureHandler->withCallback('feed.disable', function($feed_id) {
|
||||
return Model\Feed\disable($feed_id);
|
||||
});
|
||||
|
||||
// Update a feed
|
||||
$procedureHandler->withCallback('feed.update', function($feed_id) {
|
||||
return Model\Feed\refresh($feed_id);
|
||||
});
|
||||
|
||||
// Get all groups
|
||||
$procedureHandler->withCallback('group.list', function () {
|
||||
return Model\Group\get_all();
|
||||
});
|
||||
|
||||
// Add a new group
|
||||
$procedureHandler->withCallback('group.create', function($title) {
|
||||
return Model\Group\create($title);
|
||||
});
|
||||
|
||||
// Get assoc array of group ids with assigned feeds ids
|
||||
$procedureHandler->withCallback('group.map', function() {
|
||||
return Model\Group\get_map();
|
||||
});
|
||||
|
||||
// Get the id of a group
|
||||
$procedureHandler->withCallback('group.id', function($title) {
|
||||
return Model\Group\get_group_id($title);
|
||||
});
|
||||
|
||||
// Get all feed ids assigned to a group
|
||||
$procedureHandler->withCallback('group.feeds', function($group_id) {
|
||||
return Model\Group\get_feeds_by_group($group_id);
|
||||
});
|
||||
|
||||
// Add groups to feed
|
||||
$procedureHandler->withCallback('group.add', function($feed_id, $group_ids) {
|
||||
return Model\Group\add($feed_id, $group_ids);
|
||||
});
|
||||
|
||||
// Remove groups from feed
|
||||
$procedureHandler->withCallback('group.remove', function($feed_id, $group_ids) {
|
||||
return Model\Group\remove($feed_id, $group_ids);
|
||||
});
|
||||
|
||||
// Remove all groups from feed
|
||||
$procedureHandler->withCallback('group.remove_all', function($feed_id) {
|
||||
return Model\Group\remove_all($feed_id);
|
||||
});
|
||||
|
||||
// Update feed group associations
|
||||
$procedureHandler->withCallback('group.update_feed_groups', function($feed_id, $group_ids, $create_group = '') {
|
||||
return Model\Group\update_feed_groups($feed_id, $group_ids, $create_group);
|
||||
});
|
||||
|
||||
// Get all items for a specific feed
|
||||
$procedureHandler->withCallback('item.feed.list', function ($feed_id, $offset = null, $limit = null) {
|
||||
return Model\ItemFeed\get_all_items($feed_id, $offset, $limit);
|
||||
});
|
||||
|
||||
// Count all feed items
|
||||
$procedureHandler->withCallback('item.feed.count', function ($feed_id) {
|
||||
return Model\ItemFeed\count_items($feed_id);
|
||||
});
|
||||
|
||||
// Get all bookmark items
|
||||
$procedureHandler->withCallback('item.bookmark.list', function ($offset = null, $limit = null) {
|
||||
return Model\Bookmark\get_all_items($offset, $limit);
|
||||
});
|
||||
|
||||
// Count bookmarks
|
||||
$procedureHandler->withCallback('item.bookmark.count', function () {
|
||||
return Model\Bookmark\count_items();
|
||||
});
|
||||
|
||||
// Add a bookmark
|
||||
$procedureHandler->withCallback('item.bookmark.create', function ($item_id) {
|
||||
return Model\Bookmark\set_flag($item_id, 1);
|
||||
});
|
||||
|
||||
// Remove a bookmark
|
||||
$procedureHandler->withCallback('item.bookmark.delete', function ($item_id) {
|
||||
return Model\Bookmark\set_flag($item_id, 0);
|
||||
});
|
||||
|
||||
// Get all unread items
|
||||
$procedureHandler->withCallback('item.list_unread', function ($offset = null, $limit = null) {
|
||||
return Model\Item\get_all_by_status('unread', array(), $offset, $limit);
|
||||
});
|
||||
|
||||
// Count all unread items
|
||||
$procedureHandler->withCallback('item.count_unread', function () {
|
||||
return Model\Item\count_by_status('unread');
|
||||
});
|
||||
|
||||
// Get all read items
|
||||
$procedureHandler->withCallback('item.list_read', function ($offset = null, $limit = null) {
|
||||
return Model\Item\get_all_by_status('read', array(), $offset, $limit);
|
||||
});
|
||||
|
||||
// Count all read items
|
||||
$procedureHandler->withCallback('item.count_read', function () {
|
||||
return Model\Item\count_by_status('read');
|
||||
// Get all items
|
||||
$procedureHandler->withCallback('getItems', function ($since_id = null, array $item_ids = array(), $offset = 50) {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Model\Item\get_items($user_id, $since_id, $item_ids, $offset);
|
||||
});
|
||||
|
||||
// Get one item
|
||||
$procedureHandler->withCallback('item.info', function ($item_id) {
|
||||
return Model\Item\get($item_id);
|
||||
$procedureHandler->withCallback('getItem', function ($item_id) {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Model\Item\get_item($user_id, $item_id);
|
||||
});
|
||||
|
||||
// Delete an item
|
||||
$procedureHandler->withCallback('item.delete', function($item_id) {
|
||||
return Model\Item\set_removed($item_id);
|
||||
// Change items status
|
||||
$procedureHandler->withCallback('changeItemsStatus', function (array $item_ids, $status) {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Model\Item\change_item_ids_status($user_id, $item_ids, $status);
|
||||
});
|
||||
|
||||
// Mark item as read
|
||||
$procedureHandler->withCallback('item.mark_as_read', function($item_id) {
|
||||
return Model\Item\set_read($item_id);
|
||||
// Add a bookmark
|
||||
$procedureHandler->withCallback('addBookmark', function ($item_id) {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Model\Bookmark\set_flag($user_id, $item_id, 1);
|
||||
});
|
||||
|
||||
// Mark item as unread
|
||||
$procedureHandler->withCallback('item.mark_as_unread', function($item_id) {
|
||||
return Model\Item\set_unread($item_id);
|
||||
// Remove a bookmark
|
||||
$procedureHandler->withCallback('removeBookmark', function ($item_id) {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Model\Bookmark\set_flag($user_id, $item_id, 0);
|
||||
});
|
||||
|
||||
// Change the status of list of items
|
||||
$procedureHandler->withCallback('item.set_list_status', function($status, array $items) {
|
||||
return Model\Item\set_status($status, $items);
|
||||
// Get all groups
|
||||
$procedureHandler->withCallback('getGroups', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Model\Group\get_all($user_id);
|
||||
});
|
||||
|
||||
// Flush all read items
|
||||
$procedureHandler->withCallback('item.flush', function() {
|
||||
return Model\Item\mark_all_as_removed();
|
||||
// Add a new group
|
||||
$procedureHandler->withCallback('createGroup', function ($title) {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Model\Group\create_group($user_id, $title);
|
||||
});
|
||||
|
||||
// Mark all unread items as read
|
||||
$procedureHandler->withCallback('item.mark_all_as_read', function() {
|
||||
return Model\Item\mark_all_as_read();
|
||||
// Add/Update groups for a feed
|
||||
$procedureHandler->withCallback('setFeedGroups', function ($feed_id, $group_ids) {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Model\Group\update_feed_groups($user_id, $feed_id, $group_ids);
|
||||
});
|
||||
|
||||
// Get all items with the content
|
||||
$procedureHandler->withCallback('item.get_all', function() {
|
||||
return Model\Item\get_all();
|
||||
});
|
||||
|
||||
// Get all items since a date
|
||||
$procedureHandler->withCallback('item.get_all_since', function($timestamp) {
|
||||
return Model\Item\get_all_since($timestamp);
|
||||
});
|
||||
|
||||
// Get all items id and status
|
||||
$procedureHandler->withCallback('item.get_all_status', function() {
|
||||
return Model\Item\get_all_status();
|
||||
// Get favicons
|
||||
$procedureHandler->withCallback('getFavicons', function () {
|
||||
$user_id = SessionStorage::getInstance()->getUserId();
|
||||
return Model\Favicon\get_favicons_with_data_url($user_id);
|
||||
});
|
||||
|
||||
echo $server->execute();
|
||||
|
14
producer.php
14
producer.php
@ -13,11 +13,19 @@ $options = getopt('', array(
|
||||
'limit::',
|
||||
));
|
||||
|
||||
$limit = ! empty($options['limit']) && ctype_digit($options['limit']) ? (int) $options['limit'] : Model\Feed\LIMIT_ALL;
|
||||
$limit = get_cli_option('limit', $options);
|
||||
$connection = new Pheanstalk(BEANSTALKD_HOST);
|
||||
|
||||
foreach (Model\Feed\get_ids($limit) as $feed_id) {
|
||||
foreach (Model\User\get_all_users() as $user) {
|
||||
foreach (Model\Feed\get_feed_ids($user['id'], $limit) as $feed_id) {
|
||||
$payload = serialize(array(
|
||||
'feed_id' => $feed_id,
|
||||
'user_id' => $user['id'],
|
||||
));
|
||||
|
||||
$connection
|
||||
->useTube(BEANSTALKD_QUEUE)
|
||||
->put($feed_id, Pheanstalk::DEFAULT_PRIORITY, Pheanstalk::DEFAULT_DELAY, BEANSTALKD_TTL);
|
||||
->put($payload, Pheanstalk::DEFAULT_PRIORITY, Pheanstalk::DEFAULT_DELAY, BEANSTALKD_TTL);
|
||||
}
|
||||
}
|
||||
|
||||
|
245
scripts/migrate-db.php
Normal file
245
scripts/migrate-db.php
Normal file
@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/../app/common.php';
|
||||
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model;
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
die('This script can run only from the command line.'.PHP_EOL);
|
||||
}
|
||||
|
||||
$options = getopt('', array(
|
||||
'sqlite-db:',
|
||||
'admin::',
|
||||
));
|
||||
|
||||
if (empty($options)) {
|
||||
die('Usage: '.$argv[0].' --sqlite-db=/path/to/my/db.sqlite --admin=1|0'.PHP_EOL);
|
||||
}
|
||||
|
||||
$src_file = $options['sqlite-db'];
|
||||
$is_admin = isset($options['admin']) ? (int) $options['admin'] : 0;
|
||||
|
||||
$src = new PDO('sqlite:' . $src_file);
|
||||
$dst = PicoDb\Database::getInstance('db')->getConnection();
|
||||
|
||||
|
||||
function get_settings(PDO $db)
|
||||
{
|
||||
$rq = $db->prepare('SELECT * FROM settings');
|
||||
$rq->execute();
|
||||
$rows = $rq->fetchAll(PDO::FETCH_ASSOC);
|
||||
$settings = array();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$settings[$row['key']] = $row['value'];
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
function get_table(PDO $db, $table)
|
||||
{
|
||||
$rq = $db->prepare('SELECT * FROM '.$table);
|
||||
$rq->execute();
|
||||
return $rq->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
function create_user(PDO $db, array $settings, $is_admin)
|
||||
{
|
||||
$rq = $db->prepare('
|
||||
INSERT INTO users
|
||||
(username, password, is_admin, last_login, api_token, bookmarklet_token, cronjob_token, feed_token, fever_token, fever_api_key)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
');
|
||||
|
||||
$rq->execute(array(
|
||||
$settings['username'],
|
||||
$settings['password'],
|
||||
$is_admin,
|
||||
$settings['last_login'],
|
||||
$settings['api_token'],
|
||||
$settings['bookmarklet_token'],
|
||||
Helper\generate_token(),
|
||||
$settings['feed_token'],
|
||||
$settings['fever_token'],
|
||||
md5($settings['username'] . ':' . $settings['fever_token']),
|
||||
));
|
||||
|
||||
return $db->lastInsertId();
|
||||
}
|
||||
|
||||
function copy_settings(PDO $db, $user_id, array $settings)
|
||||
{
|
||||
$exclude_keys = array(
|
||||
'username',
|
||||
'password',
|
||||
'last_login',
|
||||
'api_token',
|
||||
'bookmarklet_token',
|
||||
'feed_token',
|
||||
'fever_token',
|
||||
'debug_mode',
|
||||
);
|
||||
|
||||
$rq = $db->prepare('INSERT INTO user_settings ("user_id", "key", "value") VALUES (?, ?, ?)');
|
||||
|
||||
foreach ($settings as $key => $value) {
|
||||
if (! in_array($key, $exclude_keys)) {
|
||||
$rq->execute(array($user_id, $key, $value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function copy_feeds(PDO $db, $user_id, array $feeds)
|
||||
{
|
||||
$feed_ids = array();
|
||||
$rq = $db->prepare('INSERT INTO feeds
|
||||
(user_id, feed_url, site_url, title, enabled, download_content, rtl, cloak_referrer)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?, ?)
|
||||
');
|
||||
|
||||
foreach ($feeds as $feed) {
|
||||
$rq->execute(array(
|
||||
$user_id,
|
||||
$feed['feed_url'],
|
||||
$feed['site_url'],
|
||||
$feed['title'],
|
||||
$feed['enabled'],
|
||||
$feed['download_content'],
|
||||
$feed['rtl'],
|
||||
$feed['cloak_referrer'],
|
||||
));
|
||||
|
||||
$feed_ids[$feed['id']] = $db->lastInsertId();
|
||||
}
|
||||
|
||||
return $feed_ids;
|
||||
}
|
||||
|
||||
function copy_items(PDO $db, $user_id, array $feed_ids, array $items)
|
||||
{
|
||||
$rq = $db->prepare('INSERT INTO items
|
||||
(user_id, feed_id, checksum, status, bookmark, url, title, author, content, updated, enclosure_url, enclosure_type, language)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
');
|
||||
|
||||
foreach ($items as $item) {
|
||||
$rq->execute(array(
|
||||
$user_id,
|
||||
$feed_ids[$item['feed_id']],
|
||||
$item['id'],
|
||||
$item['status'],
|
||||
$item['bookmark'],
|
||||
$item['url'],
|
||||
$item['title'],
|
||||
$item['author'],
|
||||
$item['content'],
|
||||
$item['updated'],
|
||||
$item['enclosure'],
|
||||
$item['enclosure_type'],
|
||||
$item['language'],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function copy_favicons(PDO $db, array $feed_ids, array $favicons, array $favicons_feeds)
|
||||
{
|
||||
$favicon_ids = array();
|
||||
|
||||
foreach ($favicons as $favicon) {
|
||||
$rq = $db->prepare('SELECT id from favicons WHERE "hash"=?');
|
||||
$rq->execute(array($favicon['hash']));
|
||||
$favicon_id = $rq->fetch(PDO::FETCH_COLUMN);
|
||||
|
||||
if ($favicon_id) {
|
||||
$favicon_ids[$favicon['id']] = $favicon_id;
|
||||
} else {
|
||||
$rq = $db->prepare('INSERT INTO favicons
|
||||
(hash, type)
|
||||
VALUES
|
||||
(?, ?)
|
||||
');
|
||||
|
||||
$rq->execute(array(
|
||||
$favicon['hash'],
|
||||
$favicon['type'],
|
||||
));
|
||||
|
||||
$favicon_ids[$favicon['id']] = $db->lastInsertId();
|
||||
}
|
||||
}
|
||||
|
||||
$rq = $db->prepare('INSERT INTO favicons_feeds
|
||||
(feed_id, favicon_id)
|
||||
VALUES
|
||||
(?, ?)
|
||||
');
|
||||
|
||||
foreach ($favicons_feeds as $row) {
|
||||
$rq->execute(array(
|
||||
$feed_ids[$row['feed_id']],
|
||||
$favicon_ids[$row['favicon_id']],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function copy_groups(PDO $db, $user_id, array $feed_ids, array $groups, array $feeds_groups)
|
||||
{
|
||||
$group_ids = array();
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$rq = $db->prepare('INSERT INTO groups
|
||||
(user_id, title)
|
||||
VALUES
|
||||
(?, ?)
|
||||
');
|
||||
|
||||
$rq->execute(array(
|
||||
$user_id,
|
||||
$group['title'],
|
||||
));
|
||||
|
||||
$group_ids[$group['id']] = $db->lastInsertId();
|
||||
}
|
||||
|
||||
$rq = $db->prepare('INSERT INTO feeds_groups
|
||||
(feed_id, group_id)
|
||||
VALUES
|
||||
(?, ?)
|
||||
');
|
||||
|
||||
foreach ($feeds_groups as $row) {
|
||||
$rq->execute(array(
|
||||
$feed_ids[$row['feed_id']],
|
||||
$group_ids[$row['group_id']],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$settings = get_settings($src);
|
||||
$feeds = get_table($src, 'feeds');
|
||||
$items = get_table($src, 'items');
|
||||
$groups = get_table($src, 'groups');
|
||||
$feeds_groups = get_table($src, 'feeds_groups');
|
||||
$favicons = get_table($src, 'favicons');
|
||||
$favicons_feeds = get_table($src, 'favicons_feeds');
|
||||
|
||||
try {
|
||||
$dst->beginTransaction();
|
||||
|
||||
$user_id = create_user($dst, $settings, $is_admin);
|
||||
copy_settings($dst, $user_id, $settings);
|
||||
$feed_ids = copy_feeds($dst, $user_id, $feeds);
|
||||
copy_items($dst, $user_id, $feed_ids, $items);
|
||||
copy_favicons($dst, $feed_ids, $favicons, $favicons_feeds);
|
||||
copy_groups($dst, $user_id, $feed_ids, $groups, $feeds_groups);
|
||||
|
||||
$dst->commit();
|
||||
} catch (PDOException $e) {
|
||||
$dst->rollBack();
|
||||
echo $e->getMessage().PHP_EOL;
|
||||
}
|
@ -1,10 +1,79 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/../../vendor/autoload.php';
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
use PicoDb\Database;
|
||||
use PicoFeed\Parser\Feed;
|
||||
use PicoFeed\Parser\Item;
|
||||
|
||||
require_once __DIR__.'/../../app/common.php';
|
||||
|
||||
abstract class BaseTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
SessionStorage::getInstance()->flush();
|
||||
|
||||
PicoDb\Database::setInstance('db', function () {
|
||||
$db = new PicoDb\Database(array(
|
||||
'driver' => 'sqlite',
|
||||
'filename' => DB_FILENAME,
|
||||
));
|
||||
|
||||
$db->getStatementHandler()->withLogging();
|
||||
if (! $db->schema('\Miniflux\Schema')->check(Miniflux\Schema\VERSION)) {
|
||||
var_dump($db->getLogMessages());
|
||||
}
|
||||
return $db;
|
||||
});
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
Database::getInstance('db')->closeConnection();
|
||||
}
|
||||
|
||||
public function buildItem($itemId)
|
||||
{
|
||||
$item = new Item();
|
||||
$item->setId($itemId);
|
||||
$item->setTitle('Item #1');
|
||||
$item->setUrl('some url');
|
||||
$item->setContent('some content');
|
||||
$item->setDate(new DateTime());
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function buildFeed()
|
||||
{
|
||||
$items = array();
|
||||
|
||||
$item = new Item();
|
||||
$item->setId('ID 1');
|
||||
$item->setTitle('Item #1');
|
||||
$item->setUrl('some url');
|
||||
$item->setContent('some content');
|
||||
$item->setDate(new DateTime());
|
||||
$items[] = $item;
|
||||
|
||||
$item = new Item();
|
||||
$item->setId('ID 2');
|
||||
$item->setTitle('Item #2');
|
||||
$item->setUrl('some url');
|
||||
$item->setDate(new DateTime());
|
||||
$items[] = $item;
|
||||
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('My feed');
|
||||
$feed->setFeedUrl('feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
$feed->setItems($items);
|
||||
|
||||
return $feed;
|
||||
}
|
||||
|
||||
public function assertCreateFeed(Feed $feed)
|
||||
{
|
||||
$this->assertEquals(1, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
}
|
||||
}
|
||||
|
38
tests/unit/BookmarkModelTest.php
Normal file
38
tests/unit/BookmarkModelTest.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Miniflux\Model;
|
||||
|
||||
require_once __DIR__.'/BaseTest.php';
|
||||
|
||||
class BookmarkModelTest extends BaseTest
|
||||
{
|
||||
public function testSetBookmark()
|
||||
{
|
||||
$this->assertCreateFeed($this->buildFeed());
|
||||
|
||||
$this->assertTrue(Model\Bookmark\set_flag(1, 1, 1));
|
||||
$item = Model\Item\get_item(1, 1);
|
||||
$this->assertEquals(1, $item['bookmark']);
|
||||
|
||||
$this->assertTrue(Model\Bookmark\set_flag(1, 1, 0));
|
||||
$item = Model\Item\get_item(1, 1);
|
||||
$this->assertEquals(0, $item['bookmark']);
|
||||
}
|
||||
|
||||
public function testCountBookmarkedItems()
|
||||
{
|
||||
$this->assertCreateFeed($this->buildFeed());
|
||||
$this->assertTrue(Model\Bookmark\set_flag(1, 1, 1));
|
||||
$this->assertEquals(1, Model\Bookmark\count_bookmarked_items(1));
|
||||
$this->assertEquals(0, Model\Bookmark\count_bookmarked_items(1, array(2)));
|
||||
}
|
||||
|
||||
public function testGetBookmarkedItems()
|
||||
{
|
||||
$this->assertCreateFeed($this->buildFeed());
|
||||
$this->assertTrue(Model\Bookmark\set_flag(1, 1, 1));
|
||||
|
||||
$items = Model\Bookmark\get_bookmarked_items(1);
|
||||
$this->assertCount(1, $items);
|
||||
}
|
||||
}
|
25
tests/unit/ConfigModelTest.php
Normal file
25
tests/unit/ConfigModelTest.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Miniflux\Model;
|
||||
|
||||
require_once __DIR__.'/BaseTest.php';
|
||||
|
||||
class ConfigModelTest extends BaseTest
|
||||
{
|
||||
public function testGetAllAndSave()
|
||||
{
|
||||
$settings = Model\Config\get_all(1);
|
||||
$this->assertNotEmpty($settings);
|
||||
$this->assertArrayHasKey('pinboard_enabled', $settings);
|
||||
|
||||
$this->assertTrue(Model\Config\save(1, array('foobar' => 'something')));
|
||||
|
||||
$settings = Model\Config\get_all(1);
|
||||
$this->assertEquals('something', $settings['foobar']);
|
||||
|
||||
$this->assertTrue(Model\Config\save(1, array('foobar' => 'something else')));
|
||||
|
||||
$settings = Model\Config\get_all(1);
|
||||
$this->assertEquals('something else', $settings['foobar']);
|
||||
}
|
||||
}
|
162
tests/unit/FeedModelTest.php
Normal file
162
tests/unit/FeedModelTest.php
Normal file
@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
use Miniflux\Model;
|
||||
use PicoFeed\Parser\Feed;
|
||||
use PicoFeed\Parser\Item;
|
||||
|
||||
require_once __DIR__.'/BaseTest.php';
|
||||
|
||||
class FeedModelTest extends BaseTest
|
||||
{
|
||||
public function testCreate()
|
||||
{
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('My feed');
|
||||
$feed->setFeedUrl('feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
|
||||
$this->assertEquals(1, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
$this->assertEquals(-1, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
|
||||
$subscription = Model\Feed\get_feed(1, 1);
|
||||
$this->assertNotEmpty($subscription);
|
||||
$this->assertEquals('1', $subscription['user_id']);
|
||||
$this->assertEquals('My feed', $subscription['title']);
|
||||
$this->assertEquals('site url', $subscription['site_url']);
|
||||
$this->assertEquals('feed url', $subscription['feed_url']);
|
||||
$this->assertEquals('etag', $subscription['etag']);
|
||||
$this->assertEquals('last modified', $subscription['last_modified']);
|
||||
$this->assertEquals('0', $subscription['download_content']);
|
||||
$this->assertEquals('0', $subscription['rtl']);
|
||||
$this->assertEquals('0', $subscription['cloak_referrer']);
|
||||
$this->assertEquals('1', $subscription['enabled']);
|
||||
}
|
||||
|
||||
public function testGetAll()
|
||||
{
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('My feed');
|
||||
$feed->setFeedUrl('feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
|
||||
$this->assertEquals(1, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('Some feed');
|
||||
$feed->setFeedUrl('another feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
$this->assertEquals(2, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
|
||||
$feeds = Model\Feed\get_feeds(1);
|
||||
$this->assertCount(2, $feeds);
|
||||
}
|
||||
|
||||
public function testGetFeedIds()
|
||||
{
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('My feed');
|
||||
$feed->setFeedUrl('feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
|
||||
$this->assertEquals(1, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('Some feed');
|
||||
$feed->setFeedUrl('another feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
$this->assertEquals(2, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
|
||||
$feed_ids = Model\Feed\get_feed_ids(1);
|
||||
$this->assertEquals(array(1, 2), $feed_ids);
|
||||
|
||||
$feed_ids = Model\Feed\get_feed_ids(1, 1);
|
||||
$this->assertEquals(array(1), $feed_ids);
|
||||
}
|
||||
|
||||
public function testGetFeedWithItemsCount()
|
||||
{
|
||||
$item = new Item();
|
||||
$item->setId('ID 1');
|
||||
$item->setTitle('Item #1');
|
||||
$item->setUrl('some url');
|
||||
$item->setDate(new DateTime());
|
||||
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('My feed');
|
||||
$feed->setFeedUrl('feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
$feed->setItems(array($item));
|
||||
|
||||
$this->assertEquals(1, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('Some feed');
|
||||
$feed->setFeedUrl('another feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
$this->assertEquals(2, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
|
||||
$feeds = Model\Feed\get_feeds_with_items_count(1);
|
||||
$this->assertCount(2, $feeds);
|
||||
|
||||
$this->assertEquals(1, $feeds[0]['items_unread']);
|
||||
$this->assertEquals(1, $feeds[0]['items_total']);
|
||||
|
||||
$this->assertEquals(0, $feeds[1]['items_unread']);
|
||||
$this->assertEquals(0, $feeds[1]['items_total']);
|
||||
}
|
||||
|
||||
public function testUpdate()
|
||||
{
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('My feed');
|
||||
$feed->setFeedUrl('feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
|
||||
$this->assertEquals(1, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
$this->assertTrue(Model\Feed\update_feed(1, 1, array('title' => 'new title')));
|
||||
|
||||
$subscription = Model\Feed\get_feed(1, 1);
|
||||
$this->assertNotEmpty($subscription);
|
||||
$this->assertEquals('1', $subscription['user_id']);
|
||||
$this->assertEquals('new title', $subscription['title']);
|
||||
}
|
||||
|
||||
public function testChangeStatus()
|
||||
{
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('My feed');
|
||||
$feed->setFeedUrl('feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
|
||||
$this->assertEquals(1, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
|
||||
$this->assertTrue(Model\Feed\change_feed_status(1, 1, Model\Feed\STATUS_INACTIVE));
|
||||
$subscription = Model\Feed\get_feed(1, 1);
|
||||
$this->assertEquals(0, $subscription['enabled']);
|
||||
}
|
||||
|
||||
public function testRemoveFeed()
|
||||
{
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('My feed');
|
||||
$feed->setFeedUrl('feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
|
||||
$this->assertEquals(1, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
$this->assertTrue(Model\Feed\remove_feed(1, 1));
|
||||
$this->assertNull(Model\Feed\get_feed(1, 1));
|
||||
}
|
||||
|
||||
public function testPeopleCanHaveSameFeed()
|
||||
{
|
||||
$feed = new Feed();
|
||||
$feed->setTitle('My feed');
|
||||
$feed->setFeedUrl('feed url');
|
||||
$feed->setSiteUrl('site url');
|
||||
|
||||
$this->assertEquals(2, Model\User\create_user('foobar', 'test'));
|
||||
|
||||
$this->assertEquals(1, Model\Feed\create(1, $feed, 'etag', 'last modified'));
|
||||
$this->assertEquals(2, Model\Feed\create(2, $feed, 'etag', 'last modified'));
|
||||
}
|
||||
}
|
76
tests/unit/GroupModelTest.php
Normal file
76
tests/unit/GroupModelTest.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
use Miniflux\Model;
|
||||
|
||||
require_once __DIR__.'/BaseTest.php';
|
||||
|
||||
class GroupModelTest extends BaseTest
|
||||
{
|
||||
public function testCreateGroup()
|
||||
{
|
||||
$this->assertEquals(2, Model\User\create_user('somebody', 'test'));
|
||||
|
||||
$this->assertEquals(1, Model\Group\create_group(1, 'tag'));
|
||||
$this->assertEquals(1, Model\Group\create_group(1, 'tag'));
|
||||
|
||||
$this->assertEquals(1, Model\Group\get_group_id_from_title(1, 'tag'));
|
||||
$this->assertFalse(Model\Group\get_group_id_from_title(1, 'notfound'));
|
||||
|
||||
$this->assertEquals(2, Model\Group\create_group(2, 'tag'));
|
||||
$this->assertEquals(2, Model\Group\create_group(2, 'tag'));
|
||||
}
|
||||
|
||||
public function testGetAll()
|
||||
{
|
||||
$this->assertSame(array(), Model\Group\get_all(1));
|
||||
|
||||
$this->assertEquals(1, Model\Group\create_group(1, 'tag 1'));
|
||||
$this->assertEquals(2, Model\Group\create_group(1, 'tag 2'));
|
||||
|
||||
$groups = Model\Group\get_all(1);
|
||||
$this->assertCount(2, $groups);
|
||||
|
||||
$this->assertEquals(1, $groups[0]['id']);
|
||||
$this->assertEquals('tag 1', $groups[0]['title']);
|
||||
|
||||
$this->assertEquals(2, $groups[1]['id']);
|
||||
$this->assertEquals('tag 2', $groups[1]['title']);
|
||||
}
|
||||
|
||||
public function testAssociation()
|
||||
{
|
||||
$this->assertCreateFeed($this->buildFeed());
|
||||
$this->assertEquals(1, Model\Group\create_group(1, 'tag 1'));
|
||||
$this->assertEquals(2, Model\Group\create_group(1, 'tag 2'));
|
||||
$this->assertEquals(3, Model\Group\create_group(1, 'tag 3'));
|
||||
|
||||
$this->assertTrue(Model\Group\update_feed_groups(1, 1, array(1, 2), 'tag 4'));
|
||||
|
||||
$this->assertEquals(array(1), Model\Group\get_feed_ids_by_group(1));
|
||||
$this->assertEquals(array(1), Model\Group\get_feed_ids_by_group(2));
|
||||
$this->assertEquals(array(), Model\Group\get_feed_ids_by_group(3));
|
||||
$this->assertEquals(array(1), Model\Group\get_feed_ids_by_group(4));
|
||||
|
||||
$groups = Model\Group\get_feed_groups(1);
|
||||
$expected_groups = array(
|
||||
array('id' => 1, 'title' => 'tag 1'),
|
||||
array('id' => 2, 'title' => 'tag 2'),
|
||||
array('id' => 4, 'title' => 'tag 4'),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected_groups, $groups);
|
||||
$this->assertEquals(array(1, 2, 4), Model\Group\get_feed_group_ids(1));
|
||||
$this->assertEquals(array(), Model\Group\get_feed_group_ids(2));
|
||||
|
||||
$expected = array(
|
||||
1 => array(1),
|
||||
2 => array(1),
|
||||
4 => array(1),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, Model\Group\get_groups_feed_ids(1));
|
||||
|
||||
$this->assertTrue(Model\Group\update_feed_groups(1, 1, array(1, 3)));
|
||||
$this->assertEquals(array(1, 3), Model\Group\get_feed_group_ids(1));
|
||||
}
|
||||
}
|
@ -1,9 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Miniflux\Helper;
|
||||
use Miniflux\Model;
|
||||
use Miniflux\Session\SessionStorage;
|
||||
|
||||
require_once __DIR__.'/BaseTest.php';
|
||||
|
||||
class HelperTest extends BaseTest
|
||||
{
|
||||
public function testConfig()
|
||||
{
|
||||
SessionStorage::getInstance()->setUser(array('id' => 1, 'user_id' => 1, 'username' => 'admin', 'is_admin' => 1));
|
||||
|
||||
$this->assertNull(Helper\config('option'));
|
||||
$this->assertSame('default', Helper\config('option', 'default'));
|
||||
|
||||
$this->assertTrue(Model\Config\save(1, array('option1' => '1', 'option2' => '0')));
|
||||
|
||||
$this->assertTrue(Helper\bool_config('option1'));
|
||||
$this->assertFalse(Helper\bool_config('option2'));
|
||||
$this->assertFalse(Helper\bool_config('option3'));
|
||||
$this->assertTrue(Helper\bool_config('option4', true));
|
||||
}
|
||||
|
||||
public function testGenerateToken()
|
||||
{
|
||||
$token1 = Helper\generate_token();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user