First version of the theme system

This commit is contained in:
Frederic Guillot 2013-07-16 21:58:11 -04:00
parent 7f039fc427
commit a30c21dd50
12 changed files with 100 additions and 6 deletions

View File

@ -172,3 +172,29 @@ Actually, the following constants can be overrided:
- `DB_FILENAME` => default value is `data/db.sqlite` - `DB_FILENAME` => default value is `data/db.sqlite`
- `DEBUG` => default is false (enable logs dump of picoFeed) - `DEBUG` => default is false (enable logs dump of picoFeed)
- `DEBUG_DIRECTORY` => default is /tmp (place to store log files) - `DEBUG_DIRECTORY` => default is /tmp (place to store log files)
- `THEME_DIRECTORY` => default is themes
### How to create a theme for Miniflux?
It's very easy to write a custom theme for Miniflux.
A theme is just a CSS file, images and fonts.
A theme doesn't change the behaviour of the application but only the page design.
The first step is to create a new directory structure for your theme:
mkdir -p themes/mysuperskin/{css,img,fonts}
The name of your theme should be only alphanumeric.
There is the following directories inside your theme:
- `css`: Your stylesheet, the file must be named `app.css` (required)
- `img`: Theme images (not required)
- `fonts`: Theme fonts (not required)
For a very basic theme example, have a look to the directory `examples\mytheme`.
Miniflux use responsive design, so it's better if your theme can handle mobile devices.
If you write a very cool theme for Miniflux, **send me your code to be available in the default installation!**
It would be awesome for everybody :)

View File

@ -16,6 +16,7 @@ defined('HTTP_TIMEOUT') or define('HTTP_TIMEOUT', 10);
defined('DB_FILENAME') or define('DB_FILENAME', 'data/db.sqlite'); defined('DB_FILENAME') or define('DB_FILENAME', 'data/db.sqlite');
defined('DEBUG') or define('DEBUG', false); defined('DEBUG') or define('DEBUG', false);
defined('DEBUG_DIRECTORY') or define('DEBUG_DIRECTORY', '/tmp'); defined('DEBUG_DIRECTORY') or define('DEBUG_DIRECTORY', '/tmp');
defined('THEME_DIRECTORY') or define('THEME_DIRECTORY', 'themes');
PicoTools\container('db', function() { PicoTools\container('db', function() {

View File

@ -0,0 +1,11 @@
@font-face {
font-family: Inconsolata;
src: url(../fonts/Inconsolata-Regular.ttf);
}
body {
margin-left: 300px;
padding-left: 10px;
border-left: 5px solid #000;
font-family: 'Inconsolata', sans-serif;
}

Binary file not shown.

Binary file not shown.

20
helpers.php Normal file
View File

@ -0,0 +1,20 @@
<?php
namespace Helper;
function css()
{
$theme = \Model\get_config_value('theme');
if ($theme !== 'original') {
$css_file = THEME_DIRECTORY.'/'.$theme.'/css/app.css';
if (file_exists($css_file)) {
return $css_file.'?version='.filemtime($css_file);
}
}
return 'assets/css/app.css?version='.filemtime('assets/css/app.css');
}

View File

@ -7,6 +7,7 @@ require 'vendor/PicoFarad/Response.php';
require 'vendor/PicoFarad/Request.php'; require 'vendor/PicoFarad/Request.php';
require 'vendor/PicoFarad/Session.php'; require 'vendor/PicoFarad/Session.php';
require 'vendor/PicoFarad/Router.php'; require 'vendor/PicoFarad/Router.php';
require 'helpers.php';
use PicoFarad\Router; use PicoFarad\Router;
use PicoFarad\Response; use PicoFarad\Response;
@ -492,6 +493,7 @@ Router\get_action('config', function() {
'languages' => Model\get_languages(), 'languages' => Model\get_languages(),
'autoflush_options' => Model\get_autoflush_options(), 'autoflush_options' => Model\get_autoflush_options(),
'paging_options' => Model\get_paging_options(), 'paging_options' => Model\get_paging_options(),
'theme_options' => Model\get_themes(),
'menu' => 'config', 'menu' => 'config',
'title' => t('Preferences') 'title' => t('Preferences')
))); )));
@ -525,6 +527,7 @@ Router\post_action('config', function() {
'languages' => Model\get_languages(), 'languages' => Model\get_languages(),
'autoflush_options' => Model\get_autoflush_options(), 'autoflush_options' => Model\get_autoflush_options(),
'paging_options' => Model\get_paging_options(), 'paging_options' => Model\get_paging_options(),
'theme_options' => Model\get_themes(),
'menu' => 'config', 'menu' => 'config',
'title' => t('Preferences') 'title' => t('Preferences')
))); )));

View File

