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 - - + + +