Add support for multiple users/databases
This commit is contained in:
parent
555fd9279d
commit
eab942537f
@ -25,6 +25,7 @@ Features
|
||||
- Alternative login with a Google Account or Mozilla Persona
|
||||
- **Full article download for feeds that display only a summary** (website scraper based on Xpath rules)
|
||||
- Auto-update from the user interface
|
||||
- Multiple databases (each user has his own database)
|
||||
|
||||
Todo and known bugs
|
||||
-------------------
|
||||
@ -156,6 +157,7 @@ You just need to be inside the directory `miniflux` and run the script `cronjob.
|
||||
|
||||
Parameters | Type | Value
|
||||
--------------------|--------------------------------|-----------------------------
|
||||
--database | optional | Database filename, default is db.sqlite (ex: db2.sqlite)
|
||||
--limit | optional | number of feeds
|
||||
--call-interval | optional, excluded by --limit, require --update-interval | time in minutes < update interval time
|
||||
--update-interval | optional, excluded by --limit, require --call-interval | time in minutes >= call interval time
|
||||
|
@ -643,6 +643,10 @@ iframe {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* desktop design */
|
||||
@media only screen and (min-width: 480px) {
|
||||
|
||||
|
23
common.php
23
common.php
@ -11,25 +11,34 @@ require __DIR__.'/models/feed.php';
|
||||
require __DIR__.'/models/item.php';
|
||||
require __DIR__.'/models/schema.php';
|
||||
require __DIR__.'/models/auto_update.php';
|
||||
require __DIR__.'/models/database.php';
|
||||
|
||||
if (file_exists('config.php')) require 'config.php';
|
||||
|
||||
defined('APP_VERSION') or define('APP_VERSION', 'master');
|
||||
defined('HTTP_TIMEOUT') or define('HTTP_TIMEOUT', 20);
|
||||
defined('DB_FILENAME') or define('DB_FILENAME', 'data/db.sqlite');
|
||||
|
||||
defined('ROOT_DIRECTORY') or define('ROOT_DIRECTORY', __DIR__);
|
||||
defined('DATA_DIRECTORY') or define('DATA_DIRECTORY', 'data');
|
||||
|
||||
defined('ENABLE_MULTIPLE_DB') or define('ENABLE_MULTIPLE_DB', true);
|
||||
defined('DB_FILENAME') or define('DB_FILENAME', 'db.sqlite');
|
||||
|
||||
defined('DEBUG') or define('DEBUG', true);
|
||||
defined('DEBUG_FILENAME') or define('DEBUG_FILENAME', 'data/debug.log');
|
||||
defined('DEBUG_FILENAME') or define('DEBUG_FILENAME', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'debug.log');
|
||||
|
||||
defined('THEME_DIRECTORY') or define('THEME_DIRECTORY', 'themes');
|
||||
defined('SESSION_SAVE_PATH') or define('SESSION_SAVE_PATH', '');
|
||||
|
||||
defined('PROXY_HOSTNAME') or define('PROXY_HOSTNAME', '');
|
||||
defined('PROXY_PORT') or define('PROXY_PORT', 3128);
|
||||
defined('PROXY_USERNAME') or define('PROXY_USERNAME', '');
|
||||
defined('PROXY_PASSWORD') or define('PROXY_PASSWORD', '');
|
||||
defined('ROOT_DIRECTORY') or define('ROOT_DIRECTORY', __DIR__);
|
||||
|
||||
defined('ENABLE_AUTO_UPDATE') or define('ENABLE_AUTO_UPDATE', true);
|
||||
defined('AUTO_UPDATE_DOWNLOAD_DIRECTORY') or define('AUTO_UPDATE_DOWNLOAD_DIRECTORY', 'data/download');
|
||||
defined('AUTO_UPDATE_ARCHIVE_DIRECTORY') or define('AUTO_UPDATE_ARCHIVE_DIRECTORY', 'data/archive');
|
||||
defined('AUTO_UPDATE_BACKUP_DIRECTORY') or define('AUTO_UPDATE_BACKUP_DIRECTORY', 'data/backup');
|
||||
defined('AUTO_UPDATE_DOWNLOAD_DIRECTORY') or define('AUTO_UPDATE_DOWNLOAD_DIRECTORY', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'download');
|
||||
defined('AUTO_UPDATE_ARCHIVE_DIRECTORY') or define('AUTO_UPDATE_ARCHIVE_DIRECTORY', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'archive');
|
||||
defined('AUTO_UPDATE_BACKUP_DIRECTORY') or define('AUTO_UPDATE_BACKUP_DIRECTORY', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'backup');
|
||||
|
||||
PicoFeed\Client::proxy(PROXY_HOSTNAME, PROXY_PORT, PROXY_USERNAME, PROXY_PASSWORD);
|
||||
|
||||
@ -37,7 +46,7 @@ PicoDb\Database::bootstrap('db', function() {
|
||||
|
||||
$db = new PicoDb\Database(array(
|
||||
'driver' => 'sqlite',
|
||||
'filename' => DB_FILENAME
|
||||
'filename' => \Model\Database\get_path(),
|
||||
));
|
||||
|
||||
if ($db->schema()->check(Model\Config\DB_VERSION)) {
|
||||
|
@ -11,7 +11,13 @@ Router\before(function($action) {
|
||||
|
||||
Session\open(dirname($_SERVER['PHP_SELF']), SESSION_SAVE_PATH);
|
||||
|
||||
$ignore_actions = array('login', 'google-auth', 'google-redirect-auth', 'mozilla-auth', 'bookmark-feed');
|
||||
// Select another database
|
||||
if (! empty($_SESSION['database'])) {
|
||||
Model\Database\select($_SESSION['database']);
|
||||
}
|
||||
|
||||
// Redirect to the login form if the user is not authenticated
|
||||
$ignore_actions = array('login', 'google-auth', 'google-redirect-auth', 'mozilla-auth', 'bookmark-feed', 'select-db');
|
||||
|
||||
if (! isset($_SESSION['user']) && ! in_array($action, $ignore_actions)) {
|
||||
Response\redirect('?action=login');
|
||||
@ -50,3 +56,13 @@ Router\get_action('more', function() {
|
||||
|
||||
Response\html(Template\layout('show_more', array('menu' => 'more')));
|
||||
});
|
||||
|
||||
// Select another database
|
||||
Router\get_action('select-db', function() {
|
||||
|
||||
if (ENABLE_MULTIPLE_DB) {
|
||||
$_SESSION['database'] = \Model\Database\select(Request\param('database'));
|
||||
}
|
||||
|
||||
Response\redirect('?action=login');
|
||||
});
|
||||
|
@ -7,6 +7,47 @@ use PicoFarad\Session;
|
||||
use PicoFarad\Template;
|
||||
use PicoDb\Database;
|
||||
|
||||
// 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(),
|
||||
'menu' => 'config',
|
||||
'title' => t('New database')
|
||||
)));
|
||||
}
|
||||
|
||||
Response\redirect('?action=config');
|
||||
});
|
||||
|
||||
// Create a new database
|
||||
Router\post_action('new-db', function() {
|
||||
|
||||
$values = Request\values();
|
||||
list($valid, $errors) = Model\Database\validate($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=config');
|
||||
}
|
||||
|
||||
Response\html(Template\layout('new_db', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'menu' => 'config',
|
||||
'title' => t('New database')
|
||||
)));
|
||||
});
|
||||
|
||||
// Auto-update
|
||||
Router\get_action('auto-update', function() {
|
||||
|
||||
@ -41,7 +82,7 @@ Router\get_action('optimize-db', function() {
|
||||
Router\get_action('download-db', function() {
|
||||
|
||||
Response\force_download('db.sqlite.gz');
|
||||
Response\binary(gzencode(file_get_contents(DB_FILENAME)));
|
||||
Response\binary(gzencode(file_get_contents(\Model\Database\get_path())));
|
||||
});
|
||||
|
||||
// Display preferences page
|
||||
@ -50,7 +91,7 @@ Router\get_action('config', function() {
|
||||
Response\html(Template\layout('config', array(
|
||||
'errors' => array(),
|
||||
'values' => Model\Config\get_all(),
|
||||
'db_size' => filesize(DB_FILENAME),
|
||||
'db_size' => filesize(\Model\Database\get_path()),
|
||||
'languages' => Model\Config\get_languages(),
|
||||
'timezones' => Model\Config\get_timezones(),
|
||||
'autoflush_options' => Model\Config\get_autoflush_options(),
|
||||
@ -84,7 +125,7 @@ Router\post_action('config', function() {
|
||||
Response\html(Template\layout('config', array(
|
||||
'errors' => $errors,
|
||||
'values' => Model\Config\get_all(),
|
||||
'db_size' => filesize(DB_FILENAME),
|
||||
'db_size' => filesize(\Model\Database\get_path()),
|
||||
'languages' => Model\Config\get_languages(),
|
||||
'timezones' => Model\Config\get_timezones(),
|
||||
'autoflush_options' => Model\Config\get_autoflush_options(),
|
||||
|
@ -24,7 +24,9 @@ Router\get_action('login', function() {
|
||||
'google_auth_enable' => Model\Config\get('auth_google_token') !== '',
|
||||
'mozilla_auth_enable' => Model\Config\get('auth_mozilla_token') !== '',
|
||||
'errors' => array(),
|
||||
'values' => array()
|
||||
'values' => array(),
|
||||
'databases' => Model\Database\get_list(),
|
||||
'current_database' => Model\Database\select()
|
||||
)));
|
||||
});
|
||||
|
||||
@ -40,7 +42,9 @@ Router\post_action('login', function() {
|
||||
'google_auth_enable' => Model\Config\get('auth_google_token') !== '',
|
||||
'mozilla_auth_enable' => Model\Config\get('auth_mozilla_token') !== '',
|
||||
'errors' => $errors,
|
||||
'values' => $values
|
||||
'values' => $values,
|
||||
'databases' => Model\Database\get_list(),
|
||||
'current_database' => Model\Database\select()
|
||||
)));
|
||||
});
|
||||
|
||||
|
@ -7,7 +7,8 @@ if (php_sapi_name() === 'cli') {
|
||||
$options = getopt('', array(
|
||||
'limit::',
|
||||
'call-interval::',
|
||||
'update-interval::'
|
||||
'update-interval::',
|
||||
'database::',
|
||||
));
|
||||
}
|
||||
else {
|
||||
@ -15,6 +16,10 @@ else {
|
||||
$options = $_GET;
|
||||
}
|
||||
|
||||
if (! empty($options['database'])) {
|
||||
\Model\Database\select($options['database']);
|
||||
}
|
||||
|
||||
$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;
|
||||
|
@ -213,4 +213,14 @@ return array(
|
||||
'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',
|
||||
'Database name' => 'Nom de la base de données',
|
||||
'Default database' => 'Base de données par défaut',
|
||||
'Select another database' => 'Choisir une autre base de données',
|
||||
'The database name is required' => 'Le nom de la base de données est obligatoire',
|
||||
'Database created successfully.' => 'Base de données créée avec succès.',
|
||||
'Unable to create the new database.' => 'Impossible créer la nouvelle base de données.',
|
||||
'Add a new database (new user)' => 'Ajouter une nouvelle base de données (nouvel utilisateur)',
|
||||
'Create' => 'Créer',
|
||||
);
|
||||
|
115
models/database.php
Normal file
115
models/database.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Model\Database;
|
||||
|
||||
require_once __DIR__.'/../vendor/SimpleValidator/Validator.php';
|
||||
require_once __DIR__.'/../vendor/SimpleValidator/Base.php';
|
||||
require_once __DIR__.'/../vendor/SimpleValidator/Validators/Required.php';
|
||||
require_once __DIR__.'/../vendor/SimpleValidator/Validators/MaxLength.php';
|
||||
require_once __DIR__.'/../vendor/SimpleValidator/Validators/MinLength.php';
|
||||
require_once __DIR__.'/../vendor/SimpleValidator/Validators/Equals.php';
|
||||
require_once __DIR__.'/../vendor/SimpleValidator/Validators/AlphaNumeric.php';
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
// Create a new database for a new user
|
||||
function create($filename, $username, $password)
|
||||
{
|
||||
$filename = DATA_DIRECTORY.DIRECTORY_SEPARATOR.$filename;
|
||||
|
||||
if (! file_exists($filename)) {
|
||||
|
||||
$db = new \PicoDb\Database(array(
|
||||
'driver' => 'sqlite',
|
||||
'filename' => $filename,
|
||||
));
|
||||
|
||||
if ($db->schema()->check(\Model\Config\DB_VERSION)) {
|
||||
|
||||
$db->table('config')->update(array(
|
||||
'username' => $username,
|
||||
'password' => \password_hash($password, PASSWORD_BCRYPT)
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get or set the current database
|
||||
function select($filename = '')
|
||||
{
|
||||
static $current_filename = DB_FILENAME;
|
||||
|
||||
if ($filename !== '' && in_array($filename, get_all())) {
|
||||
$current_filename = $filename;
|
||||
$_SESSION['config'] = \Model\Config\get_all();
|
||||
}
|
||||
|
||||
return $current_filename;
|
||||
}
|
||||
|
||||
// Get database path
|
||||
function get_path()
|
||||
{
|
||||
return DATA_DIRECTORY.DIRECTORY_SEPARATOR.\Model\Database\select();
|
||||
}
|
||||
|
||||
// Get the list of available databases
|
||||
function get_all()
|
||||
{
|
||||
$listing = array();
|
||||
|
||||
$dir = new \DirectoryIterator(DATA_DIRECTORY);
|
||||
|
||||
foreach ($dir as $fileinfo) {
|
||||
if ($fileinfo->getExtension() === 'sqlite') {
|
||||
$listing[] = $fileinfo->getFilename();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Validate database form
|
||||
function validate(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 doesn\'t match')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
@ -90,6 +90,11 @@
|
||||
<li><?= t('Database size:') ?> <strong><?= Helper\format_bytes($db_size) ?></strong></li>
|
||||
<li><a href="?action=optimize-db"><?= t('Optimize the database') ?></a> <?= t('(VACUUM command)') ?></li>
|
||||
<li><a href="?action=download-db"><?= t('Download the entire database') ?></a> <?= t('(Gzip compressed Sqlite file)') ?></li>
|
||||
<?php if (ENABLE_MULTIPLE_DB): ?>
|
||||
<li>
|
||||
<a href="?action=new-db"><?= t('Add a new database (new user)') ?></a></li>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?= \PicoFarad\Template\load('keyboard_shortcuts') ?>
|
||||
|
@ -28,7 +28,7 @@
|
||||
<p class="alert alert-error"><?= Helper\escape($errors['login']) ?></p>
|
||||
<?php endif ?>
|
||||
|
||||
<form method="post" action="?action=login">
|
||||
<form method="post" action="?action=login" class="login-form">
|
||||
|
||||
<?= Helper\form_label(t('Username'), 'username') ?>
|
||||
<?= Helper\form_text('username', $values, $errors, array('autofocus', 'required')) ?><br/>
|
||||
@ -48,6 +48,24 @@
|
||||
<input type="submit" value="<?= t('Sign in') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php if (ENABLE_MULTIPLE_DB && count($databases) > 1): ?>
|
||||
<div class="alert alert-normal">
|
||||
<h3><?= t('Select another database') ?></h3>
|
||||
<ul>
|
||||
<?php foreach ($databases as $filename => $dbname): ?>
|
||||
<li>
|
||||
<?php if ($current_database === $filename): ?>
|
||||
<strong><?= Helper\escape($dbname) ?></strong>
|
||||
<?php else: ?>
|
||||
<a href="?action=select-db&database=<?= Helper\escape($filename) ?>"><?= Helper\escape($dbname) ?></a>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
</body>
|
||||
|
25
templates/new_db.php
Normal file
25
templates/new_db.php
Normal file
@ -0,0 +1,25 @@
|
||||
<div class="page-header">
|
||||
<h2><?= t('New database') ?></h2>
|
||||
<ul>
|
||||
<li><a href="?action=config"><?= t('preferences') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form method="post" action="?action=new-db" autocomplete="off">
|
||||
<?= Helper\form_label(t('Database name'), 'name') ?>
|
||||
<?= Helper\form_text('name', $values, $errors, array('required', 'autofocus')) ?>
|
||||
<p class="form-help"><?= t('The name must have only alpha-numeric characters') ?></p>
|
||||
|
||||
<?= Helper\form_label(t('Username'), 'username') ?>
|
||||
<?= Helper\form_text('username', $values, $errors, array('required')) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Password'), 'password') ?>
|
||||
<?= Helper\form_password('password', $values, $errors, array('required')) ?>
|
||||
|
||||
<?= Helper\form_label(t('Confirmation'), 'confirmation') ?>
|
||||
<?= Helper\form_password('confirmation', $values, $errors, array('required')) ?><br/>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-blue"><?= t('Create') ?></button>
|
||||
</div>
|
||||
</form>
|
Loading…
Reference in New Issue
Block a user