@ -1,6 +1,7 @@
<?php <?php
return array( return array(
'Theme' => 'Thème',
'No item' => 'Aucun élément', 'No item' => 'Aucun élément',
'items' => 'éléments', 'items' => 'éléments',
'There is %d empty feeds, there is maybe an error: %s...' => 'There is %d empty feeds, there is maybe an error: %s...' =>

View File

@ -22,7 +22,7 @@ use PicoFeed\Reader;
use PicoFeed\Export; use PicoFeed\Export;
const DB_VERSION = 9; const DB_VERSION = 10;
const HTTP_USERAGENT = 'Miniflux - http://miniflux.net'; const HTTP_USERAGENT = 'Miniflux - http://miniflux.net';
const LIMIT_ALL = -1; const LIMIT_ALL = -1;
@ -40,6 +40,28 @@ function get_languages()
} }
function get_themes()
{
$themes = array(
'original' => t('Original')
);
if (file_exists(THEME_DIRECTORY)) {
$dir = new \DirectoryIterator(THEME_DIRECTORY);
foreach ($dir as $fileinfo) {
if (! $fileinfo->isDot() && $fileinfo->isDir()) {
$themes[$dir->getFilename()] = ucfirst($dir->getFilename());
}
}
}
return $themes;
}
function get_autoflush_options() function get_autoflush_options()
{ {
return array( return array(
@ -622,7 +644,7 @@ function get_config()
{ {
return \PicoTools\singleton('db') return \PicoTools\singleton('db')
->table('config') ->table('config')
->columns('username', 'language', 'autoflush', 'nocontent', 'items_per_page') ->columns('username', 'language', 'autoflush', 'nocontent', 'items_per_page', 'theme')
->findOne(); ->findOne();
} }
@ -687,6 +709,7 @@ function validate_config_update(array $values)
new Validators\Required('autoflush', t('Value required')), new Validators\Required('autoflush', t('Value required')),
new Validators\Required('items_per_page', t('Value required')), new Validators\Required('items_per_page', t('Value required')),
new Validators\Integer('items_per_page', t('Must be an integer')), new Validators\Integer('items_per_page', t('Must be an integer')),
new Validators\Required('theme', t('Value required')),
)); ));
} }
else { else {
@ -710,8 +733,8 @@ function save_config(array $values)
if (! empty($values['password'])) { if (! empty($values['password'])) {
$values['password'] = \password_hash($values['password'], PASSWORD_BCRYPT); $values['password'] = \password_hash($values['password'], PASSWORD_BCRYPT);
}
else { } else {
unset($values['password']); unset($values['password']);
} }

View File

@ -3,6 +3,12 @@
namespace Schema; namespace Schema;
function version_10($pdo)
{
$pdo->exec('ALTER TABLE config ADD COLUMN theme TEXT DEFAULT "original"');
}
function version_9($pdo) function version_9($pdo)
{ {
$pdo->exec('ALTER TABLE config ADD COLUMN items_per_page INTEGER DEFAULT 100'); $pdo->exec('ALTER TABLE config ADD COLUMN items_per_page INTEGER DEFAULT 100');

View File

@ -10,7 +10,7 @@
<link rel="apple-touch-icon" sizes="114x114" href="./assets/img/touch-icon-iphone-retina.png"> <link rel="apple-touch-icon" sizes="114x114" href="./assets/img/touch-icon-iphone-retina.png">
<link rel="apple-touch-icon" sizes="144x144" href="./assets/img/touch-icon-ipad-retina.png"> <link rel="apple-touch-icon" sizes="144x144" href="./assets/img/touch-icon-ipad-retina.png">
<title><?= isset($title) ? Helper\escape($title) : 'miniflux' ?></title> <title><?= isset($title) ? Helper\escape($title) : 'miniflux' ?></title>
<link href="./assets/css/app.css?v<?= filemtime('assets/css/app.css') ?>" rel="stylesheet" media="screen"> <link href="<?= Helper\css() ?>" rel="stylesheet" media="screen">
<script type="text/javascript" src="./assets/js/app.js?v<?= filemtime('assets/js/app.js') ?>" defer></script> <script type="text/javascript" src="./assets/js/app.js?v<?= filemtime('assets/js/app.js') ?>" defer></script>
</head> </head>
<body> <body>

View File

@ -22,6 +22,9 @@
<?= Helper\form_label(t('Items per page'), 'items_per_page') ?> <?= Helper\form_label(t('Items per page'), 'items_per_page') ?>
<?= Helper\form_select('items_per_page', $paging_options, $values, $errors) ?><br/> <?= Helper\form_select('items_per_page', $paging_options, $values, $errors) ?><br/>
<?= Helper\form_label(t('Theme'), 'theme') ?>
<?= Helper\form_select('theme', $theme_options, $values, $errors) ?><br/>
<?= Helper\form_checkbox('nocontent', t('Do not fetch the content of articles'), 1, isset($values['nocontent']) ? $values['nocontent'] : false) ?><br /> <?= Helper\form_checkbox('nocontent', t('Do not fetch the content of articles'), 1, isset($values['nocontent']) ? $values['nocontent'] : false) ?><br />
<div class="form-actions"> <div class="form-actions">