Merge pull request #1455 from FreshRSS/dev

Release 1.6.3
pull/1463/head 1.6.3
Alexandre Alapetite 8 years ago committed by GitHub
commit be0bcfef7e
  1. 28
      CHANGELOG.md
  2. 6
      CREDITS.md
  3. 1
      README.md
  4. 2
      app/Controllers/javascriptController.php
  5. 4
      app/Controllers/subscriptionController.php
  6. 2
      app/Controllers/updateController.php
  7. 21
      app/Controllers/userController.php
  8. 4
      app/Models/Auth.php
  9. 2
      app/Models/EntryDAO.php
  10. 2
      app/Models/Feed.php
  11. 2
      app/Models/FeedDAO.php
  12. 2
      app/Models/UserDAO.php
  13. 2
      app/i18n/cz/gen.php
  14. 2
      app/i18n/de/gen.php
  15. 2
      app/i18n/en/gen.php
  16. 2
      app/i18n/fr/gen.php
  17. 2
      app/i18n/it/gen.php
  18. 4
      app/i18n/nl/gen.php
  19. 2
      app/i18n/ru/gen.php
  20. 2
      app/i18n/tr/gen.php
  21. 2
      app/install.php
  22. 2
      app/layout/aside_configure.phtml
  23. 2
      app/layout/header.phtml
  24. 2
      app/views/auth/formLogin.phtml
  25. 2
      app/views/auth/register.phtml
  26. 1
      app/views/helpers/javascript_vars.phtml
  27. 2
      app/views/index/about.phtml
  28. 2
      app/views/user/manage.phtml
  29. 17
      cli/README.md
  30. 2
      cli/_cli.php
  31. 2
      cli/create-user.php
  32. 2
      cli/delete-user.php
  33. 53
      cli/do-install.php
  34. 2
      cli/list-users.php
  35. 59
      cli/reconfigure.php
  36. 16
      cli/user-info.php
  37. 2
      constants.php
  38. 3
      data/config.default.php
  39. 20
      data/shares.php
  40. 52
      lib/Favicon/DataAccess.php
  41. 3
      lib/Favicon/Favicon.php
  42. 35
      lib/lib_install.php
  43. 10
      lib/lib_rss.php
  44. 2
      p/api/greader.php
  45. 4
      p/scripts/main.js
  46. BIN
      p/themes/Origine-compact/loader.gif
  47. 7
      p/themes/Origine-compact/metadata.json
  48. 1091
      p/themes/Origine-compact/origine-compact.css
  49. BIN
      p/themes/Origine-compact/thumbs/original.png

