Merge pull request #1810 from FreshRSS/dev

FreshRSS 1.10.1
This commit is contained in:
Alexandre Alapetite
2018-03-04 15:26:24 +01:00
committed by GitHub
42 changed files with 401 additions and 47 deletions

3
.dockerignore Normal file
View File

@@ -0,0 +1,3 @@
*/.git
*/data
*/docs

View File

@@ -1,5 +1,25 @@
# FreshRSS changelog
## 2018-03-04 FreshRSS 1.10.1
* Deployment
* New Docker image, smaller (based on Alpine Linux) and newer (with PHP 7.1) [#1813](https://github.com/FreshRSS/FreshRSS/pull/1813)
* with [automated build](https://hub.docker.com/r/freshrss/freshrss/) for x86-64 (AMD64) architectures
* CLI
* New command `./cli/prepare.php` to make the needed sub-directories of the `./data/` directory [#1813](https://github.com/FreshRSS/FreshRSS/pull/1813)
* Bug fixing
* Fix API bug for EasyRSS [#1799](https://github.com/FreshRSS/FreshRSS/issues/1799)
* Fix login bug when using double authentication (HTTP + Web form) [#1807](https://github.com/FreshRSS/FreshRSS/issues/1807)
* Fix database upgrade for FreshRSS versions older than 1.1.1 [#1803](https://github.com/FreshRSS/FreshRSS/issues/1803)
* Fix cases of double port in FreshRSS public URL [#1815](https://github.com/FreshRSS/FreshRSS/pull/1815)
* UI
* Add tooltips on share configuration buttons [#1805](https://github.com/FreshRSS/FreshRSS/pull/1805)
* Misc.
* Move `./data/shares.php` to `./app/shares.php` to facilitate updates [#1812](https://github.com/FreshRSS/FreshRSS/pull/1812)
* Show article author email when there is no author name [#1801](https://github.com/FreshRSS/FreshRSS/pull/1801)
* Improve translation tools [#1808](https://github.com/FreshRSS/FreshRSS/pull/1808)
## 2018-02-24 FreshRSS 1.10.0
* API

View File

@@ -44,6 +44,7 @@ People are sorted by name so please keep this order.
* [Nicolas Lœuillet](https://github.com/nicosomb): [contributions](https://github.com/FreshRSS/documentation/commits?author=nicosomb), [Web](http://www.loeuillet.org/)
* [Nicola Spanti](https://github.com/RyDroid): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:RyDroid), [Web](http://www.nicola-spanti.info/)
* [Olivier Dossmann](https://github.com/blankoworld): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=blankoworld), [Web](https://olivier.dossmann.net)
* [perrinjerome](https://github.com/perrinjerome): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:perrinjerome)
* [plopoyop](https://github.com/plopoyop): [contributions](https://github.com/FreshRSS/FreshRSS/commits?author=plopoyop)
* [Paulius Šukys](https://github.com/psukys): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:psukys), [Web](http://sukys.eu)
* [purexo](https://github.com/purexo): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:purexo), [Web](https://purexo.mom/)

22
Docker/Dockerfile Normal file
View File

@@ -0,0 +1,22 @@
FROM alpine:3.7
RUN apk add --no-cache \
apache2 php7-apache2 \
php7 php7-curl php7-gmp php7-intl php7-mbstring php7-xml php7-zip \
php7-ctype php7-dom php7-fileinfo php7-json php7-session \
php7-pdo_sqlite \
php7-pdo_mysql \
php7-pdo_pgsql
ENV FRESHRSS_ROOT /var/www/FreshRSS
RUN mkdir -p ${FRESHRSS_ROOT} /run/apache2/
WORKDIR ${FRESHRSS_ROOT}
COPY . ${FRESHRSS_ROOT}
COPY ./Docker/*.Apache.conf /etc/apache2/conf.d/
EXPOSE 80
CMD php -f ./cli/prepare.php > /dev/null && \
chown -R :www-data ${FRESHRSS_ROOT} && \
chmod -R g+r ${FRESHRSS_ROOT} && chmod -R g+w ${FRESHRSS_ROOT}/data/ && \
exec httpd -D FOREGROUND

View File

@@ -0,0 +1,27 @@
<IfModule !deflate_module>
LoadModule deflate_module modules/mod_deflate.so
</IfModule>
<IfModule !expires_module>
LoadModule expires_module modules/mod_expires.so
</IfModule>
<IfModule !headers_module>
LoadModule headers_module modules/mod_headers.so
</IfModule>
<IfModule !mime_module>
LoadModule mime_module modules/mod_mime.so
</IfModule>
<IfModule !rewrite_module>
LoadModule rewrite_module modules/mod_rewrite.so
</IfModule>
ServerName freshrss.localhost
Listen 0.0.0.0:80
DocumentRoot /var/www/FreshRSS/p/
ErrorLog /dev/stderr
TransferLog /dev/stdout
AllowEncodedSlashes On
<Directory /var/www/FreshRSS/p>
AllowOverride AuthConfig FileInfo Indexes Limit
Require all granted
</Directory>

119
Docker/README.md Normal file
View File

@@ -0,0 +1,119 @@
# Deploy FreshRSS with Docker
* See also:
* https://hub.docker.com/r/freshrss/freshrss/
* https://cloud.docker.com/app/freshrss/repository/docker/freshrss/freshrss
## Install Docker
```sh
curl -fsSL https://get.docker.com/ -o get-docker.sh
sh get-docker.sh
```
## Optional: Build Docker image of FreshRSS
Optional, as a *less recent* online image can be automatically fetched during the next step (run),
but online images are not available for as many platforms as if you build yourself.
```sh
# First time only
git clone https://github.com/FreshRSS/FreshRSS.git
cd ./FreshRSS/
git pull
sudo docker pull alpine:3.7
sudo docker build --tag freshrss/freshrss -f Docker/Dockerfile .
```
## Run FreshRSS
Example using SQLite, and exposing FreshRSS on port 8080. You may have to adapt the network parameters to fit your needs.
```sh
# You can optionally run from the directory containing the FreshRSS source code:
cd ./FreshRSS/
# The data will be saved on the host in `./data/`
mkdir -p ./data/
sudo docker run -dit --restart unless-stopped --log-opt max-size=10m \
-v $(pwd)/data:/var/www/FreshRSS/data \
-p 8080:80 \
--name freshrss freshrss/freshrss
```
### Examples with external databases
You may want to use other link methods such as Docker bridges, and use Docker volumes for the data, but here are some simple examples:
#### MySQL
See https://hub.docker.com/_/mysql/
```sh
sudo docker run -d -v /path/to/mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=rootpass -e MYSQL_DATABASE=freshrss -e MYSQL_USER=freshrss -e MYSQL_PASSWORD=pass --name mysql mysql
sudo docker run -dit --restart unless-stopped --log-opt max-size=10m \
-v $(pwd)/data:/var/www/FreshRSS/data \
--link mysql -p 8080:80 \
--name freshrss freshrss/freshrss
```
#### PostgreSQL
See https://hub.docker.com/_/postgres/
```sh
sudo docker run -d -v /path/to/pgsql-data:/var/lib/postgresql/data -e POSTGRES_DB=freshrss -e POSTGRES_USER=freshrss -e POSTGRES_PASSWORD=pass --name postgres postgres
sudo docker run -dit --restart unless-stopped --log-opt max-size=10m \
-v $(pwd)/data:/var/www/FreshRSS/data \
--link postgres -p 8080:80 \
--name freshrss freshrss/freshrss
```
## Update
```sh
# Rebuild an image (see build section above) or get a new online version:
sudo docker pull freshrss/freshrss
# And then
sudo docker stop freshrss
sudo docker rename freshrss freshrss_old
# See the run section above for the full command
sudo docker run ...
# If everything is working, delete the old container
sudo docker rm freshrss_old
```
## Command line
```sh
sudo docker exec --user apache -it freshrss php ./cli/list-users.php
```
See the [CLI documentation](../cli/) for all the other commands.
### Cron job to refresh feeds
Set a cron job up on your host machine, calling the `actualize_script.php` inside the FreshRSS Docker instance.
#### Example on Debian / Ubuntu
Create `/etc/cron.d/FreshRSS` with:
```
7,37 * * * * root docker exec --user apache -it freshrss php ./app/actualize_script.php > /tmp/FreshRSS.log 2>&1
```
## Debugging
```sh
# See FreshRSS data (it is on the host)
cd ./data/
# See Web server logs
sudo docker logs -f freshrss
# Enter inside FreshRSS docker container
sudo docker exec -it freshrss sh
## See FreshRSS root inside the container
ls /var/www/FreshRSS/
```
## Deployment in production
Use a reverse proxy on your host server, such as [Træfik](https://traefik.io/) or [nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/),
with HTTPS, for instance using [Lets Encrypt](https://letsencrypt.org/).

View File

@@ -55,6 +55,8 @@ Nous sommes une communauté amicale.
7. Avec Apache, activer [`AllowEncodedSlashes`](https://httpd.apache.org/docs/trunk/mod/core.html#allowencodedslashes) pour une meilleure compatibilité avec les clients mobiles.
## Installation automatisée
* [Docker](./Docker/)
* [![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)
@@ -172,7 +174,7 @@ Voir le [dépôt dédié à ces extensions](https://github.com/FreshRSS/Extensio
* [password_compat](https://github.com/ircmaxell/password_compat)
# [Clients compatibles](https://freshrss.github.io/FreshRSS/en/users/06_Mobile_access.html)
# [Clients compatibles](https://freshrss.github.io/FreshRSS/fr/users/06_Mobile_access.html)
Tout client supportant une API de type Google Reader. Sélection :
* Android

View File

@@ -1,5 +1,6 @@
[![Build Status][travis-badge]][travis-link]
* Read this document on [github.com/FreshRSS/FreshRSS/](https://github.com/FreshRSS/FreshRSS/blob/master/README.md) to get the correct links and pictures.
* [Version française](README.fr.md)
# FreshRSS
@@ -46,7 +47,7 @@ We are a friendly community.
# Documentation
* https://freshrss.github.io/FreshRSS/en/
# [Installation](https://freshrss.github.io/FreshRSS/en/users/01_Installation.html)
# [Installation](https://freshrss.github.io/FreshRSS/en/admins/02_Installation.html)
1. Get FreshRSS with git or [by downloading the archive](https://github.com/FreshRSS/FreshRSS/archive/master.zip)
2. Dump the application on your server (expose only the `./p/` folder)
3. Add write access on `./data/` folder to the webserver user
@@ -59,7 +60,8 @@ We are a friendly community.
More information about installation and server configuration can be found in [our documentation](https://freshrss.github.io/FreshRSS/en/admins/02_Installation.html).
## Automated install
* [![Install on Cloudron](https://cloudron.io/img/button.svg)](https://cloudron.io/button.html?app=org.freshrss.cloudronapp)
* [Docker](./Docker/)
* [![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)

View File

@@ -169,6 +169,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
$nb_month_old = max(FreshRSS_Context::$user_conf->old_entries, 1);
$date_min = time() - (3600 * 24 * 30 * $nb_month_old);
$entryDAO = FreshRSS_Factory::createEntryDao();
$feedDAO = FreshRSS_Factory::createFeedDao();
$feeds = $feedDAO->listFeeds();
$nb_total = 0;
@@ -182,7 +183,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
}
if ($feed_history >= 0) {
$nb = $feedDAO->cleanOldEntries($feed->id(), $date_min, $feed_history);
$nb = $entryDAO->cleanOldEntries($feed->id(), $date_min, $feed_history);
if ($nb > 0) {
$nb_total += $nb;
Minz_Log::debug($nb . ' old entries cleaned in feed [' . $feed->url() . ']');

View File

@@ -408,7 +408,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
$entryDAO->beginTransaction();
}
$nb = $feedDAO->cleanOldEntries($feed->id(),
$nb = $entryDAO->cleanOldEntries($feed->id(),
$date_min,
max($feed_history, count($entries) + 10));
if ($nb > 0) {

View File

@@ -128,7 +128,7 @@ class FreshRSS extends Minz_FrontController {
}
header("X-Content-Type-Options: nosniff");
FreshRSS_Share::load(join_path(DATA_PATH, 'shares.php'));
FreshRSS_Share::load(join_path(APP_PATH, 'shares.php'));
self::loadStylesAndScripts();
}
}

View File

@@ -63,7 +63,6 @@ class FreshRSS_Auth {
$login_ok = $current_user != '';
if ($login_ok) {
Minz_Session::_param('currentUser', $current_user);
Minz_Session::_param('REMOTE_USER', $current_user);
}
return $login_ok;
case 'none':
@@ -102,6 +101,7 @@ class FreshRSS_Auth {
}
Minz_Session::_param('loginOk', self::$login_ok);
Minz_Session::_param('REMOTE_USER', httpAuthUser());
}
/**
@@ -133,6 +133,7 @@ class FreshRSS_Auth {
self::$login_ok = false;
Minz_Session::_param('loginOk');
Minz_Session::_param('csrf');
Minz_Session::_param('REMOTE_USER');
$system_conf = Minz_Configuration::get('system');
$username = '';

View File

@@ -560,6 +560,33 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
return $affected;
}
public function cleanOldEntries($id_feed, $date_min, $keep = 15) { //Remember to call updateCachedValue($id_feed) or updateCachedValues() just after
$sql = 'DELETE FROM `' . $this->prefix . 'entry` '
. 'WHERE id_feed=:id_feed AND id<=:id_max '
. 'AND is_favorite=0 ' //Do not remove favourites
. 'AND `lastSeen` < (SELECT maxLastSeen FROM (SELECT (MAX(e3.`lastSeen`)-99) AS maxLastSeen FROM `' . $this->prefix . 'entry` e3 WHERE e3.id_feed=:id_feed) recent) ' //Do not remove the most newly seen articles, plus a few seconds of tolerance
. 'AND id NOT IN (SELECT id FROM (SELECT e2.id FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=:id_feed ORDER BY id DESC LIMIT :keep) keep)'; //Double select: MySQL doesn't support 'LIMIT & IN/ALL/ANY/SOME subquery'
$stm = $this->bd->prepare($sql);
if ($stm) {
$id_max = intval($date_min) . '000000';
$stm->bindParam(':id_feed', $id_feed, PDO::PARAM_INT);
$stm->bindParam(':id_max', $id_max, PDO::PARAM_STR);
$stm->bindParam(':keep', $keep, PDO::PARAM_INT);
}
if ($stm && $stm->execute()) {
return $stm->rowCount();
} else {
$info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo();
if ($this->autoUpdateDb($info)) {
return $this->cleanOldEntries($id_feed, $date_min, $keep);
}
Minz_Log::error('SQL error cleanOldEntries: ' . $info[2]);
return false;
}
}
public function searchByGuid($id_feed, $guid) {
// un guid est unique pour un flux donné
$sql = 'SELECT id, guid, title, author, '

View File

@@ -355,7 +355,7 @@ class FreshRSS_Feed extends Minz_Model {
$this->id(),
$item->get_id(false, false),
$title === null ? '' : $title,
$author === null ? '' : html_only_entity_decode(strip_tags($author->name)),
$author === null ? '' : html_only_entity_decode(strip_tags($author->name == null ? $author->email : $author->name)),
$content === null ? '' : $content,
$link === null ? '' : $link,
$date ? $date : time()

View File

@@ -353,30 +353,6 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
return $affected;
}
public function cleanOldEntries($id, $date_min, $keep = 15) { //Remember to call updateCachedValue($id) or updateCachedValues() just after
$sql = 'DELETE FROM `' . $this->prefix . 'entry` '
. 'WHERE id_feed=:id_feed AND id<=:id_max '
. 'AND is_favorite=0 ' //Do not remove favourites
. 'AND `lastSeen` < (SELECT maxLastSeen FROM (SELECT (MAX(e3.`lastSeen`)-99) AS maxLastSeen FROM `' . $this->prefix . 'entry` e3 WHERE e3.id_feed=:id_feed) recent) ' //Do not remove the most newly seen articles, plus a few seconds of tolerance
. 'AND id NOT IN (SELECT id FROM (SELECT e2.id FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=:id_feed ORDER BY id DESC LIMIT :keep) keep)'; //Double select: MySQL doesn't support 'LIMIT & IN/ALL/ANY/SOME subquery'
$stm = $this->bd->prepare($sql);
if ($stm) {
$id_max = intval($date_min) . '000000';
$stm->bindParam(':id_feed', $id, PDO::PARAM_INT);
$stm->bindParam(':id_max', $id_max, PDO::PARAM_STR);
$stm->bindParam(':keep', $keep, PDO::PARAM_INT);
}
if ($stm && $stm->execute()) {
return $stm->rowCount();
} else {
$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
Minz_Log::error('SQL error cleanOldEntries: ' . $info[2]);
return false;
}
}
public static function daoToFeed($listDAO, $catID = null) {
$list = array();

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => 'Sdílení',
'add' => 'Add a sharing method', // TODO
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => 'Více informací',
'print' => 'Tisk',
'remove' => 'Remove sharing method', // TODO
'shaarli' => 'Shaarli',
'share_name' => 'Jméno pro zobrazení',
'share_url' => 'Jakou URL použít pro sdílení',

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => 'Teilen',
'add' => 'Add a sharing method', // TODO
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'E-Mail',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => 'Weitere Informationen',
'print' => 'Drucken',
'remove' => 'Remove sharing method', // TODO
'shaarli' => 'Shaarli',
'share_name' => 'Anzuzeigender Teilen-Name',
'share_url' => 'Zu verwendende Teilen-URL',

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => 'Sharing',
'add' => 'Add a sharing method',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => 'More information',
'print' => 'Print',
'remove' => 'Remove sharing method',
'shaarli' => 'Shaarli',
'share_name' => 'Share name to display',
'share_url' => 'Share URL to use',

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => 'Compartir',
'add' => 'Add a sharing method', // TODO
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => 'Más información',
'print' => 'Print',
'remove' => 'Remove sharing method', // TODO
'shaarli' => 'Shaarli',
'share_name' => 'Compartir nombre a mostrar',
'share_url' => 'Compatir URL a usar',

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => 'Partage',
'add' => 'Ajouter une méthode de partage',
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Courriel',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => 'Plus dinformations',
'print' => 'Print',
'remove' => 'Supprimer la méthode de partage',
'shaarli' => 'Shaarli',
'share_name' => 'Nom du partage à afficher',
'share_url' => 'URL du partage à utiliser',

View File

@@ -123,6 +123,7 @@ return array(
),
'sharing' => array(
'_' => 'שיתוף',
'add' => 'Add a sharing method', // TODO
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'דואר אלקטרוני',
@@ -130,6 +131,7 @@ return array(
'g+' => 'Google+',
'more_information' => 'מידע נוסף',
'print' => 'הדפסה',
'remove' => 'Remove sharing method', // TODO
'shaarli' => 'Shaarli',
'share_name' => 'שיתוף שם לתצוגה',
'share_url' => 'לשימוש שתפו URL',

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => 'Condivisione',
'add' => 'Add a sharing method', // TODO
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => 'Ulteriori informazioni',
'print' => 'Stampa',
'remove' => 'Remove sharing method', // TODO
'shaarli' => 'Shaarli',
'share_name' => 'Nome condivisione',
'share_url' => 'URL condivisione',

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => '공유',
'add' => 'Add a sharing method', // TODO
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => '메일',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => '자세한 정보',
'print' => '인쇄',
'remove' => 'Remove sharing method', // TODO
'shaarli' => 'Shaarli',
'share_name' => '표시할 이름',
'share_url' => '사용할 공유 URL',

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => 'Delen',
'add' => 'Add a sharing method', // TODO
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => 'Meer informatie',
'print' => 'Afdrukken',
'remove' => 'Remove sharing method', // TODO
'shaarli' => 'Shaarli',
'share_name' => 'Gedeelde naam om weer te geven',
'share_url' => 'Deel URL voor gebruik',

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => 'Compartilhando',
'add' => 'Add a sharing method', // TODO
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => 'Mais informação',
'print' => 'Imprimir',
'remove' => 'Remove sharing method', // TODO
'shaarli' => 'Shaarli',
'share_name' => 'Nome de visualização para compartilhar',
'share_url' => 'URL utilizada para compartilhar',

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => 'Sharing',
'add' => 'Add a sharing method', // TODO
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => 'More information',
'print' => 'Print',
'remove' => 'Remove sharing method', // TODO
'shaarli' => 'Shaarli',
'share_name' => 'Share name to display',
'share_url' => 'Share URL to use',

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => 'Paylaşım',
'add' => 'Add a sharing method', // TODO
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => 'Daha fazla bilgi',
'print' => 'Yazdır',
'remove' => 'Remove sharing method', // TODO
'shaarli' => 'Shaarli',
'share_name' => 'Paylaşım ismi',
'share_url' => 'Paylaşım URL si',

View File

@@ -126,6 +126,7 @@ return array(
),
'sharing' => array(
'_' => '分享',
'add' => 'Add a sharing method', // TODO
'blogotext' => 'Blogotext',
'diaspora' => 'Diaspora*',
'email' => 'Email',
@@ -133,6 +134,7 @@ return array(
'g+' => 'Google+',
'more_information' => '更多信息',
'print' => '打印',
'remove' => 'Remove sharing method', // TODO
'shaarli' => 'Shaarli',
'share_name' => '名称',
'share_url' => '地址',

View File

View File

@@ -14,7 +14,7 @@
<div class="stick">
<input type="text" id="share_##key##_name" name="share[##key##][name]" class="extend" value="" placeholder="<?php echo _t('conf.sharing.share_name'); ?>" size="64" />
<input type="url" id="share_##key##_url" name="share[##key##][url]" class="extend" value="" placeholder="<?php echo _t('conf.sharing.share_url'); ?>" size="64" />
<a href="#" class="remove btn btn-attention" data-remove="group-share-##key##"><?php echo _i('close'); ?></a></div>
<a href="#" class="remove btn btn-attention" data-remove="group-share-##key##" title="<?php echo _t('conf.sharing.remove'); ?>"><?php echo _i('close'); ?></a></div>
<a target="_blank" rel="noreferrer" class="btn" title="<?php echo _t('conf.sharing.more_information'); ?>" href="##help##"><?php echo _i('help'); ?></a>
</div></div>'>
<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
@@ -39,7 +39,7 @@
<?php } else { ?>
<input type="url" id="share_<?php echo $key; ?>_url" name="share[<?php echo $key; ?>][url]" class="extend" value="<?php echo $share->baseUrl(); ?>" placeholder="<?php echo _t('gen.short.not_applicable'); ?>" size="64" disabled/>
<?php } ?>
<a href='#' class='remove btn btn-attention' data-remove="group-share-<?php echo $key; ?>"><?php echo _i('close'); ?></a>
<a href='#' class='remove btn btn-attention' data-remove="group-share-<?php echo $key; ?>" title="<?php echo _t('conf.sharing.remove'); ?>"><?php echo _i('close'); ?></a>
</div>
<?php if ($share->formType() === 'advanced') { ?>
<a target="_blank" rel="noreferrer" class="btn" title="<?php echo _t('conf.sharing.more_information'); ?>" href="<?php echo $share->help(); ?>"><?php echo _i('help'); ?></a>
@@ -57,7 +57,7 @@
</option>
<?php } ?>
</select>
<a href='#' class='share add btn'><?php echo _i('add'); ?></a>
<a href='#' class='share add btn' title="<?php echo _t('conf.sharing.add'); ?>"><?php echo _i('add'); ?></a>
</div>
</div>

View File

@@ -32,6 +32,9 @@ Options in parenthesis are optional.
```sh
cd /usr/share/FreshRSS
./cli/prepare.php
# Ensure the needed directories in ./data/
./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)

View File

@@ -32,6 +32,7 @@ class I18nData {
* Add a new language. It's a copy of the reference language.
*
* @param string $language
* @throws Exception
*/
public function addLanguage($language) {
if (array_key_exists($language, $this->data)) {
@@ -45,6 +46,7 @@ class I18nData {
*
* @param string $key
* @param string $value
* @throws Exception
*/
public function addKey($key, $value) {
if (array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) {
@@ -53,10 +55,29 @@ class I18nData {
$this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)][$key] = $value;
}
/**
* Add a value for a key for the selected language.
*
* @param string $key
* @param string $value
* @param string $language
* @throws Exception
*/
public function addValue($key, $value, $language) {
if (!in_array($language, $this->getAvailableLanguages())) {
throw new Exception('The selected language does not exist.');
}
if (!array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) {
throw new Exception('The selected key does not exist for the selected language.');
}
$this->data[$language][$this->getFilenamePrefix($key)][$key] = $value;
}
/**
* Duplicate a key from the reference language to all other languages
*
* @param string $key
* @throws Exception
*/
public function duplicateKey($key) {
if (!array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) {
@@ -68,7 +89,7 @@ class I18nData {
continue;
}
if (array_key_exists($key, $this->data[$language][$this->getFilenamePrefix($key)])) {
throw new Exception(sprintf('The selected key already exist in %s.', $language));
continue;
}
$this->data[$language][$this->getFilenamePrefix($key)][$key] = $value;
}
@@ -78,6 +99,7 @@ class I18nData {
* Remove a key in all languages
*
* @param string $key
* @throws Exception
*/
public function removeKey($key) {
if (!array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) {

View File

@@ -36,8 +36,7 @@ class i18nFile {
}
foreach ($file as $name => $content) {
$filename = $dir . DIRECTORY_SEPARATOR . $name;
$fullContent = var_export($this->unflatten($content), true);
file_put_contents($filename, sprintf('<?php return %s;', $fullContent));
file_put_contents($filename, $this->format($content));
}
}
}
@@ -89,4 +88,32 @@ class i18nFile {
return $a;
}
/**
* Format an array of translation
*
* It takes an array of translation and format it to be dumped in a
* translation file. The array is first converted to a string then some
* formatting regexes are applied to match the original content.
*
* @param array $translation
* @return string
*/
private function format($translation) {
$translation = var_export($this->unflatten($translation), true);
$patterns = array(
'/array \(/',
'/=>\s*array/',
'/ {2}/',
);
$replacements = array(
'array(',
'=> array',
"\t", // Double quoting is mandatory to have a tab instead of the \t string
);
$translation = preg_replace($patterns, $replacements, $translation);
// Double quoting is mandatory to have new lines instead of \n strings
return sprintf("<?php\n\nreturn %s;\n", $translation);
}
}

View File

@@ -6,7 +6,7 @@ if (array_key_exists('h', $options)) {
help();
}
if (1 === $argc || 4 < $argc) {
if (1 === $argc || 5 < $argc) {
help();
}
@@ -25,12 +25,21 @@ switch ($argv[1]) {
}
$i18nData->addKey($argv[2], $argv[3]);
break;
case 'add_value':
if (4 === $argc) {
help();
}
$i18nData->addValue($argv[2], $argv[3], $argv[4]);
break;
case 'duplicate_key' :
$i18nData->duplicateKey($argv[2]);
break;
case 'delete_key' :
$i18nData->removeKey($argv[2]);
break;
case 'format' :
$i18nFile->dump($i18nData);
break;
default :
help();
}
@@ -48,7 +57,7 @@ NAME
%s
SYNOPSIS
php %s [OPTION] [OPERATION] [KEY] [VALUE]
php %s [OPTION] [OPERATION] [KEY] [VALUE] [LANGUAGE]
DESCRIPTION
Manipulate translation files. Available operations are
@@ -64,6 +73,10 @@ OPERATION
add_key add a new key in the referential. This operation needs a KEY and
a VALUE.
add_value
add a value in the referential. This operation needs a KEY, a
VALUE, and a LANGUAGE.
duplicate_key
duplicate a referential key in other languages. This operation
needs only a KEY.
@@ -72,6 +85,8 @@ OPERATION
delete a referential key from all languages. This operation needs
only a KEY.
format format i18n files.
HELP;
$file = str_replace(__DIR__ . '/', '', __FILE__);
echo sprintf($help, $file, $file);

37
cli/prepare.php Executable file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/php
<?php
require(__DIR__ . '/_cli.php');
$dirs = array(
'/',
'/cache',
'/extensions-data',
'/favicons',
'/PubSubHubbub',
'/PubSubHubbub/feeds',
'/PubSubHubbub/keys',
'/tokens',
'/users',
'/users/_',
);
$ok = true;
foreach ($dirs as $dir) {
@mkdir(DATA_PATH . $dir, 0770, true);
$ok &= touch(DATA_PATH . $dir . '/index.html');
}
if (!is_file(DATA_PATH . '/config.php')) {
$ok &= touch(DATA_PATH . '/do-install.txt');
}
file_put_contents(DATA_PATH . '/.htaccess',
"Order Allow,Deny\n" .
"Deny from all\n" .
"Satisfy all\n"
);
accessRights();
done($ok);

View File

@@ -2,7 +2,7 @@
//NB: Do not edit; use ./constants.local.php instead.
//<Not customisable>
define('FRESHRSS_VERSION', '1.10.0');
define('FRESHRSS_VERSION', '1.10.1');
define('FRESHRSS_WEBSITE', 'https://freshrss.org');
define('FRESHRSS_WIKI', 'https://freshrss.github.io/FreshRSS/');

View File

@@ -9,7 +9,7 @@ You need to verify that your server can run FreshRSS before installing it. If yo
| Web server | **Apache 2** | Nginx |
| PHP | **PHP 5.5+** | PHP 5.3.8+ |
| PHP modules | Required: libxml, cURL, PDO_MySQL, PCRE and ctype. \\ Required (32-bit only): GMP \\Recommanded: JSON, Zlib, mbstring, iconv, ZipArchive | |
| Database | **MySQL 5.0.3+** | SQLite 3.7.4+ |
| Database | **MySQL 5.5.3+** | SQLite 3.7.4+ |
| Browser | **Firefox** | Chrome, Opera, Safari, or IE11+ |
## Important notice

View File

@@ -33,3 +33,14 @@ Here is a list of feeds which don't work:
* http://foulab.org/fr/rss/Foulab_News: is not a W3C valid feed (November 2014)
* http://eu.battle.net/hearthstone/fr/feed/news: is not a W3C valid feed (Novembre 2014)
* http://webseriesmag.blogs.liberation.fr/we/atom.xml: is not working for the user but succeed on all the described validations (November 2014)
## How to change a forgotten password?
Since [1.10.0](https://github.com/FreshRSS/FreshRSS/releases/tag/1.10.0) release, admins are able to change user passwords directly from the interface. This interface is available under ```Administration → Manage users```.
Select a user, enter a password, and validate.
Since [1.8.0](https://github.com/FreshRSS/FreshRSS/releases/tag/1.8.0) release, admins are able to change user passwords using a terminal. It worth mentioning that it must have access to PHP CLI. Open a terminal, and type the following command:
```sh
./cli/update_user.php --user <username> --password <password>
```
For more information on that matter, there is a [dedicated documentation](../../cli/README.md).

View File

@@ -9,7 +9,7 @@ Il est toutefois de votre responsabilité de vérifier que votre hébergement pe
| Serveur web | **Apache 2** | Nginx |
| PHP | **PHP 5.5+** | PHP 5.3.8+ |
| Modules PHP | Requis : libxml, cURL, PDO_MySQL, PCRE et ctype \\ Requis (32 bits seulement) : GMP \\ Recommandé : JSON, Zlib, mbstring et iconv, ZipArchive | |
| Base de données | **MySQL 5.0.3+** | SQLite 3.7.4+ |
| Base de données | **MySQL 5.5.3+** | SQLite 3.7.4+ |
| Navigateur | **Firefox** | Chrome, Opera, Safari, or IE 11+ |
## Note importante

View File

@@ -33,3 +33,14 @@ Voici une liste des flux qui ne fonctionnent pas :
* http://foulab.org/fr/rss/Foulab_News : ne passe pas la validation W3C (novembre 2014)
* http://eu.battle.net/hearthstone/fr/feed/news : ne passe pas la validation W3C (novembre 2014)
* http://webseriesmag.blogs.liberation.fr/we/atom.xml : ne fonctionne pas chez l'utilisateur mais passe l'ensemble des validations ci-dessus (novembre 2014)
## Comment changer un mot de passe oublié ?
Depuis la version [1.10.0](https://github.com/FreshRSS/FreshRSS/releases/tag/1.10.0), l'administrateur peut modifier le mot de passe d'un utilisateur depuis l'interface. Cette interface est disponible dans le menu ```Administration → Gestion des utilisateurs```.
Il suffit de sélectionner l'utilisateur, de saisir un mot de passe et de valider.
Depuis la version [1.8.0](https://github.com/FreshRSS/FreshRSS/releases/tag/1.8.0), l'administrateur peut modifier le mot de passe d'un utilisateur depuis un terminal. Il est bon de noter que celui-ci doit avoir un accès à PHP en ligne de commande. Pour cela, il suffit d'ouvrir son terminal et de saisir la commande suivante :
```sh
./cli/update_user.php --user <username> --password <password>
```
Pour plus d'information à ce sujet, il existe la [documentation dédiée](../../cli/README.md).

View File

@@ -106,7 +106,8 @@ class Minz_Request {
$https = self::isHttps();
if (!empty($_SERVER['HTTP_HOST'])) {
$host = $_SERVER['HTTP_HOST'];
//Might contain a port number, and mind IPv6 addresses
$host = parse_url('http://' . $_SERVER['HTTP_HOST'], PHP_URL_HOST);
} elseif (!empty($_SERVER['SERVER_NAME'])) {
$host = $_SERVER['SERVER_NAME'];
} else {

View File

@@ -445,6 +445,9 @@ function unreadCount() { //http://blog.martindoms.com/2009/10/16/using-the-googl
}
function entriesToArray($entries) {
$feedDAO = FreshRSS_Factory::createFeedDao();
$arrayFeedCategoryNames = $feedDAO->arrayFeedCategoryNames();
$items = array();
foreach ($entries as $entry) {
$f_id = $entry->feed();
@@ -494,9 +497,6 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex
//http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#feed
header('Content-Type: application/json; charset=UTF-8');
$feedDAO = FreshRSS_Factory::createFeedDao();
$arrayFeedCategoryNames = $feedDAO->arrayFeedCategoryNames();
switch ($path) {
case 'reading-list':
$type = 'A';