diff --git a/.jshintignore b/.jshintignore
new file mode 100644
index 000000000..80bbc5b89
--- /dev/null
+++ b/.jshintignore
@@ -0,0 +1,4 @@
+node_modules
+p/scripts/bcrypt.min.js
+p/scripts/flotr2.min.js
+p/scripts/jquery.min.js
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 000000000..07d282b1c
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,8 @@
+{
+ "esversion" : 6,
+ "browser" : true,
+ "globals": {
+ "confirm": true,
+ "console": true
+ }
+}
diff --git a/.travis.yml b/.travis.yml
index 44bc59e3f..dbd4eb8f6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,15 +1,16 @@
language: php
php:
- - '5.4'
- - '5.5'
- - '5.6'
- - '7.0'
- - '7.1'
- - '7.2'
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7.0
+ - 7.1
+ - 7.2
+ - 7.3
install:
# newest version without https://github.com/squizlabs/PHP_CodeSniffer/pull/1404
- - composer global require squizlabs/php_codesniffer "<=3.0.0RC4"
+ - composer global require squizlabs/php_codesniffer
script:
- phpenv rehash
@@ -34,6 +35,15 @@ matrix:
dist: precise
- php: "7.2"
env: CHECK_TRANSLATION=yes VALIDATE_STANDARD=no
+ - language: node_js
+ node_js:
+ - "node"
+ php:
+ # none
+ install:
+ - npm install jshint
+ script:
+ - node_modules/jshint/bin/jshint .
allow_failures:
- env: CHECK_TRANSLATION=yes VALIDATE_STANDARD=no
- dist: precise
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f43d470a8..d0b7a2f23 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,32 @@
# FreshRSS changelog
+## 2019-04-07 FreshRSS 1.14.1
+
+* Bug fixing (regressions introduced in 1.14.0)
+ * Fix *load more articles* when using ascending order [#2314](https://github.com/FreshRSS/FreshRSS/issues/2314)
+ * Fix cron in the Ubuntu flavour of the Docker image [#2319](https://github.com/FreshRSS/FreshRSS/issues/2319)
+ * Fix the use of arrow keyboard keys for shortcuts [#2316](https://github.com/FreshRSS/FreshRSS/issues/2316)
+ * Fix control+click or middle-click for opening articles in a background tab [#2310](https://github.com/FreshRSS/FreshRSS/issues/2310)
+ * Fix the naming of the option to unfold categories [#2307](https://github.com/FreshRSS/FreshRSS/issues/2307)
+ * Fix shortcut problem when using unfolded articles [#2328](https://github.com/FreshRSS/FreshRSS/issues/2328)
+ * Fix auto-hiding articles [#2323](https://github.com/FreshRSS/FreshRSS/issues/2323)
+ * Fix scroll functions with Edge [#2337](https://github.com/FreshRSS/FreshRSS/pull/2337)
+ * Fix drop-down menu warning [#2353](https://github.com/FreshRSS/FreshRSS/pull/2353)
+ * Fix delay for individual mark-as-read actions [#2332](https://github.com/FreshRSS/FreshRSS/issues/2332)
+ * Fix scroll functions in Edge [#2337](https://github.com/FreshRSS/FreshRSS/pull/2337)
+* Bug fixing (misc.)
+ * Fix extensions in Windows [#994](https://github.com/FreshRSS/FreshRSS/issues/994)
+ * Fix import of empty articles [#2351](https://github.com/FreshRSS/FreshRSS/pull/2351)
+ * Fix quote escaping on CLI i18n tools [#2355](https://github.com/FreshRSS/FreshRSS/pull/2355)
+* UI
+ * Better handling of bad Ajax requests and fast page unload (ask confirmation) [#2346](https://github.com/FreshRSS/FreshRSS/pull/2346)
+* I18n
+ * Improve Dutch [#2312](https://github.com/FreshRSS/FreshRSS/pull/2312)
+* Misc.
+ * Check JavaScript (jshint) in Travis continuous integration [#2315](https://github.com/FreshRSS/FreshRSS/pull/2315)
+ * Add PHP 7.3 to Travis [#2317](https://github.com/FreshRSS/FreshRSS/pull/2317)
+
+
## 2019-03-31 FreshRSS 1.14.0
* Features
@@ -31,7 +58,7 @@
* API
* Supported by [Readably](https://play.google.com/store/apps/details?id=com.isaiasmatewos.readably) (client for Android using Fever API)
* I18n
- * Improved Korean [#2242](https://github.com/FreshRSS/FreshRSS/pull/2242)
+ * Improve Korean [#2242](https://github.com/FreshRSS/FreshRSS/pull/2242)
* Improve Occitan [#2253](https://github.com/FreshRSS/FreshRSS/pull/2253)
* Security
* Reworked the CSRF token interaction with the session in some edge cases [#2290](https://github.com/FreshRSS/FreshRSS/pull/2290)
diff --git a/Docker/Dockerfile b/Docker/Dockerfile
index 56623b001..661543724 100644
--- a/Docker/Dockerfile
+++ b/Docker/Dockerfile
@@ -11,7 +11,7 @@ RUN apt update && \
php-sqlite3 php-mysql php-pgsql && \
rm -rf /var/lib/apt/lists/
-RUN mkdir -p /var/www/FreshRSS /run/apache2/ /run/php/
+RUN mkdir -p /var/www/FreshRSS /run/apache2/
WORKDIR /var/www/FreshRSS
COPY . /var/www/FreshRSS
@@ -25,8 +25,10 @@ RUN a2dismod -f alias autoindex negotiation status && \
RUN sed -r -i "/^\s*(CustomLog|ErrorLog|Listen) /s/^/#/" /etc/apache2/apache2.conf && \
sed -r -i "/^\s*Listen /s/^/#/" /etc/apache2/ports.conf && \
- echo "17,37 su apache -s /bin/sh -c 'php /var/www/FreshRSS/app/actualize_script.php' 2>> /proc/1/fd/2 > /tmp/FreshRSS.log" >> \
- /var/spool/cron/crontabs/root
+ touch /var/www/FreshRSS/Docker/env.txt && \
+ echo "17,47 * * * * . /var/www/FreshRSS/Docker/env.txt; \
+ su www-data -s /bin/sh -c 'php /var/www/FreshRSS/app/actualize_script.php' \
+ 2>> /proc/1/fd/2 > /tmp/FreshRSS.log" | crontab -
ENV COPY_SYSLOG_TO_STDERR On
ENV CRON_MIN ''
diff --git a/Docker/Dockerfile-Alpine b/Docker/Dockerfile-Alpine
index cd0f521a0..589d266e5 100644
--- a/Docker/Dockerfile-Alpine
+++ b/Docker/Dockerfile-Alpine
@@ -16,14 +16,16 @@ COPY ./Docker/*.Apache.conf /etc/apache2/conf.d/
RUN rm -f /etc/apache2/conf.d/languages.conf /etc/apache2/conf.d/info.conf \
/etc/apache2/conf.d/status.conf /etc/apache2/conf.d/userdir.conf && \
- sed -r -i "/^\s*LoadModule .*mod_(alias|autoindex|negotiation|status).so$/s/^/#/" \
+ sed -r -i "/^\s*LoadModule .*mod_(alias|autoindex|negotiation|status).so$/s/^/#/" \
/etc/apache2/httpd.conf && \
- sed -r -i "/^\s*#\s*LoadModule .*mod_(deflate|expires|headers|mime|setenvif).so$/s/^\s*#//" \
+ sed -r -i "/^\s*#\s*LoadModule .*mod_(deflate|expires|headers|mime|setenvif).so$/s/^\s*#//" \
/etc/apache2/httpd.conf && \
sed -r -i "/^\s*(CustomLog|ErrorLog|Listen) /s/^/#/" \
/etc/apache2/httpd.conf && \
- echo "17,37 * * * * su apache -s /bin/sh -c 'php /var/www/FreshRSS/app/actualize_script.php' 2>> /proc/1/fd/2 > /tmp/FreshRSS.log" >> \
- /var/spool/cron/crontabs/root
+ touch /var/www/FreshRSS/Docker/env.txt && \
+ echo "27,57 * * * * . /var/www/FreshRSS/Docker/env.txt; \
+ su apache -s /bin/sh -c 'php /var/www/FreshRSS/app/actualize_script.php' \
+ 2>> /proc/1/fd/2 > /tmp/FreshRSS.log" | crontab -
ENV COPY_SYSLOG_TO_STDERR On
ENV CRON_MIN ''
diff --git a/Docker/README.md b/Docker/README.md
index 3cfb05c69..9bf20c8c2 100644
--- a/Docker/README.md
+++ b/Docker/README.md
@@ -211,12 +211,23 @@ For advanced users. Offers good logging and monitoring with auto-restart on fail
Watch out to use the same run parameters than in your main FreshRSS instance, for database, networking, and file system.
See cron option 1 for customising the cron schedule.
+#### For the Ubuntu image (default)
```sh
sudo docker run -d --restart unless-stopped --log-opt max-size=10m \
-v freshrss-data:/var/www/FreshRSS/data \
- -e 'CRON_MIN=17,37' \
+ -e 'CRON_MIN=17,47' \
--net freshrss-network \
--name freshrss_cron freshrss/freshrss \
+ cron
+```
+
+#### For the Alpine image
+```sh
+sudo docker run -d --restart unless-stopped --log-opt max-size=10m \
+ -v freshrss-data:/var/www/FreshRSS/data \
+ -e 'CRON_MIN=27,57' \
+ --net freshrss-network \
+ --name freshrss_cron freshrss/freshrss:alpine \
crond -f -d 6
```
diff --git a/Docker/entrypoint.sh b/Docker/entrypoint.sh
index 528388073..b7a961569 100755
--- a/Docker/entrypoint.sh
+++ b/Docker/entrypoint.sh
@@ -8,7 +8,8 @@ chmod -R g+r . && chmod -R g+w ./data/
find /etc/php*/ -name php.ini -exec sed -r -i "\#^;?date.timezone#s#^.*#date.timezone = $TZ#" {} \;
if [ -n "$CRON_MIN" ]; then
- sed -r -i "\#FreshRSS#s#^[^ ]+ #$CRON_MIN #" /var/spool/cron/crontabs/root
+ (echo "export TZ=$TZ" ; echo "export COPY_SYSLOG_TO_STDERR=$COPY_SYSLOG_TO_STDERR") > /var/www/FreshRSS/Docker/env.txt
+ crontab -l | sed -r "\#FreshRSS#s#^[^ ]+ #$CRON_MIN #" | crontab -
fi
exec "$@"
diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php
index 9c3900f39..16dd82121 100755
--- a/app/Controllers/configureController.php
+++ b/app/Controllers/configureController.php
@@ -166,30 +166,16 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
* tab and up.
*/
public function shortcutAction() {
- $list_keys = array('a', 'b', 'backspace', 'c', 'd', 'delete', 'down', 'e', 'end', 'enter',
- 'escape', 'f', 'g', 'h', 'home', 'i', 'insert', 'j', 'k', 'l', 'left',
- 'm', 'n', 'o', 'p', 'page_down', 'page_up', 'q', 'r', 'return', 'right',
- 's', 'space', 't', 'tab', 'u', 'up', 'v', 'w', 'x', 'y',
- 'z', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9',
- 'f10', 'f11', 'f12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0');
- $this->view->list_keys = $list_keys;
+ $this->view->list_keys = SHORTCUT_KEYS;
if (Minz_Request::isPost()) {
- $shortcuts = Minz_Request::param('shortcuts');
- $shortcuts_ok = array();
-
- foreach ($shortcuts as $key => $value) {
- if (in_array($value, $list_keys)) {
- $shortcuts_ok[$key] = $value;
- }
- }
-
- FreshRSS_Context::$user_conf->shortcuts = $shortcuts_ok;
+ FreshRSS_Context::$user_conf->shortcuts = validateShortcutList(Minz_Request::param('shortcuts'));
FreshRSS_Context::$user_conf->save();
invalidateHttpCache();
- Minz_Request::good(_t('feedback.conf.shortcuts_updated'),
- array('c' => 'configure', 'a' => 'shortcut'));
+ Minz_Request::good(_t('feedback.conf.shortcuts_updated'), array('c' => 'configure', 'a' => 'shortcut'));
+ } else {
+ FreshRSS_Context::$user_conf->shortcuts = validateShortcutList(FreshRSS_Context::$user_conf->shortcuts);
}
Minz_View::prependTitle(_t('conf.shortcut.title') . ' · ');
diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php
index 93d1183c9..b47cd55ad 100644
--- a/app/Models/EntryDAO.php
+++ b/app/Models/EntryDAO.php
@@ -350,7 +350,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
$sql .= $hasWhere ? ' AND' : ' WHERE';
$hasWhere = true;
$sql .= ' f.id=?';
- $values[] = $id;
+ $values[] = $feedId;
}
if ($catId !== false) {
$sql .= $hasWhere ? ' AND' : ' WHERE';
diff --git a/app/i18n/cz/conf.php b/app/i18n/cz/conf.php
index d0203c252..a2618e310 100644
--- a/app/i18n/cz/conf.php
+++ b/app/i18n/cz/conf.php
@@ -92,7 +92,7 @@ return array(
'auto_remove_article' => 'Po přečtení články schovat',
'confirm_enabled' => 'Vyžadovat potvrzení pro akci “označit vše jako přečtené”',
'display_articles_unfolded' => 'Ve výchozím stavu zobrazovat články otevřené',
- 'display_categories_unfolded' => 'Ve výchozím stavu zobrazovat kategorie zavřené',
+ 'display_categories_unfolded' => 'Ve výchozím stavu zobrazovat kategorie otevřené',
'hide_read_feeds' => 'Schovat kategorie a kanály s nulovým počtem nepřečtených článků (nefunguje s nastavením “Zobrazit všechny články”)',
'img_with_lazyload' => 'Použít "lazy load" mód pro načítaní obrázků',
'jump_next' => 'skočit na další nepřečtený (kanál nebo kategorii)',
diff --git a/app/i18n/de/conf.php b/app/i18n/de/conf.php
index e0beb1ac7..40209576e 100644
--- a/app/i18n/de/conf.php
+++ b/app/i18n/de/conf.php
@@ -92,7 +92,7 @@ return array(
'auto_remove_article' => 'Artikel nach dem Lesen verstecken',
'confirm_enabled' => 'Bei der Aktion „Alle als gelesen markieren“ einen Bestätigungsdialog anzeigen',
'display_articles_unfolded' => 'Artikel standardmäßig ausgeklappt zeigen',
- 'display_categories_unfolded' => 'Kategorien standardmäßig eingeklappt zeigen',
+ 'display_categories_unfolded' => 'Kategorien standardmäßig ausgeklappt zeigen',
'hide_read_feeds' => 'Kategorien & Feeds ohne ungelesene Artikel verstecken (funktioniert nicht mit der Einstellung „Alle Artikel zeigen“)',
'img_with_lazyload' => 'Verwende die "träges Laden"-Methode zum Laden von Bildern',
'jump_next' => 'springe zum nächsten ungelesenen Geschwisterelement (Feed oder Kategorie)',
diff --git a/app/i18n/fr/conf.php b/app/i18n/fr/conf.php
index d0d146c89..ef29a360e 100644
--- a/app/i18n/fr/conf.php
+++ b/app/i18n/fr/conf.php
@@ -92,7 +92,7 @@ return array(
'auto_remove_article' => 'Cacher les articles après lecture',
'confirm_enabled' => 'Afficher une confirmation lors des actions “marquer tout comme lu”',
'display_articles_unfolded' => 'Afficher les articles dépliés par défaut',
- 'display_categories_unfolded' => 'Afficher les catégories pliées par défaut',
+ 'display_categories_unfolded' => 'Afficher les catégories dépliées par défaut',
'hide_read_feeds' => 'Cacher les catégories & flux sans article non-lu (ne fonctionne pas avec la configuration “Afficher tous les articles”)',
'img_with_lazyload' => 'Utiliser le mode “chargement différé” pour les images',
'jump_next' => 'sauter au prochain voisin non lu (flux ou catégorie)',
diff --git a/app/i18n/he/conf.php b/app/i18n/he/conf.php
index 2f699bcf2..1eb447911 100644
--- a/app/i18n/he/conf.php
+++ b/app/i18n/he/conf.php
@@ -92,7 +92,7 @@ return array(
'auto_remove_article' => 'Hide articles after reading', //TODO - Translation
'confirm_enabled' => 'הצגת דו-שיח לאישור “סימון הכל כנקרא” ',
'display_articles_unfolded' => 'הצגת מאמרים בשלמותם כברירת מחדל',
- 'display_categories_unfolded' => 'הצגת קטגוריות מקופלות כברירת מחדל',
+ 'display_categories_unfolded' => 'הצגת קטגוריות בשלמותן כברירת מחדל',
'hide_read_feeds' => 'הסתרת קטגוריות & הזנות ללא מאמרים שלא נקראו (לא עובד יחד עם “הצגת כל המאמרים”)',
'img_with_lazyload' => 'שימוש ב "טעינה עצלה" על מנת לטעון תמונות',
'jump_next' => 'קפיצה לפריט הבא שלא נקרא (הזנה או קטגוריה)',
diff --git a/app/i18n/kr/conf.php b/app/i18n/kr/conf.php
index 11a8494c5..acd4c40c1 100644
--- a/app/i18n/kr/conf.php
+++ b/app/i18n/kr/conf.php
@@ -92,7 +92,7 @@ return array(
'auto_remove_article' => '글을 읽은 후 숨기기',
'confirm_enabled' => '“모두 읽음으로 표시” 실행시 확인 창 표시',
'display_articles_unfolded' => '글을 펼쳐진 상태로 보여주기',
- 'display_categories_unfolded' => '카테고리를 접힌 상태로 보여주기',
+ 'display_categories_unfolded' => '카테고리를 펼친 상태로 보여주기',
'hide_read_feeds' => '읽지 않은 글이 없는 카테고리와 피드 감추기 (“모든 글 표시”가 설정된 경우 동작하지 않습니다)',
'img_with_lazyload' => '그림을 불러오는 데에 "lazy load" 모드 사용하기',
'jump_next' => '다음 읽지 않은 항목으로 이동 (피드 또는 카테고리)',
diff --git a/app/i18n/nl/admin.php b/app/i18n/nl/admin.php
index c6fd1dc9e..e5d126eb8 100644
--- a/app/i18n/nl/admin.php
+++ b/app/i18n/nl/admin.php
@@ -160,8 +160,8 @@ return array(
'_' => 'Systeem configuratie',
'auto-update-url' => 'Automatische update server URL',
'instance-name' => 'Voorbeeld naam',
- 'max-categories' => 'Categoriën limiet per gebruiker',
- 'max-feeds' => 'Feed limiet per gebruiker',
+ 'max-categories' => 'Categorielimiet per gebruiker',
+ 'max-feeds' => 'Feedlimiet per gebruiker',
'cookie-duration' => array(
'help' => 'in seconden',
'number' => 'Tijdsduur om ingelogd te blijven',
diff --git a/app/i18n/nl/conf.php b/app/i18n/nl/conf.php
index 8e6a59d56..fa84ae184 100644
--- a/app/i18n/nl/conf.php
+++ b/app/i18n/nl/conf.php
@@ -91,18 +91,18 @@ return array(
'auto_load_more' => 'Laad volgende artikel onderaan de pagina',
'auto_remove_article' => 'Verberg artikel na lezen',
'confirm_enabled' => 'Toon een bevestigings dialoog op “markeer alles als gelezen” acties',
- 'display_articles_unfolded' => 'Toon artikelen uitgeklapt als standaard',
- 'display_categories_unfolded' => 'Toon categoriën ingeklapt als standaard',
- 'hide_read_feeds' => 'Verberg categoriën en feeds zonder ongelezen artikelen (werkt niet met “Toon alle artikelen” configuratie)',
+ 'display_articles_unfolded' => 'Artikelen standaard uitklappen',
+ 'display_categories_unfolded' => 'Categorieën standaard uitklappen',
+ 'hide_read_feeds' => 'Categorieën en feeds zonder ongelezen artikelen verbergen (werkt niet met “Toon alle artikelen” configuratie)',
'img_with_lazyload' => 'Gebruik "lazy load" methode om afbeeldingen te laden',
'jump_next' => 'Ga naar volgende ongelezen (feed of categorie)',
'mark_updated_article_unread' => 'Markeer vernieuwd artikel als ongelezen',
'number_divided_when_reader' => 'Gedeeld door 2 in de lees modus.',
'read' => array(
- 'article_open_on_website' => 'Als het artikel is geopend op de originele website',
- 'article_viewed' => 'Als het artikel is bekeken',
- 'scroll' => 'Tijdens scrollen',
- 'upon_reception' => 'Tijdens ontvangst van het artikel',
+ 'article_open_on_website' => 'als het artikel wordt geopend op de originele website',
+ 'article_viewed' => 'als het artikel wordt bekeken',
+ 'scroll' => 'tijdens het scrollen',
+ 'upon_reception' => 'bij ontvangst van het artikel',
'when' => 'Markeer artikel als gelezen…',
),
'show' => array(
@@ -145,8 +145,8 @@ return array(
'wallabag' => 'wallabag',
),
'shortcut' => array(
- '_' => 'Shortcuts',
- 'article_action' => 'Artikel acties',
+ '_' => 'Snelkoppelingen',
+ 'article_action' => 'Artikelacties',
'auto_share' => 'Delen',
'auto_share_help' => 'Als er slechts één deelmethode is, dan wordt die gebruikt. Anders zijn ze toegankelijk met hun nummer.',
'close_dropdown' => 'Sluit menu',
@@ -161,8 +161,8 @@ return array(
'mark_favorite' => 'Markeer als favoriet',
'mark_read' => 'Markeer als gelezen',
'navigation' => 'Navigatie',
- 'navigation_help' => 'Met de "Shift" toets, kunt u navigatie verwijzingen voor feeds gebruiken.
Met de "Alt" toets, kunt u navigatie verwijzingen voor categoriën gebruiken.',
- 'navigation_no_mod_help' => 'De volgende navigatiesnelkoppelingen ondersteunen geen besturingstoetsen.',
+ 'navigation_help' => 'Met de "Shift" toets worden navigatieverwijzingen op feeds toegepast.
Met de "Alt" toets worden navigatieverwijzingen op categorieën toegepast.',
+ 'navigation_no_mod_help' => 'De volgende navigatiesnelkoppelingen ondersteunen geen toetsencombinaties.',
'next_article' => 'Spring naar volgende artikel',
'normal_view' => 'Schakel naar gewoon aanzicht',
'other_action' => 'Andere acties',
diff --git a/app/i18n/nl/feedback.php b/app/i18n/nl/feedback.php
index 07ac7e89d..25378360b 100644
--- a/app/i18n/nl/feedback.php
+++ b/app/i18n/nl/feedback.php
@@ -70,15 +70,15 @@ return array(
'no_name' => 'Categorie naam mag niet leeg zijn.',
'not_delete_default' => 'U kunt de standaard categorie niet verwijderen!',
'not_exist' => 'De categorie bestaat niet!',
- 'over_max' => 'U hebt het maximale aantal categoriën bereikt (%d)',
- 'updated' => 'Categorie is vernieuwd.',
+ 'over_max' => 'Maximum aantal categorieën bereikt (%d)',
+ 'updated' => 'Categorie vernieuwd.',
),
'feed' => array(
- 'actualized' => '%s is vernieuwd',
- 'actualizeds' => 'RSS feeds zijn vernieuwd',
- 'added' => 'RSS feed %s is toegevoegd',
- 'already_subscribed' => 'U bent al geabonneerd op %s',
- 'deleted' => 'Feed is verwijderd',
+ 'actualized' => '%s vernieuwd',
+ 'actualizeds' => 'RSS feeds vernieuwd',
+ 'added' => 'RSS feed %s toegevoegd',
+ 'already_subscribed' => 'Al geabonneerd op %s',
+ 'deleted' => 'Feed verwijderd',
'error' => 'Feed kan niet worden vernieuwd',
'internal_problem' => 'De feed kon niet worden toegevoegd. Controleer de FreshRSS-logbestanden voor details. Toevoegen forceren kan worden geprobeerd door #force_feed
aan de URL toe te voegen.',
'invalid_url' => 'URL %s is ongeldig',
@@ -86,7 +86,7 @@ return array(
'n_entries_deleted' => '%d artikelen zijn verwijderd',
'no_refresh' => 'Er is geen feed om te vernieuwen…',
'not_added' => '%s kon niet worden toegevoegd',
- 'over_max' => 'U hebt het maximale aantal feeds bereikt(%d)',
+ 'over_max' => 'Maximum aantal feeds bereikt (%d)',
'updated' => 'Feed is vernieuwd',
),
'purge_completed' => 'Opschonen klaar (%d artikelen verwijderd)',
diff --git a/app/i18n/oc/conf.php b/app/i18n/oc/conf.php
index ad52691da..3ac573ca7 100644
--- a/app/i18n/oc/conf.php
+++ b/app/i18n/oc/conf.php
@@ -92,7 +92,7 @@ return array(
'auto_remove_article' => 'Rescondre los articles aprèp lectura',
'confirm_enabled' => 'Mostrar una confirmacion per las accions del tipe « o marcar tot coma legit »',
'display_articles_unfolded' => 'Mostrar los articles desplegats per defaut',
- 'display_categories_unfolded' => 'Mostrar las categorias plegadas per defaut',
+ 'display_categories_unfolded' => 'Mostrar las categorias desplegats per defaut',
'hide_read_feeds' => 'Rescondre las categorias & fluxes sens articles pas legits (fonciona pas amb la configuracion « Mostrar totes los articles »)',
'img_with_lazyload' => 'Utilizar lo mòde “cargament tardiu” pels imatges',
'jump_next' => 'sautar al vesin venent pas legit (flux o categoria)',
diff --git a/app/i18n/pt-br/conf.php b/app/i18n/pt-br/conf.php
index ca365db5b..8f5eb7746 100644
--- a/app/i18n/pt-br/conf.php
+++ b/app/i18n/pt-br/conf.php
@@ -92,7 +92,7 @@ return array(
'auto_remove_article' => 'Esconder artigos depois de lidos',
'confirm_enabled' => 'Exibir uma caixa de diálogo de confirmação quando acionar "marcar todos como lido"',
'display_articles_unfolded' => 'Mostrar aritogs abertos por padrão',
- 'display_categories_unfolded' => 'Mostrar artigos fechados por padrão',
+ 'display_categories_unfolded' => 'Mostrar artigos abertos por padrão',
'hide_read_feeds' => 'Esconder categorias e feeds com nenhum artigo não lido (não funciona com a configuração "Mostrar todos os artigos”)',
'img_with_lazyload' => 'Utilizar o modo "lazy load" para carregar as imagens',
'jump_next' => 'Vá para o próximo irmão não lido (feed ou categoria)',
diff --git a/app/i18n/zh-cn/conf.php b/app/i18n/zh-cn/conf.php
index 535dfd358..216e4590a 100644
--- a/app/i18n/zh-cn/conf.php
+++ b/app/i18n/zh-cn/conf.php
@@ -91,8 +91,8 @@ return array(
'auto_load_more' => '在页面底部载入下一篇文章',
'auto_remove_article' => '阅读后隐藏文章',
'confirm_enabled' => '“全部设为已读”时显示确认对话框',
- 'display_articles_unfolded' => '默认展开文章',
- 'display_categories_unfolded' => '默认展开分类',
+ 'display_articles_unfolded' => '默认展开显示文章',
+ 'display_categories_unfolded' => '默认展开显示类别',
'hide_read_feeds' => '隐藏没有未读文章的分类或 RSS 源 (启用“显示所有文章”时不生效))',
'img_with_lazyload' => '延迟加载图片',
'jump_next' => '跳转到下一未读项 (RSS 源或分类)',
diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml
index b62263ecf..52489c5c3 100644
--- a/app/views/helpers/javascript_vars.phtml
+++ b/app/views/helpers/javascript_vars.phtml
@@ -1,6 +1,6 @@
mark_when;
-$s = FreshRSS_Context::$user_conf->shortcuts;
+$s = validateShortcutList(FreshRSS_Context::$user_conf->shortcuts);
echo htmlspecialchars(json_encode(array(
'context' => array(
'anonymous' => !FreshRSS_Auth::hasAccess(),
diff --git a/cli/i18n/I18nFile.php b/cli/i18n/I18nFile.php
index bdcf3c079..56459ce8b 100644
--- a/cli/i18n/I18nFile.php
+++ b/cli/i18n/I18nFile.php
@@ -84,7 +84,7 @@ class I18nFile implements I18nFileInterface{
foreach ($translation as $compoundKey => $value) {
$keys = explode('.', $compoundKey);
array_shift($keys);
- eval("\$a['" . implode("']['", $keys) . "'] = '" . $value . "';");
+ eval("\$a['" . implode("']['", $keys) . "'] = '" . addcslashes($value, "'") . "';");
}
return $a;
diff --git a/constants.php b/constants.php
index 1432ced48..ec833c4d1 100644
--- a/constants.php
+++ b/constants.php
@@ -2,7 +2,7 @@
//NB: Do not edit; use ./constants.local.php instead.
//
-define('FRESHRSS_VERSION', '1.14.0');
+define('FRESHRSS_VERSION', '1.14.1');
define('FRESHRSS_WEBSITE', 'https://freshrss.org');
define('FRESHRSS_WIKI', 'https://freshrss.github.io/FreshRSS/');
diff --git a/lib/SimplePie/SimplePie/Cache/File.php b/lib/SimplePie/SimplePie/Cache/File.php
index 6ba6c5f6e..a09dea637 100644
--- a/lib/SimplePie/SimplePie/Cache/File.php
+++ b/lib/SimplePie/SimplePie/Cache/File.php
@@ -101,7 +101,7 @@ class SimplePie_Cache_File implements SimplePie_Cache_Base
*/
public function save($data)
{
- if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location))
+ if (file_exists($this->name) && is_writable($this->name) || file_exists($this->location) && is_writable($this->location))
{
if ($data instanceof SimplePie)
{
diff --git a/lib/lib_rss.php b/lib/lib_rss.php
index 3e0033a61..fd1389047 100644
--- a/lib/lib_rss.php
+++ b/lib/lib_rss.php
@@ -280,6 +280,9 @@ function customSimplePie($attributes = array()) {
}
function sanitizeHTML($data, $base = '') {
+ if (!is_string($data)) {
+ return '';
+ }
static $simplePie = null;
if ($simplePie == null) {
$simplePie = customSimplePie();
@@ -544,3 +547,39 @@ function base64url_decode($data) {
function _i($icon, $url_only = false) {
return FreshRSS_Themes::icon($icon, $url_only);
}
+
+
+const SHORTCUT_KEYS = array(
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12',
+ 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'Backspace', 'Delete',
+ 'End', 'Enter', 'Escape', 'Home', 'Insert', 'PageDown', 'PageUp', 'Space', 'Tab',
+ );
+
+function validateShortcutList($shortcuts) {
+ $legacy = array(
+ 'down' => 'ArrowDown', 'left' => 'ArrowLeft', 'page_down' => 'PageDown', 'page_up' => 'PageUp',
+ 'right' => 'ArrowRight', 'up' => 'ArrowUp',
+ );
+ $upper = null;
+ $shortcuts_ok = array();
+
+ foreach ($shortcuts as $key => $value) {
+ if (in_array($value, SHORTCUT_KEYS)) {
+ $shortcuts_ok[$key] = $value;
+ } elseif (isset($legacy[$value])) {
+ $shortcuts_ok[$key] = $legacy[$value];
+ } else { //Case-insensitive search
+ if ($upper === null) {
+ $upper = array_map('strtoupper', SHORTCUT_KEYS);
+ }
+ $i = array_search(strtoupper($value), $upper);
+ if ($i !== false) {
+ $shortcuts_ok[$key] = SHORTCUT_KEYS[$i];
+ }
+ }
+ }
+ return $shortcuts_ok;
+}
diff --git a/p/ext.php b/p/ext.php
index 427bdc253..1b123f47d 100644
--- a/p/ext.php
+++ b/p/ext.php
@@ -20,6 +20,11 @@ require(__DIR__ . '/../constants.php');
function is_valid_path($path) {
// It must be under the extension path.
$real_ext_path = realpath(EXTENSIONS_PATH);
+
+ //Windows compatibility
+ $real_ext_path = str_replace('\\', '/', $real_ext_path);
+ $path = str_replace('\\', '/', $path);
+
$in_ext_path = (substr($path, 0, strlen($real_ext_path)) === $real_ext_path);
if (!$in_ext_path) {
return false;
diff --git a/p/scripts/install.js b/p/scripts/install.js
index b7975fd6e..967d27627 100644
--- a/p/scripts/install.js
+++ b/p/scripts/install.js
@@ -1,15 +1,15 @@
"use strict";
/* jshint globalstrict: true */
-function show_password() {
- var button = this;
+function show_password(ev) {
+ var button = ev.target;
var passwordField = document.getElementById(button.getAttribute('data-toggle'));
passwordField.setAttribute('type', 'text');
button.className += ' active';
return false;
}
-function hide_password() {
- var button = this;
+function hide_password(ev) {
+ var button = ev.target;
var passwordField = document.getElementById(button.getAttribute('data-toggle'));
passwordField.setAttribute('type', 'password');
button.className = button.className.replace(/(?:^|\s)active(?!\S)/g , '');
@@ -61,10 +61,10 @@ if (bd_type) {
bd_type.addEventListener('change', mySqlShowHide);
}
-function ask_confirmation(e) {
- var str_confirmation = this.getAttribute('data-str-confirm');
+function ask_confirmation(ev) {
+ var str_confirmation = ev.target.getAttribute('data-str-confirm');
if (!confirm(str_confirmation)) {
- e.preventDefault();
+ ev.preventDefault();
}
}
var confirms = document.getElementsByClassName('confirm');
diff --git a/p/scripts/main.js b/p/scripts/main.js
index f59976b39..d85a3ae15 100644
--- a/p/scripts/main.js
+++ b/p/scripts/main.js
@@ -2,6 +2,7 @@
/* jshint esversion:6, strict:global */
//
+if (!document.scrollingElement) document.scrollingElement = document.documentElement;
if (!NodeList.prototype.forEach) NodeList.prototype.forEach = Array.prototype.forEach;
if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.webkitMatchesSelector;
if (!Element.prototype.closest) Element.prototype.closest = function (s) {
@@ -44,18 +45,20 @@ var context;
}());
//
-function badAjax() {
+function badAjax(reload) {
openNotification(context.i18n.notif_request_failed, 'bad');
- location.reload();
+ if (reload) {
+ setTimeout(function () { location.reload(); }, 2000);
+ }
return true;
}
function needsScroll(elem) {
- const winBottom = document.documentElement.scrollTop + document.documentElement.clientHeight,
+ const winBottom = document.scrollingElement.scrollTop + document.scrollingElement.clientHeight,
elemTop = elem.offsetParent.offsetTop + elem.offsetTop,
elemBottom = elemTop + elem.offsetHeight;
- return (elemTop < document.documentElement.scrollTop || elemBottom > winBottom) ?
- elemTop - (document.documentElement.clientHeight / 2) : 0;
+ return (elemTop < document.scrollingElement.scrollTop || elemBottom > winBottom) ?
+ elemTop - (document.scrollingElement.clientHeight / 2) : 0;
}
function str2int(str) {
@@ -159,6 +162,29 @@ function incUnreadsTag(tag_id, nb) {
}
}
+function removeArticle(div) {
+ if (!div || div.classList.contains('not_read') || (context.auto_mark_article && div.classList.contains('active'))) {
+ return;
+ }
+ let scrollTop = box_to_follow.scrollTop;
+ let dirty = false;
+ const p = div.previousElementSibling,
+ n = div.nextElementSibling;
+ if (p && p.classList.contains('day') && n && n.classList.contains('day')) {
+ scrollTop -= p.offsetHeight;
+ dirty = true;
+ p.remove();
+ }
+ if (div.offsetHeight > 0 && div.offsetParent.offsetTop + div.offsetTop + div.offsetHeight < scrollTop) {
+ scrollTop -= div.offsetHeight;
+ dirty = true;
+ }
+ div.remove();
+ if (dirty) {
+ box_to_follow.scrollTop = scrollTop;
+ }
+}
+
var pending_entries = {},
mark_read_queue = [];
@@ -167,19 +193,19 @@ function send_mark_read_queue(queue, asRead, callback) {
req.open('POST', '.?c=entry&a=read' + (asRead ? '' : '&is_read=0'), true);
req.responseType = 'json';
req.onerror = function (e) {
- openNotification(context.i18n.notif_request_failed, 'bad');
for (let i = queue.length - 1; i >= 0; i--) {
delete pending_entries['flux_' + queue[i]];
}
- if (this.status == 403) {
- badAjax();
- }
+ badAjax(this.status == 403);
};
req.onload = function (e) {
if (this.status != 200) {
return req.onerror(e);
}
const json = xmlHttpRequestJson(this);
+ if (!json) {
+ return req.onerror(e);
+ }
for (let i = queue.length - 1; i >= 0; i--) {
const div = document.getElementById('flux_' + queue[i]),
myIcons = context.icons;
@@ -191,6 +217,9 @@ function send_mark_read_queue(queue, asRead, callback) {
});
div.querySelectorAll('a.read > .icon').forEach(function (img) { img.outerHTML = myIcons.read; });
inc--;
+ if (context.auto_remove_article) {
+ removeArticle(div);
+ }
} else {
div.classList.add('not_read');
div.classList.add('keep_unread'); //Split for IE11
@@ -237,14 +266,15 @@ function send_mark_queue_tick(callback) {
mark_read_queue = [];
send_mark_read_queue(queue, true, callback);
}
+var delayedFunction = send_mark_queue_tick;
function delayedClick(a) {
if (a) {
- send_mark_queue_tick(function () { a.click(); });
+ delayedFunction(function () { a.click(); });
}
}
-function mark_read(div, only_not_read) {
+function mark_read(div, only_not_read, asBatch) {
if (!div || !div.id || context.anonymous ||
(only_not_read && !div.classList.contains('not_read'))) {
return false;
@@ -256,7 +286,7 @@ function mark_read(div, only_not_read) {
const asRead = div.classList.contains('not_read'),
entryId = div.id.replace(/^flux_/, '');
- if (asRead) {
+ if (asRead && asBatch) {
mark_read_queue.push(entryId);
if (send_mark_read_queue_timeout == 0) {
send_mark_read_queue_timeout = setTimeout(function () { send_mark_queue_tick(null); }, 1000);
@@ -287,17 +317,17 @@ function mark_favorite(div) {
req.open('POST', url, true);
req.responseType = 'json';
req.onerror = function (e) {
- openNotification(context.i18n.notif_request_failed, 'bad');
delete pending_entries[div.id];
- if (this.status == 403) {
- badAjax();
- }
+ badAjax(this.status == 403);
};
req.onload = function (e) {
if (this.status != 200) {
return req.onerror(e);
}
const json = xmlHttpRequestJson(this);
+ if (!json) {
+ return req.onerror(e);
+ }
let inc = 0;
if (div.classList.contains('favorite')) {
div.classList.remove('favorite');
@@ -357,21 +387,23 @@ function toggleContent(new_active, old_active, skipping) {
if (old_active) {
old_active.classList.remove('active');
old_active.classList.remove('current'); //Split for IE11
+ if (context.auto_remove_article) {
+ removeArticle(old_active);
+ }
}
} else {
new_active.classList.toggle('active');
}
const relative_move = context.current_view === 'global',
- box_to_move = relative_move ? document.getElementById('panel') : document.documentElement;
+ box_to_move = relative_move ? document.getElementById('panel') : document.scrollingElement;
- if (context.sticky_post) {
+ if (context.sticky_post) { //Stick the article to the top when opened
let prev_article = new_active.previousElementSibling,
- new_pos = new_active.offsetTop + document.documentElement.scrollTop,
- old_scroll = box_to_move.scrollTop;
+ new_pos = new_active.offsetParent.offsetTop + new_active.offsetTop;
if (prev_article && new_active.offsetTop - prev_article.offsetTop <= 150) {
- new_pos = prev_article.offsetTop;
+ new_pos = prev_article.offsetParent.offsetTop + prev_article.offsetTop;
if (relative_move) {
new_pos -= box_to_move.offsetTop;
}
@@ -382,14 +414,14 @@ function toggleContent(new_active, old_active, skipping) {
new_pos -= document.body.clientHeight / 4;
}
if (relative_move) {
- new_pos += old_scroll;
+ new_pos += box_to_move.scrollTop;
}
box_to_move.scrollTop = new_pos;
}
if (new_active.classList.contains('active') && !skipping) {
if (context.auto_mark_article) {
- mark_read(new_active, true);
+ mark_read(new_active, true, true);
}
new_active.dispatchEvent(freshrssOpenArticleEvent);
}
@@ -529,7 +561,7 @@ function user_filter(key) {
// Force scrolling to the filter div
const scroll = needsScroll(document.querySelector('.header'));
if (scroll !== 0) {
- document.documentElement.scrollTop = scroll;
+ document.scrollingElement.scrollTop = scroll;
}
// Force the key value if there is only one action, so we can trigger it automatically
if (filters.length === 1) {
@@ -557,7 +589,7 @@ function auto_share(key) {
// Force scrolling to the share div
const scrollTop = needsScroll(share.closest('.bottom'));
if (scrollTop !== 0) {
- document.documentElement.scrollTop = scrollTop;
+ document.scrollingElement.scrollTop = scrollTop;
}
// Force the key value if there is only one action, so we can trigger it automatically
if (shares.length === 1) {
@@ -585,30 +617,9 @@ function onScroll() {
document.querySelectorAll('.not_read:not(.keep_unread)').forEach(function (div) {
if (div.offsetHeight > 0 &&
div.offsetParent.offsetTop + div.offsetTop + div.offsetHeight < minTop) {
- mark_read(div, true);
- }
- });
- }
- if (context.auto_remove_article) {
- let maxTop = box_to_follow.scrollTop,
- scrollOffset = 0;
- document.querySelectorAll('.flux:not(.active):not(.keep_unread)').forEach(function (div) {
- if (!pending_entries[div.id] && div.offsetHeight > 0 &&
- div.offsetParent.offsetTop + div.offsetTop + div.offsetHeight < maxTop) {
- const p = div.previousElementSibling,
- n = div.nextElementSibling;
- if (p && p.classList.contains('day') && n && n.classList.contains('day')) {
- p.remove();
- }
- maxTop -= div.offsetHeight;
- scrollOffset -= div.offsetHeight;
- div.remove();
+ mark_read(div, true, true);
}
});
- if (scrollOffset != 0) {
- box_to_follow.scrollTop += scrollOffset;
- return; //onscroll will be called again
- }
}
if (context.auto_load_more) {
const pagination = document.getElementById('mark-read-pagination');
@@ -621,10 +632,10 @@ function onScroll() {
function init_posts() {
if (context.auto_load_more || context.auto_mark_scroll || context.auto_remove_article) {
- box_to_follow = context.current_view === 'global' ? document.getElementById('panel') : document.documentElement;
+ box_to_follow = context.current_view === 'global' ? document.getElementById('panel') : document.scrollingElement;
let lastScroll = 0, //Throttle
timerId = 0;
- (box_to_follow === document.documentElement ? window : box_to_follow).onscroll = function () {
+ (box_to_follow === document.scrollingElement ? window : box_to_follow).onscroll = function () {
clearTimeout(timerId);
if (lastScroll + 500 < Date.now()) {
lastScroll = Date.now();
@@ -681,7 +692,10 @@ function init_column_categories() {
a.href = '#dropdown-' + id;
div.querySelector('.dropdown-target').id = 'dropdown-' + id;
div.insertAdjacentHTML('beforeend', template);
- div.querySelector('button.confirm').disabled = false;
+ const b = div.querySelector('button.confirm');
+ if (b) {
+ b.disabled = false;
+ }
} else if (getComputedStyle(dropdownMenu).display === 'none') {
const id2 = div.closest('.item').id.substr(2);
a.href = '#dropdown-' + id2;
@@ -745,7 +759,7 @@ function init_shortcuts() {
} else if (ev.shiftKey) { // Mark everything as read
document.querySelector('.nav_menu .read_all').click();
} else { // Toggle the read state
- mark_read(document.querySelector('.flux.current'), false);
+ mark_read(document.querySelector('.flux.current'), false, false);
}
return false;
}
@@ -787,7 +801,7 @@ function init_shortcuts() {
}
if (k === s.go_website) {
if (context.auto_mark_site) {
- mark_read(document.querySelector('.flux.current'), true);
+ mark_read(document.querySelector('.flux.current'), true, false);
}
window.open(document.querySelector('.flux.current a.go_website').href);
return false;
@@ -813,7 +827,7 @@ function init_stream(stream) {
stream.onclick = function (ev) {
let el = ev.target.closest('.flux a.read');
if (el) {
- mark_read(el.closest('.flux'), false);
+ mark_read(el.closest('.flux'), false, false);
return false;
}
@@ -882,7 +896,7 @@ function init_stream(stream) {
new_active = el.parentNode;
if (ev.target.tagName.toUpperCase() === 'A') { //Leave real links alone
if (context.auto_mark_article) {
- mark_read(new_active, true);
+ mark_read(new_active, true, false);
}
return true;
}
@@ -891,21 +905,28 @@ function init_stream(stream) {
}
};
- stream.onmouseup = function (ev) { // Mouseup enables us to catch middle click
+ stream.onmouseup = function (ev) { // Mouseup enables us to catch middle click, and control+click in IE/Edge
+ if (ev.altKey || ev.metaKey || ev.shiftKey) {
+ return;
+ }
+
let el = ev.target.closest('.item.title > a');
if (el) {
- if (ev.ctrlKey) {
- return; // CTRL+click, it will be manage by previous rule.
- }
- if (ev.which == 2) {
- // If middle click, we want same behaviour as CTRL+click.
- const evc = document.createEvent('click');
- evc.ctrlKey = true;
- el.dispatchEvent(evc);
- } else if (ev.which == 1) {
- // Normal click, just toggle article.
- el.parentElement.click();
+ if (ev.which == 1) {
+ if (ev.ctrlKey) { //Control+click
+ if (context.auto_mark_site) {
+ mark_read(el.closest('.flux'), true, false);
+ }
+ } else {
+ el.parentElement.click(); //Normal click, just toggle article.
+ }
+ } else if (ev.which == 2 && !ev.ctrlKey) { //Simple middle click: same behaviour as CTRL+click
+ if (context.auto_mark_article) {
+ const new_active = el.closest('.flux');
+ mark_read(new_active, true, false);
+ }
}
+ return;
}
if (context.auto_mark_site) {
@@ -916,7 +937,7 @@ function init_stream(stream) {
if (ev.which == 3) {
return;
}
- mark_read(el.closest('.flux'), true);
+ mark_read(el.closest('.flux'), true, false);
}
}
};
@@ -937,9 +958,7 @@ function init_stream(stream) {
req.responseType = 'json';
req.onerror = function (e) {
checkboxTag.checked = !isChecked;
- if (this.status == 403) {
- badAjax();
- }
+ badAjax(this.status == 403);
};
req.onload = function (e) {
if (this.status != 200) {
@@ -980,10 +999,10 @@ function init_nav_entries() {
};
nav_entries.querySelector('.up').onclick = function (e) {
const active_item = document.querySelector('.flux.current'),
- windowTop = document.documentElement.scrollTop,
+ windowTop = document.scrollingElement.scrollTop,
item_top = active_item.offsetParent.offsetTop + active_item.offsetTop;
- document.documentElement.scrollTop = windowTop > item_top ? item_top : 0;
+ document.scrollingElement.scrollTop = windowTop > item_top ? item_top : 0;
return false;
};
}
@@ -1006,6 +1025,9 @@ function loadDynamicTags(div) {
return req.onerror(e);
}
const json = xmlHttpRequestJson(this);
+ if (!json) {
+ return req.onerror(e);
+ }
let html = '';
if (json && json.length) {
for (let i = 0; i < json.length; i++) {
@@ -1031,7 +1053,7 @@ function updateFeed(feeds, feeds_count) {
req.open('POST', feed.url, true);
req.onloadend = function (e) {
if (this.status != 200) {
- return badAjax();
+ return badAjax(false);
}
feed_processed++;
const div = document.getElementById('actualizeProgress');
@@ -1042,7 +1064,7 @@ function updateFeed(feeds, feeds_count) {
const req2 = new XMLHttpRequest();
req2.open('POST', './?c=feed&a=actualize&id=-1&ajax=1', true);
req2.onloadend = function (e) {
- location.reload();
+ delayedFunction(function () { location.reload(); });
};
req2.setRequestHeader('Content-Type', 'application/json');
req2.send(JSON.stringify({
@@ -1074,9 +1096,12 @@ function init_actualize() {
req.responseType = 'json';
req.onload = function (e) {
if (this.status != 200) {
- return badAjax();
+ return badAjax(false);
}
const json = xmlHttpRequestJson(this);
+ if (!json) {
+ return badAjax(false);
+ }
if (auto && json.feeds.length < 1) {
auto = false;
context.ajax_loading = false;
@@ -1184,10 +1209,12 @@ function notifs_html5_show(nb) {
});
notification.onclick = function () {
- location.reload();
- window.focus();
- notification.close();
- };
+ delayedFunction(function() {
+ location.reload();
+ window.focus();
+ notification.close();
+ });
+ };
if (context.html5_notif_timeout !== 0) {
setTimeout(function () {
@@ -1211,6 +1238,9 @@ function refreshUnreads() {
req.responseType = 'json';
req.onload = function (e) {
const json = xmlHttpRequestJson(this);
+ if (!json) {
+ return badAjax(false);
+ }
const isAll = document.querySelector('.category.all.active');
let new_articles = false;
@@ -1286,12 +1316,11 @@ function load_more_posts() {
paginationNew = streamAdopted.querySelector('.pagination');
formPagination.replaceChild(paginationNew, paginationOld);
- if (context.display_order === 'ASC') {
- document.querySelector('#nav_menu_read_all .read_all').formAction =
- document.getElementById('bigMarkAsRead').formAction;
- } else {
- const bigMarkAsRead = document.getElementById('bigMarkAsRead');
- if (bigMarkAsRead) {
+ const bigMarkAsRead = document.getElementById('bigMarkAsRead');
+ if (bigMarkAsRead) {
+ if (context.display_order === 'ASC') {
+ document.querySelector('#nav_menu_read_all .read_all').formAction = bigMarkAsRead.formAction;
+ } else {
bigMarkAsRead.formAction = document.querySelector('#nav_menu_read_all .read_all').formAction;
}
}
@@ -1305,8 +1334,7 @@ function load_more_posts() {
init_load_more(box_load_more);
- const bigMarkAsRead = document.getElementById('bigMarkAsRead'),
- div_load_more = document.getElementById('load_more');
+ const div_load_more = document.getElementById('load_more');
if (bigMarkAsRead) {
bigMarkAsRead.removeAttribute('disabled');
}
@@ -1407,6 +1435,12 @@ function init_normal() {
init_shortcuts();
init_actualize();
faviconNbUnread();
+
+ window.onbeforeunload = function (e) {
+ if (mark_read_queue && mark_read_queue.length > 0) {
+ return false;
+ }
+ };
}
function init_beforeDOM() {
diff --git a/phpcs.xml b/phpcs.xml
index d79e2a4d9..c6cc44e80 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -2,7 +2,7 @@
Created with the PHP Coding Standard Generator. https://edorian.github.com/php-coding-standard-generator/
-
+
./static
./vendor
./lib/SimplePie/
@@ -33,8 +33,9 @@
./app/SQL/install.sql.mysql.php
./app/SQL/install.sql.pgsql.php
-
-
+
+
+