@ -1,5 +1,33 @@
# Changelog
## 2017-03-11 FreshRSS 1.6.3
* Features
* New option `disable_update` (also from CLI) to hide the system to update to new FreshRSS versions [#1436](https://github.com/FreshRSS/FreshRSS/pull/1436)
* Share with Ⓚnown [#1420](https://github.com/FreshRSS/FreshRSS/pull/1420)
* Share with GNU social [#1422](https://github.com/FreshRSS/FreshRSS/issues/1422)
* UI
* New theme *Origine-compact* [#1388](https://github.com/FreshRSS/FreshRSS/pull/1388)
* Chrome parity with Firefox: auto-focus tab when clicking on notification [#1409](https://github.com/FreshRSS/FreshRSS/pull/1409)
* CLI
* New command `./cli/reconfigure.php` to update an existing installation [#1439](https://github.com/FreshRSS/FreshRSS/pull/1439)
* Many CLI improvements [#1447](https://github.com/FreshRSS/FreshRSS/pull/1447)
* More information (number of feeds, articles, etc.) in `./cli/user-info.php`
* Better idempotency of `./cli/do-install.php` and language parameter [#1449](https://github.com/FreshRSS/FreshRSS/issues/1449)
* Bug fixing
* Fix several CLI issues [#1445](https://github.com/FreshRSS/FreshRSS/issues/1445)
* Fix CLI install bugs with SQLite [#1443](https://github.com/FreshRSS/FreshRSS/issues/1443), [#1448](https://github.com/FreshRSS/FreshRSS/issues/1448)
* Allow empty strings in CLI do-install [#1435](https://github.com/FreshRSS/FreshRSS/pull/1435)
* Fix PostgreSQL bugs with API and feed modifications [#1417](https://github.com/FreshRSS/FreshRSS/pull/1417)
* Do not mark as read in anonymous mode [#1431](https://github.com/FreshRSS/FreshRSS/issues/1431)
* Fix Favicons warnings [#59dfc64](https://github.com/FreshRSS/FreshRSS/commit/59dfc64512372eaba7609d84500d943bb7274399), [#1452](https://github.com/FreshRSS/FreshRSS/pull/1452)
* Security
* Sanitize feed Web site URL [#1434](https://github.com/FreshRSS/FreshRSS/issues/1434)
* No version number for anonymous users [#1404](https://github.com/FreshRSS/FreshRSS/issues/1404)
* Misc.
* Relaxed requirements for username to `/^[0-9a-zA-Z]|[0-9a-zA-Z_]{2,38}$/` [#1423](https://github.com/FreshRSS/FreshRSS/pull/1423)
## 2016-12-26 FreshRSS 1.6.2
* Features

@ -11,11 +11,14 @@ People are sorted by name so please keep this order.
* [Alwaysin](https://github.com/Alwaysin): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=Alwaysin)
* [Amaury Carrade](https://github.com/AmauryCarrade): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=AmauryCarrade), [Web](https://amaury.carrade.eu/)
* [ASMfreaK](https://github.com/ASMfreaK): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=ASMfreaK)
* [Damstre](https://github.com/Damstre): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:Damstre),
* [Crupuk](https://github.com/Crupuk): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:Crupuk)
* [Damstre](https://github.com/Damstre): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:Damstre)
* [danc](https://github.com/danc): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=danc), [Web](http://tintouli.free.fr/)
* [dswd](https://github.com/dswd): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:dswd)
* [ealdraed](https://github.com/ealdraed): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=ealdraed)
* [Frans de Jonge](https://github.com/Frenzie): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=Frenzie), [Web](http://fransdejonge.com/)
* [Guillaume Fillon](https://github.com/kokaz): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:kokaz), [Web](http://www.guillaume-fillon.com/)
* [Guillaume Hayot](https://github.com/postblue): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:postblue), [Web](https://postblue.info/)
* [hckweb](https://github.com/hckweb): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=hckweb)
* [Jaussoin Timothée](https://github.com/edhelas): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=edhelas), [Web](http://edhelas.movim.eu/)
* [Julien Reichardt](https://github.com/j8r): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=j8r), [Web](https://blog.jrei.ch/)
@ -33,5 +36,6 @@ People are sorted by name so please keep this order.
* [romibi](https://github.com/romibi): [contributions](https://github.com/FreshRSS/FreshRSS/commits/dev?author=romibi)
* [subic](https://github.com/subic): [contributions](https://github.com/FreshRSS/documentation/commits?author=subic)
* [Tets42](https://github.com/Tets42): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=Tets42)
* [Thomas Citharel](https://github.com/tcitworld): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:tomgue), [Web](https://www.tcit.fr/)
* [tomgue](https://github.com/tomgue): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=tomgue)
* [Wanabo](https://github.com/Wanabo): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=Wanabo)

@ -51,6 +51,7 @@ We are a friendly community.
6. Advanced configuration settings can be seen in [config.php](./data/config.default.php).
## Automated install
* [![Install on Cloudron](https://cloudron.io/img/button.svg)](https://cloudron.io/button.html?app=org.freshrss.cloudronapp)
* [![DP deploy](https://raw.githubusercontent.com/DFabric/DPlatform-ShellCore/gh-pages/img/deploy.png)](https://dfabric.github.io/DPlatform-ShellCore)
* [YunoHost](https://github.com/YunoHost-Apps/freshrss_ynh)

@ -26,7 +26,7 @@ class FreshRSS_javascript_Controller extends Minz_ActionController {
header('Pragma: no-cache');
$user = isset($_GET['user']) ? $_GET['user'] : '';
if (ctype_alnum($user)) {
if (FreshRSS_user_Controller::checkUsername($user)) {
try {
$salt = FreshRSS_Context::$system_conf->salt;
$conf = get_user_configuration($user);

@ -90,8 +90,8 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
$values = array(
'name' => Minz_Request::param('name', ''),
'description' => sanitizeHTML(Minz_Request::param('description', '', true)),
'website' => Minz_Request::param('website', ''),
'url' => Minz_Request::param('url', ''),
'website' => checkUrl(Minz_Request::param('website', '')),
'url' => checkUrl(Minz_Request::param('url', '')),
'category' => $cat,
'pathEntries' => Minz_Request::param('path_entries', ''),
'priority' => intval(Minz_Request::param('priority', 0)),

@ -162,7 +162,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
}
public function applyAction() {
if (!file_exists(UPDATE_FILENAME) || !is_writable(FRESHRSS_PATH)) {
if (!file_exists(UPDATE_FILENAME) || !is_writable(FRESHRSS_PATH) || Minz_Configuration::get('system')->disable_update) {
Minz_Request::forward(array('c' => 'update'), true);
}

@ -34,6 +34,16 @@ class FreshRSS_user_Controller extends Minz_ActionController {
return $passwordHash == '' ? '' : $passwordHash;
}
/**
* The username is also used as folder name, file name, and part of SQL table name.
* '_' is a reserved internal username.
*/
const USERNAME_PATTERN = '[0-9a-zA-Z]|[0-9a-zA-Z_]{2,38}';
public static function checkUsername($username) {
return preg_match('/^' . self::USERNAME_PATTERN . '$/', $username) === 1;
}
/**
* This action displays the user profile page.
*/
@ -104,7 +114,8 @@ class FreshRSS_user_Controller extends Minz_ActionController {
$userConfig = array();
}
$ok = ($new_user_name != '') && ctype_alnum($new_user_name);
$ok = self::checkUsername($new_user_name);
$homeDir = join_path(DATA_PATH, 'users', $new_user_name);
if ($ok) {
$languages = Minz_Translate::availableLanguages();
@ -114,7 +125,7 @@ class FreshRSS_user_Controller extends Minz_ActionController {
$ok &= !in_array(strtoupper($new_user_name), array_map('strtoupper', listUsers())); //Not an existing user, case-insensitive
$configPath = join_path(DATA_PATH, 'users', $new_user_name, 'config.php');
$configPath = join_path($homeDir, 'config.php');
$ok &= !file_exists($configPath);
}
if ($ok) {
@ -131,7 +142,9 @@ class FreshRSS_user_Controller extends Minz_ActionController {
}
}
if ($ok) {
mkdir(join_path(DATA_PATH, 'users', $new_user_name));
if (!is_dir($homeDir)) {
mkdir($homeDir);
}
$userConfig['passwordHash'] = $passwordHash;
$userConfig['apiPasswordHash'] = $apiPasswordHash;
$ok &= (file_put_contents($configPath, "<?php\n return " . var_export($userConfig, true) . ';') !== false);
@ -187,7 +200,7 @@ class FreshRSS_user_Controller extends Minz_ActionController {
$db = FreshRSS_Context::$system_conf->db;
require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php');
$ok = ctype_alnum($username);
$ok = self::checkUsername($username);
if ($ok) {
$default_user = FreshRSS_Context::$system_conf->default_user;
$ok &= (strcasecmp($username, $default_user) !== 0); //It is forbidden to delete the default user

@ -182,7 +182,7 @@ class FreshRSS_Auth {
class FreshRSS_FormAuth {
public static function checkCredentials($username, $hash, $nonce, $challenge) {
if (!ctype_alnum($username) ||
if (!FreshRSS_user_Controller::checkUsername($username) ||
!ctype_graph($challenge) ||
!ctype_alnum($nonce)) {
Minz_Log::debug('Invalid credential parameters:' .
@ -211,7 +211,7 @@ class FreshRSS_FormAuth {
// Token has expired (> 1 month) or does not exist.
// TODO: 1 month -> use a configuration instead
@unlink($token_file);
return array();
return array();
}
$credentials = @file_get_contents($token_file);

@ -649,7 +649,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
$values[] = intval($id);
break;
case 'A':
$where .= '1 ';
$where .= '1=1 ';
break;
default:
throw new FreshRSS_EntriesGetter_Exception('Bad type in Entry->listByType: [' . $type . ']!');

@ -442,7 +442,7 @@ class FreshRSS_Feed extends Minz_Model {
file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
}
$currentUser = Minz_Session::param('currentUser');
if (ctype_alnum($currentUser) && !file_exists($path . '/' . $currentUser . '.txt')) {
if (FreshRSS_user_Controller::checkUsername($currentUser) && !file_exists($path . '/' . $currentUser . '.txt')) {
touch($path . '/' . $currentUser . '.txt');
}
}

@ -67,7 +67,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
$set = '';
foreach ($valuesTmp as $key => $v) {
$set .= $key . '=?, ';
$set .= '`' . $key . '`=?, ';
if ($key == 'httpAuth') {
$valuesTmp[$key] = base64_encode($v);

@ -85,7 +85,7 @@ class FreshRSS_UserDAO extends Minz_ModelPdo {
}
public static function touch($username = '') {
if (($username == '') || (!ctype_alnum($username))) {
if (!FreshRSS_user_Controller::checkUsername($username)) {
$username = Minz_Session::param('currentUser', '_');
}
return touch(join_path(DATA_PATH , 'users', $username, 'config.php'));

@ -165,6 +165,8 @@ return array(
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
'jdh' => 'Journal du hacker',
'Known' => 'Known based sites',
'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Upozornění!',

@ -165,6 +165,8 @@ return array(
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
'jdh' => 'Journal du hacker',
'Known' => 'Known based sites',
'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Achtung!',

@ -165,6 +165,8 @@ return array(
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
'jdh' => 'Journal du hacker',
'Known' => 'Known based sites',
'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Warning!',

@ -165,6 +165,8 @@ return array(
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
'jdh' => 'Journal du hacker',
'Known' => 'Sites basés sur Known',
'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Attention !',

@ -165,6 +165,8 @@ return array(
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
'jdh' => 'Journal du hacker',
'Known' => 'Siti basati su Known',
'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Attenzione!',

@ -163,8 +163,10 @@ return array(
'shaarli' => 'Shaarli',
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
'wallabagv2' => 'wallabag v2',
'jdh' => 'Journal du hacker',
'Known' => 'Known based sites',
'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Attentie!',

@ -165,6 +165,8 @@ return array(
'twitter' => 'Twitter',
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
'Known' => 'Known based sites',
'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Warning!',

@ -165,6 +165,8 @@ return array(
'wallabag' => 'wallabag v1',
'wallabagv2' => 'wallabag v2',
'jdh' => 'Journal du hacker',
'Known' => 'Known based sites',
'gnusocial' => 'GNU social',
),
'short' => array(
'attention' => 'Tehlike!',

@ -553,7 +553,7 @@ function printStep2() {
<div class="form-group">
<label class="group-name" for="default_user"><?php echo _t('install.default_user'); ?></label>
<div class="group-controls">
<input type="text" id="default_user" name="default_user" required="required" size="16" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" value="<?php echo isset($_SESSION['default_user']) ? $_SESSION['default_user'] : ''; ?>" placeholder="<?php echo httpAuthUser() == '' ? 'alice' : httpAuthUser(); ?>" tabindex="3" />
<input type="text" id="default_user" name="default_user" required="required" size="16" pattern="<?php echo FreshRSS_user_Controller::USERNAME_PATTERN; ?>" value="<?php echo isset($_SESSION['default_user']) ? $_SESSION['default_user'] : ''; ?>" placeholder="<?php echo httpAuthUser() == '' ? 'alice' : httpAuthUser(); ?>" tabindex="3" />
</div>
</div>

@ -41,9 +41,11 @@
Minz_Request::actionName() === 'checkInstall' ? ' active' : ''; ?>">
<a href="<?php echo _url('update', 'checkInstall'); ?>"><?php echo _t('gen.menu.check_install'); ?></a>
</li>
<?php if (!Minz_Configuration::get('system')->disable_update) { ?>
<li class="item<?php echo Minz_Request::controllerName() === 'update' &&
Minz_Request::actionName() === 'index' ? ' active' : ''; ?>">
<a href="<?php echo _url('update', 'index'); ?>"><?php echo _t('gen.menu.update'); ?></a>
</li>
<?php } ?>
<?php } ?>
</ul>

@ -71,8 +71,10 @@ if (FreshRSS_Auth::accessNeedsAction()) {
<li class="item"><a href="<?php echo _url('user', 'manage'); ?>"><?php echo _t('gen.menu.user_management'); ?></a></li>
<li class="item"><a href="<?php echo _url('auth', 'index'); ?>"><?php echo _t('gen.menu.authentication'); ?></a></li>
<li class="item"><a href="<?php echo _url('update', 'checkInstall'); ?>"><?php echo _t('gen.menu.check_install'); ?></a></li>
<?php if (!Minz_Configuration::get('system')->disable_update) { ?>
<li class="item"><a href="<?php echo _url('update', 'index'); ?>"><?php echo _t('gen.menu.update'); ?></a></li>
<?php } ?>
<?php } ?>
<li class="separator"></li>
<li class="item"><a href="<?php echo _url('stats', 'index'); ?>"><?php echo _t('gen.menu.stats'); ?></a></li>
<li class="item"><a href="<?php echo _url('index', 'logs'); ?>"><?php echo _t('gen.menu.logs'); ?></a></li>

@ -9,7 +9,7 @@
<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
<div>
<label for="username"><?php echo _t('gen.auth.username'); ?></label>
<input type="text" id="username" name="username" size="16" required="required" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" autofocus="autofocus" />
<input type="text" id="username" name="username" size="16" required="required" pattern="<?php echo FreshRSS_user_Controller::USERNAME_PATTERN; ?>" autofocus="autofocus" />
</div>
<div>
<label for="passwordPlain"><?php echo _t('gen.auth.password'); ?></label>

@ -5,7 +5,7 @@
<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
<div>
<label class="group-name" for="new_user_name"><?php echo _t('gen.auth.username'), '<br />', _i('help'), ' ', _t('gen.auth.username.format'); ?></label>
<input id="new_user_name" name="new_user_name" type="text" size="16" required="required" maxlength="16" autocomplete="off" pattern="[0-9a-zA-Z]{1,16}" />
<input id="new_user_name" name="new_user_name" type="text" size="16" required="required" autocomplete="off" pattern="<?php echo FreshRSS_user_Controller::USERNAME_PATTERN; ?>" />
</div>
<div>

@ -3,6 +3,7 @@ $mark = FreshRSS_Context::$user_conf->mark_when;
$s = FreshRSS_Context::$user_conf->shortcuts;
echo htmlspecialchars(json_encode(array(
'context' => array(
'anonymous' => !FreshRSS_Auth::hasAccess(),
'auto_remove_article' => !!FreshRSS_Context::isAutoRemoveAvailable(),
'hide_posts' => !(FreshRSS_Context::$user_conf->display_posts || Minz_Request::actionName() === 'reader'),
'display_order' => Minz_Request::param('order', FreshRSS_Context::$user_conf->sort_order),

@ -13,8 +13,10 @@
<dt><?php echo _t('index.about.license'); ?></dt>
<dd><?php echo _t('index.about.agpl3'); ?></dd>
<?php if (FreshRSS_Auth::hasAccess()): ?>
<dt><?php echo _t('index.about.version'); ?></dt>
<dd><?php echo FRESHRSS_VERSION; ?></dd>
<?php endif; ?>
</dl>
<p><?php echo _t('index.about.freshrss_description'); ?></p>

@ -22,7 +22,7 @@
<div class="form-group">
<label class="group-name" for="new_user_name"><?php echo _t('admin.user.username'); ?></label>
<div class="group-controls">
<input id="new_user_name" name="new_user_name" type="text" size="16" required="required" maxlength="16" autocomplete="off" pattern="[0-9a-zA-Z]{1,16}" placeholder="demo" />
<input id="new_user_name" name="new_user_name" type="text" size="16" required="required" autocomplete="off" pattern="<?php echo FreshRSS_user_Controller::USERNAME_PATTERN; ?>" placeholder="demo" />
</div>
</div>

@ -32,13 +32,17 @@ Options in parenthesis are optional.
```sh
cd /usr/share/FreshRSS
./cli/do-install.php --default_user admin ( --auth_type form --environment production --base_url https://rss.example.net/ --title FreshRSS --allow_anonymous --api_enabled --db-type mysql --db-host localhost:3306 --db-user freshrss --db-password dbPassword123 --db-base freshrss --db-prefix freshrss )
./cli/do-install.php --default_user admin ( --auth_type form --environment production --base_url https://rss.example.net/ --language en --title FreshRSS --allow_anonymous --api_enabled --db-type mysql --db-host localhost:3306 --db-user freshrss --db-password dbPassword123 --db-base freshrss --db-prefix freshrss )
# --auth_type can be: 'form' (default), 'http_auth' (using the Web server access control), 'none' (dangerous)
# --db-type can be: 'sqlite' (default), 'mysql' (MySQL or MariaDB), 'pgsql' (PostgreSQL)
# --environment can be: 'production' (default), 'development' (for additional log messages)
# --language can be: 'en' (default), 'fr', or one of the [supported languages](../app/i18n/)
# --db-prefix is an optional prefix in front of the names of the tables. We suggest using 'freshrss_'
# This command does not create the default user. Do that with ./cli/create-user.php
./cli/reconfigure.php
# Same parameters as for do-install.php. Used to update an existing installation.
./cli/create-user.php --user username ( --password 'password' --api-password 'api_password' --language en --email user@example.net --token 'longRandomString' --no-default-feeds )
# --language can be: 'en' (default), 'fr', or one of the [supported languages](../app/i18n/)
@ -59,14 +63,15 @@ cd /usr/share/FreshRSS
./cli/user-info.php -h --user username
# -h is to use a human-readable format
# --user can be a username, or '*' to loop on all users
# Returns a * if the user is admin, the name of the user, the date/time of last action, and the size occupied
# Returns: 1) a * iff the user is admin, 2) the name of the user,
# 3) the date/time of last user action, 4) the size occupied,
# and the number of: 5) categories, 6) feeds, 7) read articles, 8) unread articles, and 9) favourites
```
## Unix piping
It is possible to invoke a command multiple times, e.g. with different usernames, thanks to the `xargs -n1` command.
Example showing user information for all users which username starts with 'a':
```sh
@ -78,3 +83,9 @@ Example showing all users ranked by date of last activity:
```sh
./cli/user-info.php -h --user '*' | sort -k2 -r
```
Example to get the number of feeds of a given user:
```sh
./cli/user-info.php --user alex | cut -f6
```

@ -20,7 +20,7 @@ function fail($message) {
}
function cliInitUser($username) {
if (!ctype_alnum($username)) {
if (!FreshRSS_user_Controller::checkUsername($username)) {
fail('FreshRSS error: invalid username: ' . $username . "\n");
}

@ -17,7 +17,7 @@ if (empty($options['user'])) {
" --language en --email user@example.net --token 'longRandomString --no-default-feeds' )");
}
$username = $options['user'];
if (!ctype_alnum($username)) {
if (!FreshRSS_user_Controller::checkUsername($username)) {
fail('FreshRSS error: invalid username “' . $username . '”');
}

@ -10,7 +10,7 @@ if (empty($options['user'])) {
fail('Usage: ' . basename(__FILE__) . " --user username");
}
$username = $options['user'];
if (!ctype_alnum($username)) {
if (!FreshRSS_user_Controller::checkUsername($username)) {
fail('FreshRSS error: invalid username “' . $username . '”');
}

@ -3,9 +3,14 @@
require('_cli.php');
require(LIB_PATH . '/lib_install.php');
if (!file_exists(DATA_PATH . '/do-install.txt')) {
fail('FreshRSS looks to be already installed! Please use `./cli/reconfigure.php` instead.');
}
$params = array(
'environment:',
'base_url:',
'language:',
'title:',
'default_user:',
'allow_anonymous',
@ -13,6 +18,7 @@ $params = array(
'auth_type:',
'api_enabled',
'allow_robots',
'disable_update',
);
$dBparams = array(
@ -29,32 +35,13 @@ $options = getopt('', array_merge($params, $dBparams));
if (empty($options['default_user'])) {
fail('Usage: ' . basename(__FILE__) . " --default_user admin ( --auth_type form" .
" --environment production --base_url https://rss.example.net/" .
" --title FreshRSS --allow_anonymous --api_enabled" .
" --language en --title FreshRSS --allow_anonymous --api_enabled" .
" --db-type mysql --db-host localhost:3306 --db-user freshrss --db-password dbPassword123" .
" --db-base freshrss --db-prefix freshrss_ )");
" --db-base freshrss --db-prefix freshrss_ --disable_update )");
}
fwrite(STDERR, 'FreshRSS install…' . "\n");
$requirements = checkRequirements();
if ($requirements['all'] !== 'ok') {
$message = 'FreshRSS install failed requirements:' . "\n";
foreach ($requirements as $requirement => $check) {
if ($check !== 'ok' && $requirement !== 'all') {
$message .= '• ' . $requirement . "\n";
}
}
fail($message);
}
if (!ctype_alnum($options['default_user'])) {
fail('FreshRSS invalid default username (must be ASCII alphanumeric): ' . $options['default_user']);
}
if (isset($options['auth_type']) && !in_array($options['auth_type'], array('form', 'http_auth', 'none'))) {
fail('FreshRSS invalid authentication method (auth_type must be one of { form, http_auth, none }: ' . $options['auth_type']);
}
$config = array(
'salt' => generateSalt(),
'db' => FreshRSS_Context::$system_conf->db,
@ -73,12 +60,34 @@ if ((!empty($config['base_url'])) && server_is_public($config['base_url'])) {
foreach ($dBparams as $dBparam) {
$dBparam = rtrim($dBparam, ':');
if (!empty($options[$dBparam])) {
if (isset($options[$dBparam])) {
$param = substr($dBparam, strlen('db-'));
$config['db'][$param] = $options[$dBparam];
}
}
$requirements = checkRequirements($config['db']['type']);
if ($requirements['all'] !== 'ok') {
$message = 'FreshRSS install failed requirements:' . "\n";
foreach ($requirements as $requirement => $check) {
if ($check !== 'ok' && !in_array($requirement, array('all', 'pdo', 'message'))) {
$message .= '• ' . $requirement . "\n";
}
}
if (!empty($requirements['message'])) {
$message .= '• ' . $requirements['message'] . "\n";
}
fail($message);
}
if (!FreshRSS_user_Controller::checkUsername($options['default_user'])) {
fail('FreshRSS invalid default username (must be ASCII alphanumeric): ' . $options['default_user']);
}
if (isset($options['auth_type']) && !in_array($options['auth_type'], array('form', 'http_auth', 'none'))) {
fail('FreshRSS invalid authentication method (auth_type must be one of { form, http_auth, none }: ' . $options['auth_type']);
}
if (file_put_contents(join_path(DATA_PATH, 'config.php'), "<?php\n return " . var_export($config, true) . ";\n") === false) {
fail('FreshRSS could not write configuration file!: ' . join_path(DATA_PATH, 'config.php'));
}

@ -4,7 +4,7 @@ require('_cli.php');
$users = listUsers();
sort($users);
if (FreshRSS_Context::$system_conf->default_user !== '') {
if (FreshRSS_Context::$system_conf->default_user !== '' && in_array(FreshRSS_Context::$system_conf->default_user, $users, true)) {
array_unshift($users, FreshRSS_Context::$system_conf->default_user);
$users = array_unique($users);
}

@ -0,0 +1,59 @@
#!/usr/bin/php
<?php
require('_cli.php');
$params = array(
'environment:',
'base_url:',
'language:',
'title:',
'default_user:',
'allow_anonymous',
'allow_anonymous_refresh',
'auth_type:',
'api_enabled',
'allow_robots',
'disable_update',
);
$dBparams = array(
'db-type:',
'db-host:',
'db-user:',
'db-password:',
'db-base:',
'db-prefix:',
);
$options = getopt('', array_merge($params, $dBparams));
fwrite(STDERR, 'Reconfiguring FreshRSS…' . "\n");
$config = Minz_Configuration::get('system');
foreach ($params as $param) {
$param = rtrim($param, ':');
if (isset($options[$param])) {
$config->$param = $options[$param] === false ? true : $options[$param];
}
}
$db = $config->db;
foreach ($dBparams as $dBparam) {
$dBparam = rtrim($dBparam, ':');
if (isset($options[$dBparam])) {
$param = substr($dBparam, strlen('db-'));
$db[$param] = $options[$dBparam];
}
}
$config->db = $db;
if (!FreshRSS_user_Controller::checkUsername($config->default_user)) {
fail('FreshRSS invalid default username (must be ASCII alphanumeric): ' . $config->default_user);
}
if (isset($config->auth_type) && !in_array($config->auth_type, array('form', 'http_auth', 'none'))) {
fail('FreshRSS invalid authentication method (auth_type must be one of { form, http_auth, none }: ' . $config->auth_type);
}
$config->save();
done();

@ -14,22 +14,36 @@ $users = $options['user'] === '*' ? listUsers() : array($options['user']);
foreach ($users as $username) {
$username = cliInitUser($username);
echo $username === FreshRSS_Context::$system_conf->default_user ? '*' : ' ', "\t";
$catDAO = new FreshRSS_CategoryDAO();
$feedDAO = FreshRSS_Factory::createFeedDao($username);
$entryDAO = FreshRSS_Factory::createEntryDao($username);
echo $username === FreshRSS_Context::$system_conf->default_user ? '*' : ' ', "\t";
$nbEntries = $entryDAO->countUnreadRead();
$nbFavorites = $entryDAO->countUnreadReadFavorites();
if (isset($options['h'])) { //Human format
echo
$username, "\t",
date('c', FreshRSS_UserDAO::mtime($username)), "\t",
format_bytes($entryDAO->size()), "\t",
$catDAO->count(), " categories\t",
count($feedDAO->listFeedsIds()), " feeds\t",
$nbEntries['read'], " reads\t",
$nbEntries['unread'], " unreads\t",
$nbFavorites['all'], " favourites\t",
"\n";
} else {
echo
$username, "\t",
FreshRSS_UserDAO::mtime($username), "\t",
$entryDAO->size(), "\t",
$catDAO->count(), "\t",
count($feedDAO->listFeedsIds()), "\t",
$nbEntries['read'], "\t",
$nbEntries['unread'], "\t",
$nbFavorites['all'], "\t",
"\n";
}
}

@ -1,5 +1,5 @@
<?php
define('FRESHRSS_VERSION', '1.6.2');
define('FRESHRSS_VERSION', '1.6.3');
define('FRESHRSS_WEBSITE', 'http://freshrss.org');
define('FRESHRSS_WIKI', 'http://doc.freshrss.org');

@ -146,4 +146,7 @@ return array(
# List of enabled FreshRSS extensions.
'extensions_enabled' => array(),
# Disable self-update,
'disable_update' => false,
);

@ -85,8 +85,20 @@ return array(
'form' => 'simple',
),
'jdh' => array(
'url' => 'https://www.journalduhacker.net/stories/new?url=~LINK~&title=~TITLE~',
'transform' => array('rawurlencode'),
'form' => 'simple',
),
'url' => 'https://www.journalduhacker.net/stories/new?url=~LINK~&title=~TITLE~',
'transform' => array('rawurlencode'),
'form' => 'simple',
),
'Known' => array(
'url' => '~URL~/share?share_url=~LINK~&share_title=~TITLE~',
'transform' => array('rawurlencode'),
'help' => 'https://withknown.com/',
'form' => 'advanced',
),
'gnusocial' => array(
'url' => '~URL~/notice/new?content=~TITLE~%20~LINK~',
'transform' => array('urlencode'),
'help' => 'https://gnu.io/social/',
'form' => 'advanced',
),
);

@ -9,33 +9,33 @@ namespace Favicon;
**/
class DataAccess {
public function retrieveUrl($url) {
$this->set_context();
return @file_get_contents($url);
$this->set_context();
return @file_get_contents($url);
}
public function retrieveHeader($url) {
$this->set_context();
$this->set_context();
$headers = @get_headers($url, 1);
return $headers ? array_change_key_case($headers) : array();
return is_array($headers) ? array_change_key_case($headers) : array();
}
public function saveCache($file, $data) {
file_put_contents($file, $data);
}
public function readCache($file) {
return file_get_contents($file);
}
private function set_context() {
stream_context_set_default(
array(
'http' => array(
'method' => 'GET',
'timeout' => 10,
'header' => "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:20.0; Favicon; +https://github.com/ArthurHoaro/favicon) Gecko/20100101 Firefox/32.0\r\n",
)
)
);
}
public function saveCache($file, $data) {
file_put_contents($file, $data);
}
public function readCache($file) {
return file_get_contents($file);
}
private function set_context() {
stream_context_set_default(
array(
'http' => array(
'method' => 'GET',
'timeout' => 10,
'header' => "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:20.0; Favicon; +https://github.com/ArthurHoaro/favicon) Gecko/20100101 Firefox/32.0\r\n",
)
)
);
}
}
}

@ -89,6 +89,9 @@ class Favicon
$loop = TRUE;
while ($loop && $max_loop-- > 0) {
$headers = $this->dataAccess->retrieveHeader($url);
if (empty($headers)) {
return false;
}
$exploded = explode(' ', $headers[0]);
if( !isset($exploded[1]) ) {

@ -5,14 +5,36 @@ define('BCRYPT_COST', 9);
Minz_Configuration::register('default_system', join_path(DATA_PATH, 'config.default.php'));
Minz_Configuration::register('default_user', join_path(USERS_PATH, '_', 'config.default.php'));
function checkRequirements() {
function checkRequirements($dbType = '') {
$php = version_compare(PHP_VERSION, '5.3.3') >= 0;
$minz = file_exists(join_path(LIB_PATH, 'Minz'));
$curl = extension_loaded('curl');
$pdo_mysql = extension_loaded('pdo_mysql');
$pdo_sqlite = extension_loaded('pdo_sqlite');
$pdo_pgsql = extension_loaded('pdo_pgsql');
$pdo = $pdo_mysql || $pdo_sqlite || $pdo_pgsql;
$message = '';
switch ($dbType) {
case 'mysql':
$pdo_sqlite = $pdo_pgsql = true;
$pdo = $pdo_mysql;
break;
case 'sqlite':
$pdo_mysql = $pdo_pgsql = true;
$pdo = $pdo_sqlite;
break;
case 'pgsql':
$pdo_mysql = $pdo_sqlite = true;
$pdo = $pdo_pgsql;
break;
case '':
$pdo = $pdo_mysql || $pdo_sqlite || $pdo_pgsql;
break;
default:
$pdo_mysql = $pdo_sqlite = $pdo_pgsql = true;
$pdo = false;
$message = 'Invalid database type!';
break;
}
$pcre = extension_loaded('pcre');
$ctype = extension_loaded('ctype');
$fileinfo = extension_loaded('fileinfo');
@ -44,8 +66,9 @@ function checkRequirements() {
'users' => $users ? 'ok' : 'ko',
'favicons' => $favicons ? 'ok' : 'ko',
'http_referer' => $http_referer ? 'ok' : 'ko',
'message' => $message ?: 'ok',
'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $fileinfo && $dom && $xml &&
$data && $cache && $users && $favicons && $http_referer ?
$data && $cache && $users && $favicons && $http_referer && $message == '' ?
'ok' : 'ko'
);
}
@ -77,7 +100,11 @@ function checkDb(&$dbOptions) {
break;
case 'sqlite':
include_once(APP_PATH . '/SQL/install.sql.sqlite.php');
$dsn = 'sqlite:' . join_path(USERS_PATH, $dbOptions['default_user'], 'db.sqlite');
$path = join_path(USERS_PATH, $dbOptions['default_user']);
if (!is_dir($path)) {
mkdir($path);
}
$dsn = 'sqlite:' . join_path($path, 'db.sqlite');
$driver_options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
);

@ -69,10 +69,10 @@ function idn_to_puny($url) {
}
function checkUrl($url) {
if (empty ($url)) {
if ($url == '') {
return '';
}
if (!preg_match ('#^https?://#i', $url)) {
if (!preg_match('#^https?://#i', $url)) {
$url = 'http://' . $url;
}
$url = idn_to_puny($url); //PHP bug #53474 IDN
@ -285,7 +285,7 @@ function uSecString() {
}
function invalidateHttpCache($username = '') {
if (($username == '') || (!ctype_alnum($username))) {
if (!FreshRSS_user_Controller::checkUsername($username)) {
Minz_Session::_param('touch', uTimeString());
$username = Minz_Session::param('currentUser', '_');
}
@ -299,13 +299,11 @@ function listUsers() {
scandir($base_path),
array('..', '.', '_')
));
foreach ($dir_list as $file) {
if (is_dir(join_path($base_path, $file))) {
if ($file[0] !== '.' && is_dir(join_path($base_path, $file)) && file_exists(join_path($base_path, $file, 'config.php'))) {
$final_list[] = $file;
}
}
return $final_list;
}

@ -152,7 +152,7 @@ function authorizationToUser() {
$headerAuthX = explode('/', $headerAuth, 2);
if (count($headerAuthX) === 2) {
$user = $headerAuthX[0];
if (ctype_alnum($user)) {
if (FreshRSS_user_Controller::checkUsername($user)) {
FreshRSS_Context::$user_conf = get_user_configuration($user);
if (FreshRSS_Context::$user_conf == null) {
Minz_Log::warning('Invalid API user ' . $user . ': configuration cannot be found.');

@ -117,6 +117,7 @@ function incUnreadsFeed(article, feed_id, nb) {
var pending_entries = {};
function mark_read(active, only_not_read) {
if ((active.length === 0) || (!active.attr('id')) ||
context.anonymous ||
(only_not_read && !active.hasClass("not_read"))) {
return false;
}
@ -935,6 +936,8 @@ function notifs_html5_show(nb) {
notification.onclick = function() {
window.location.reload();
window.focus();
notification.close();
};
if (context.html5_notif_timeout !== 0) {
@ -1014,6 +1017,7 @@ function load_more_posts() {
init_load_more(box_load_more);
$('#load_more').removeClass('loading');
$('#bigMarkAsRead').removeAttr('disabled');
load_more = false;
$(document.body).trigger('sticky_kit:recalc');
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

@ -0,0 +1,7 @@
{
"name": "Origine-compact",
"author": "Kevin Papst",
"description": "A theme that tries to use the screen size more efficiently, based on Origine",
"version": 0.1,
"files": ["_template.css", "origine-compact.css"]
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Loading…
Cancel
Save