diff options
57 files changed, 428 insertions, 171 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index f2ca6f13b..a9490f25e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,38 @@ # FreshRSS changelog +## 2019-01-26 FreshRSS 1.13.1 + +* Features + * Include articles with custom labels during export [#2196](https://github.com/FreshRSS/FreshRSS/issues/2196) + * Export/import articles read/unread state [#2226](https://github.com/FreshRSS/FreshRSS/pull/2226) + * Import FeedBin, and more robust general import [#2228](https://github.com/FreshRSS/FreshRSS/pull/2228) +* Bug fixing + * Fix missing HTTP `X-Forwarded-Prefix` in cookie path behind a reverse-proxy [#2201](https://github.com/FreshRSS/FreshRSS/pull/2201) +* Deployment + * Docker improvements [#2202](https://github.com/FreshRSS/FreshRSS/pull/2202) + * Performance: Hard-include Apache .htaccess to avoid having to scan for changes in those files + * Performance: Disable unused Apache security check of symlinks + * Performance: Disable unused Apache modules + * Add option to mount custom `.htaccess` for HTTP authentication + * Docker logs gets PHP syslog messages (e.g. from cron job and when fetching external content) + * New environment variable `COPY_SYSLOG_TO_STDERR` or in `constants.local.php` to copy PHP syslog messages to STDERR [#2213](https://github.com/FreshRSS/FreshRSS/pull/2213) + * New `TZ` timezone environment variable [#2153](https://github.com/FreshRSS/FreshRSS/issues/2153) + * Run Docker cron job with Apache user instead of root [#2208](https://github.com/FreshRSS/FreshRSS/pull/2208) + * Accept HTTP header `X-WebAuth-User` for delegated HTTP Authentication [#2204](https://github.com/FreshRSS/FreshRSS/pull/2204) +* Extensions + * Trigger a `freshrss:openArticle` JavaScript event [#2222](https://github.com/FreshRSS/FreshRSS/pull/2222) +* API + * Automatic test of API configuration [#2207](https://github.com/FreshRSS/FreshRSS/pull/2207) + * Performance + compatibility: Use Apache `SetEnvIf` module if available and fall-back to `RewriteRule` [#2202](https://github.com/FreshRSS/FreshRSS/pull/2202) +* Security + * Fixes when HTTP user does not exist in FreshRSS [#2204](https://github.com/FreshRSS/FreshRSS/pull/2204) +* I18n + * Improve Dutch [#2221](https://github.com/FreshRSS/FreshRSS/pull/2221) + * Improve Occitan [#2230](https://github.com/FreshRSS/FreshRSS/pull/2230) +* Accessibility + * Remove alt in logo [#2209](https://github.com/FreshRSS/FreshRSS/pull/2209) + + ## 2018-12-22 FreshRSS 1.13.0 * API @@ -24,7 +57,7 @@ [#1620](https://github.com/FreshRSS/FreshRSS/issues/1620), [#2089](https://github.com/FreshRSS/FreshRSS/pull/2089), [#2122](https://github.com/FreshRSS/FreshRSS/pull/2122), [#2161](https://github.com/FreshRSS/FreshRSS/pull/2161) * Deployment - * Support for `HTTP_X_FORWARDED_PREFIX` to ease the use of reverse proxies [#2191](https://github.com/FreshRSS/FreshRSS/pull/2191) + * Support for HTTP `X-Forwarded-Prefix` to ease the use of reverse proxies [#2191](https://github.com/FreshRSS/FreshRSS/pull/2191) * Updated Docker + Træfik + Let’s Encrypt deployment guide [#2189](https://github.com/FreshRSS/FreshRSS/pull/2189) * Docker image updated to Alpine 3.8.2 with PHP 7.2.13 and Apache 2.4.35 * Fix `.dockerignore` [#2195](https://github.com/FreshRSS/FreshRSS/pull/2195) diff --git a/Docker/Dockerfile b/Docker/Dockerfile index cca7bb65e..a4be9fd84 100644 --- a/Docker/Dockerfile +++ b/Docker/Dockerfile @@ -1,24 +1,31 @@ FROM alpine:3.8 +ENV TZ UTC + 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-iconv php7-json php7-session php7-simplexml php7-xmlreader php7-zlib \ - php7-pdo_sqlite \ - php7-pdo_mysql \ - php7-pdo_pgsql + 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} +RUN mkdir -p /var/www/FreshRSS /run/apache2/ +WORKDIR /var/www/FreshRSS -COPY . ${FRESHRSS_ROOT} +COPY . /var/www/FreshRSS COPY ./Docker/*.Apache.conf /etc/apache2/conf.d/ -RUN sed -r -i "/^[ ]*(CustomLog|ErrorLog|Listen) /s/^/#/" /etc/apache2/httpd.conf && \ - echo "17,37 * * * * php ${FRESHRSS_ROOT}/app/actualize_script.php 2>&1 | tee /tmp/FreshRSS.log" >> \ +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/^/#/" \ + /etc/apache2/httpd.conf && \ + 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 +ENV COPY_SYSLOG_TO_STDERR On ENV CRON_MIN '' ENTRYPOINT ["./Docker/entrypoint.sh"] diff --git a/Docker/FreshRSS.Apache.conf b/Docker/FreshRSS.Apache.conf index adfc804c6..80f6389d8 100644 --- a/Docker/FreshRSS.Apache.conf +++ b/Docker/FreshRSS.Apache.conf @@ -1,19 +1,3 @@ -<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/ @@ -21,7 +5,23 @@ CustomLog /dev/stdout combined ErrorLog /dev/stderr AllowEncodedSlashes On +<Directory /> + AllowOverride None + Options FollowSymLinks + Require all denied +</Directory> + <Directory /var/www/FreshRSS/p> - AllowOverride AuthConfig FileInfo Indexes Limit + AllowOverride None + Include /var/www/FreshRSS/p/.htaccess + Options FollowSymLinks Require all granted </Directory> + +<Directory /var/www/FreshRSS/p/api> + Include /var/www/FreshRSS/p/api/.htaccess +</Directory> + +<Directory /var/www/FreshRSS/p/i> + IncludeOptional /var/www/FreshRSS/p/i/.htaccess +</Directory> diff --git a/Docker/README.md b/Docker/README.md index 6b3871c6b..ac745c49d 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -66,6 +66,7 @@ sudo docker volume create freshrss-data sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ -v freshrss-data:/var/www/FreshRSS/data \ -e 'CRON_MIN=4,34' \ + -e TZ=Europe/Paris \ --net freshrss-network \ --label traefik.port=80 \ --label traefik.frontend.rule='Host:freshrss.example.net' \ @@ -74,6 +75,7 @@ sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ --name freshrss freshrss/freshrss ``` +* Replace `TZ=Europe/Paris` by your [server timezone](http://php.net/timezones), or remove the line to use `UTC`. * If you cannot have FreshRSS at the root of a dedicated domain, update the command above according to the following model: `--label traefik.frontend.rule='Host:freshrss.example.net;PathPrefixStrip:/FreshRSS/' \` * You may remove the `--label traefik.*` lines if you do not use Træfik. @@ -205,6 +207,27 @@ sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ ## More deployment options +### Custom Apache configuration (advanced users) + +Changes in Apache `.htaccess` files are applied when restarting the container. +In particular, if you want FreshRSS to use HTTP-based login (instead of the easier Web form login), you can mount your own `./FreshRSS/p/i/.htaccess`: + +``` +sudo docker run ... + -v /your/.htaccess:/var/www/FreshRSS/p/i/.htaccess \ + -v /your/.htpasswd:/var/www/FreshRSS/data/.htpasswd \ + ... + --name freshrss freshrss/freshrss +``` + +Example of `/your/.htaccess` referring to `/your/.htpasswd`: +``` +AuthUserFile /var/www/FreshRSS/data/.htpasswd +AuthName "FreshRSS" +AuthType Basic +Require valid-user +``` + ### Example with [docker-compose](https://docs.docker.com/compose/) A [docker-compose.yml](docker-compose.yml) file is given as an example, using PostgreSQL. In order to use it, you have to adapt: diff --git a/Docker/entrypoint.sh b/Docker/entrypoint.sh index d4e1808bc..528388073 100755 --- a/Docker/entrypoint.sh +++ b/Docker/entrypoint.sh @@ -5,6 +5,8 @@ php -f ./cli/prepare.php > /dev/null chown -R :www-data . 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 fi diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php index 5ad1a51d9..3b2d78b19 100644 --- a/app/Controllers/authController.php +++ b/app/Controllers/authController.php @@ -79,8 +79,12 @@ class FreshRSS_auth_Controller extends Minz_ActionController { Minz_Request::forward(array('c' => 'auth', 'a' => 'formLogin')); break; case 'http_auth': + Minz_Error::error(403, array('error' => array(_t('feedback.access.denied'), + ' [HTTP Remote-User=' . htmlspecialchars(httpAuthUser(), ENT_NOQUOTES, 'UTF-8') . ']' + )), false); + break; case 'none': - // It should not happened! + // It should not happen! Minz_Error::error(404); default: // TODO load plugin instead diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 2d8d4e01d..80b9868ec 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -41,7 +41,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $list_files = array( 'opml' => array(), 'json_starred' => array(), - 'json_feed' => array() + 'json_feed' => array(), + 'ttrss_starred' => array(), ); // We try to list all files according to their type @@ -434,10 +435,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } return false; } + $items = isset($article_object['items']) ? $article_object['items'] : $article_object; - $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; - - $google_compliant = strpos($article_object['id'], 'com.google') !== false; + $mark_as_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; $error = false; $article_to_feed = array(); @@ -447,9 +447,23 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $limits = FreshRSS_Context::$system_conf->limits; // First, we check feeds of articles are in DB (and add them if needed). - foreach ($article_object['items'] as $item) { - $key = $google_compliant ? 'htmlUrl' : 'feedUrl'; - $feed = new FreshRSS_Feed($item['origin'][$key]); + foreach ($items as $item) { + if (!isset($item['origin'])) { + $item['origin'] = array('title' => 'Import'); + } + if (!empty($item['origin']['feedUrl'])) { + $feedUrl = $item['origin']['feedUrl']; + } elseif (!empty($item['origin']['streamId']) && strpos($item['origin']['streamId'], 'feed/') === 0) { + $feedUrl = substr($item['origin']['streamId'], 5); //Google Reader + $item['origin']['feedUrl'] = $feedUrl; + } elseif (!empty($item['origin']['htmlUrl'])) { + $feedUrl = $item['origin']['htmlUrl']; + } else { + $feedUrl = 'http://import.localhost/import.xml'; + $item['origin']['feedUrl'] = $feedUrl; + $item['origin']['disable'] = true; + } + $feed = new FreshRSS_Feed($feedUrl); $feed = $this->feedDAO->searchByUrl($feed->url()); if ($feed == null) { @@ -497,7 +511,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // Then, articles are imported. $newGuids = array(); $this->entryDAO->beginTransaction(); - foreach ($article_object['items'] as $item) { + foreach ($items as $item) { if (empty($article_to_feed[$item['id']])) { // Related feed does not exist for this entry, do nothing. continue; @@ -505,14 +519,19 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $feed_id = $article_to_feed[$item['id']]; $author = isset($item['author']) ? $item['author'] : ''; - $is_starred = $starred; - $tags = $item['categories']; + $is_starred = false; + $is_read = null; + $tags = empty($item['categories']) ? array() : $item['categories']; $labels = array(); for ($i = count($tags) - 1; $i >= 0; $i --) { $tag = trim($tags[$i]); if (strpos($tag, 'user/-/') !== false) { if ($tag === 'user/-/state/com.google/starred') { $is_starred = true; + } elseif ($tag === 'user/-/state/com.google/read') { + $is_read = true; + } elseif ($tag === 'user/-/state/com.google/unread') { + $is_read = false; } elseif (strpos($tag, 'user/-/label/') === 0) { $tag = trim(substr($tag, 13)); if ($tag != '') { @@ -522,19 +541,49 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { unset($tags[$i]); } } + if ($starred && !$is_starred) { + //If the article has no label, mark it as starred (old format) + $is_starred = empty($labels); + } + if ($is_read === null) { + $is_read = $mark_as_read; + } + + if (isset($item['alternate'][0]['href'])) { + $url = $item['alternate'][0]['href']; + } elseif (isset($item['url'])) { + $url = $item['url']; //FeedBin + } else { + $url = ''; + } - $url = $item['alternate'][0]['href']; if (!empty($item['content']['content'])) { $content = $item['content']['content']; } elseif (!empty($item['summary']['content'])) { $content = $item['summary']['content']; + } elseif (!empty($item['content'])) { + $content = $item['content']; //FeedBin + } else { + $content = ''; } $content = sanitizeHTML($content, $url); + if (!empty($item['published'])) { + $published = $item['published']; + } elseif (!empty($item['timestampUsec'])) { + $published = substr($item['timestampUsec'], 0, -6); + } elseif (!empty($item['updated'])) { + $published = $item['updated']; + } else { + $published = 0; + } + if (!ctype_digit('' . $published)) { + $published = strtotime($published); + } + $entry = new FreshRSS_Entry( $feed_id, $item['id'], $item['title'], $author, - $content, $url, - $item['published'], $is_read, $is_starred + $content, $url, $published, $is_read, $is_starred ); $entry->_id(min(time(), $entry->date(true)) . uSecString()); $entry->_tags($tags); @@ -626,6 +675,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $feed->_category(FreshRSS_CategoryDAO::DEFAULTCATEGORYID); $feed->_name($name); $feed->_website($website); + if (!empty($origin['disable'])) { + $feed->_ttl(-1 * FreshRSS_Context::$user_conf->ttl_default); + } // Call the extension hook $feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed); @@ -650,7 +702,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return $return; } - public function exportFile($export_opml = true, $export_starred = false, $export_feeds = array(), $maxFeedEntries = 50, $username = null) { + public function exportFile($export_opml = true, $export_starred = false, $export_labelled = false, $export_feeds = array(), $maxFeedEntries = 50, $username = null) { require_once(LIB_PATH . '/lib_opml.php'); $this->catDAO = new FreshRSS_CategoryDAO($username); @@ -674,8 +726,11 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $export_files["feeds_${day}.opml.xml"] = $this->generateOpml(); } - if ($export_starred) { - $export_files["starred_${day}.json"] = $this->generateEntries('starred'); + if ($export_starred || $export_labelled) { + $export_files["starred_${day}.json"] = $this->generateEntries( + ($export_starred ? 'S' : '') . + ($export_labelled ? 'T' : '') + ); } foreach ($export_feeds as $feed_id) { @@ -683,7 +738,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if ($feed) { $filename = "feed_${day}_" . $feed->category() . '_' . $feed->id() . '.json'; - $export_files[$filename] = $this->generateEntries('feed', $feed, $maxFeedEntries); + $export_files[$filename] = $this->generateEntries('f', $feed, $maxFeedEntries); } } @@ -725,6 +780,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $nb_files = $this->exportFile( Minz_Request::param('export_opml', false), Minz_Request::param('export_starred', false), + Minz_Request::param('export_labelled', false), Minz_Request::param('export_feeds', array()) ); } catch (FreshRSS_ZipMissing_Exception $zme) { @@ -758,27 +814,29 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { /** * This method returns a JSON file content. * - * @param string $type must be "starred" or "feed" + * @param string $type must be one of: + * 'S' (starred/favourite), 'f' (feed), 'T' (taggued/labelled), 'ST' (starred or labelled) * @param FreshRSS_Feed $feed feed of which we want to get entries. * @return string the JSON file content. */ private function generateEntries($type, $feed = null, $maxFeedEntries = 50) { $this->view->categories = $this->catDAO->listCategories(); + $tagDAO = FreshRSS_Factory::createTagDao(); - if ($type == 'starred') { + if ($type === 's' || $type === 'S' || $type === 'T' || $type === 'ST') { $this->view->list_title = _t('sub.import_export.starred_list'); $this->view->type = 'starred'; - $unread_fav = $this->entryDAO->countUnreadReadFavorites(); - $this->view->entriesRaw = $this->entryDAO->listWhereRaw( - 's', '', FreshRSS_Entry::STATE_ALL, 'ASC', $unread_fav['all'] - ); - } elseif ($type === 'feed' && $feed != null) { + $this->view->entriesId = $this->entryDAO->listIdsWhere($type, '', FreshRSS_Entry::STATE_ALL, 'ASC', -1); + $this->view->entryIdsTagNames = $tagDAO->getEntryIdsTagNames($this->view->entriesId); + //The following is a streamable query, i.e. must be last + $this->view->entriesRaw = $this->entryDAO->listWhereRaw($type, '', FreshRSS_Entry::STATE_ALL, 'ASC', -1); + } elseif ($type === 'f' && $feed != null) { $this->view->list_title = _t('sub.import_export.feed_list', $feed->name()); $this->view->type = 'feed/' . $feed->id(); - $this->view->entriesRaw = $this->entryDAO->listWhereRaw( - 'f', $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', - $maxFeedEntries - ); + $this->view->entriesId = $this->entryDAO->listIdsWhere($type, $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', $maxFeedEntries); + $this->view->entryIdsTagNames = $tagDAO->getEntryIdsTagNames($this->view->entriesId); + //The following is a streamable query, i.e. must be last + $this->view->entriesRaw = $this->entryDAO->listWhereRaw($type, $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', $maxFeedEntries); $this->view->feed = $feed; } diff --git a/app/Models/Auth.php b/app/Models/Auth.php index 9c3e31952..513a9cb2f 100644 --- a/app/Models/Auth.php +++ b/app/Models/Auth.php @@ -28,13 +28,13 @@ class FreshRSS_Auth { if (self::$login_ok) { self::giveAccess(); - } elseif (self::accessControl()) { - self::giveAccess(); + } elseif (self::accessControl() && self::giveAccess()) { FreshRSS_UserDAO::touch(); } else { // Be sure all accesses are removed! self::removeAccess(); } + return self::$login_ok; } /** @@ -60,7 +60,7 @@ class FreshRSS_Auth { return $current_user != ''; case 'http_auth': $current_user = httpAuthUser(); - $login_ok = $current_user != ''; + $login_ok = $current_user != '' && FreshRSS_UserDAO::exists($current_user); if ($login_ok) { Minz_Session::_param('currentUser', $current_user); } @@ -81,7 +81,7 @@ class FreshRSS_Auth { $user_conf = get_user_configuration($current_user); if ($user_conf == null) { self::$login_ok = false; - return; + return false; } $system_conf = Minz_Configuration::get('system'); @@ -102,6 +102,7 @@ class FreshRSS_Auth { Minz_Session::_param('loginOk', self::$login_ok); Minz_Session::_param('REMOTE_USER', httpAuthUser()); + return self::$login_ok; } /** diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 985276734..f2f3d08fe 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -209,6 +209,7 @@ class FreshRSS_Entry extends Minz_Model { $feed_timeout = empty($attributes['timeout']) ? 0 : intval($attributes['timeout']); if ($system_conf->simplepie_syslog_enabled) { + prepareSyslog(); syslog(LOG_INFO, 'FreshRSS GET ' . SimplePie_Misc::url_remove_credentials($url)); } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 6d77a33cd..08927196e 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -839,6 +839,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $where .= 'f.priority >= ' . FreshRSS_Feed::PRIORITY_NORMAL . ' '; $where .= 'AND e.is_favorite=1 '; break; + case 'S': //Starred + $where .= 'e.is_favorite=1 '; + break; case 'c': //Category $where .= 'f.priority >= ' . FreshRSS_Feed::PRIORITY_NORMAL . ' '; $where .= 'AND f.category=? '; @@ -855,6 +858,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { case 'T': //Any tag $where .= '1=1 '; break; + case 'ST': //Starred or tagged + $where .= 'e.is_favorite=1 OR EXISTS (SELECT et2.id_tag FROM `' . $this->prefix . 'entrytag` et2 WHERE et2.id_entry = e.id) '; + break; default: throw new FreshRSS_EntriesGetter_Exception('Bad type in Entry->listByType: [' . $type . ']!'); } diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 7f00642f4..c9c9f6301 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -61,7 +61,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $valuesTmp['lastUpdate'], base64_encode($valuesTmp['httpAuth']), FreshRSS_Feed::KEEP_HISTORY_DEFAULT, - FreshRSS_Feed::TTL_DEFAULT, + isset($valuesTmp['ttl']) ? intval($valuesTmp['ttl']) : FreshRSS_Feed::TTL_DEFAULT, isset($valuesTmp['attributes']) ? json_encode($valuesTmp['attributes']) : '', ); @@ -95,6 +95,9 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { 'httpAuth' => $feed->httpAuth(), 'attributes' => $feed->attributes(), ); + if ($feed->mute() || $feed->ttl() != FreshRSS_Context::$user_conf->ttl_default) { + $values['ttl'] = $feed->ttl() * ($feed->mute() ? -1 : 1); + } $id = $this->addFeed($values); if ($id) { diff --git a/app/Models/TagDAO.php b/app/Models/TagDAO.php index b55d2b35d..dba854aa4 100644 --- a/app/Models/TagDAO.php +++ b/app/Models/TagDAO.php @@ -265,8 +265,18 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $values = array(); if (is_array($entries) && count($entries) > 0) { $sql .= ' AND et.id_entry IN (' . str_repeat('?,', count($entries) - 1). '?)'; - foreach ($entries as $entry) { - $values[] = is_array($entry) ? $entry['id'] : $entry->id(); + if (is_array($entries[0])) { + foreach ($entries as $entry) { + $values[] = $entry['id']; + } + } elseif (is_object($entries[0])) { + foreach ($entries as $entry) { + $values[] = $entry->id(); + } + } else { + foreach ($entries as $entry) { + $values[] = $entry; + } } } $stm = $this->bd->prepare($sql); diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index 5fb46c947..e9d3a7329 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -65,7 +65,7 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); if ($db['type'] === 'sqlite') { - return unlink(join_path(DATA_PATH, 'users', $username, 'db.sqlite')); + return unlink(USERS_PATH . '/' . $username . '/db.sqlite'); } else { $userPDO = new Minz_ModelPdo($username); @@ -81,18 +81,18 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { } } - public static function exist($username) { - return is_dir(join_path(DATA_PATH, 'users', $username)); + public static function exists($username) { + return is_dir(USERS_PATH . '/' . $username); } public static function touch($username = '') { if (!FreshRSS_user_Controller::checkUsername($username)) { $username = Minz_Session::param('currentUser', '_'); } - return touch(join_path(DATA_PATH, 'users', $username, 'config.php')); + return touch(USERS_PATH . '/' . $username . '/config.php'); } public static function mtime($username) { - return @filemtime(join_path(DATA_PATH, 'users', $username, 'config.php')); + return @filemtime(USERS_PATH . '/' . $username . '/config.php'); } } diff --git a/app/actualize_script.php b/app/actualize_script.php index ba9660a14..f1dec5640 100755 --- a/app/actualize_script.php +++ b/app/actualize_script.php @@ -12,6 +12,9 @@ if (defined('STDOUT')) { fwrite(STDOUT, 'Starting feed actualization at ' . $begin_date->format('c') . "\n"); //Unbuffered } +prepareSyslog(); +syslog(LOG_INFO, 'FreshRSS Start feeds actualization...'); + // Set the header params ($_GET) to call the FRSS application. $_GET['c'] = 'feed'; $_GET['a'] = 'actualize'; @@ -64,7 +67,7 @@ foreach ($users as $user) { if (!invalidateHttpCache()) { Minz_Log::warning('FreshRSS write access problem in ' . join_path(USERS_PATH, $user, 'log.txt'), ADMIN_LOG); if (defined('STDERR')) { - fwrite(STDERR, 'Write access problem in ' . join_path(USERS_PATH, $user, 'log.txt') . "\n"); + fwrite(STDERR, 'FreshRSS write access problem in ' . join_path(USERS_PATH, $user, 'log.txt') . "\n"); } } } @@ -75,7 +78,8 @@ if (defined('STDOUT')) { $end_date = date_create('now'); $duration = date_diff($end_date, $begin_date); fwrite(STDOUT, 'Ending feed actualization at ' . $end_date->format('c') . "\n"); //Unbuffered - fwrite(STDOUT, 'Feed actualizations took ' . $duration->format('%a day(s), %h hour(s), %i minute(s) and %s seconds') . ' for ' . count($users) . " users\n"); //Unbuffered + fwrite(STDOUT, 'Feed actualizations took ' . $duration->format('%a day(s), %h hour(s), %i minute(s) and %s seconds') . ' for ' . count($users) . " users\n"); //Unbuffered } echo 'End.', "\n"; ob_end_flush(); +syslog(LOG_INFO, 'FreshRSS feeds actualization done.'); diff --git a/app/i18n/cz/sub.php b/app/i18n/cz/sub.php index ad02f6f49..5b5634fed 100644 --- a/app/i18n/cz/sub.php +++ b/app/i18n/cz/sub.php @@ -72,6 +72,7 @@ return array( 'export' => 'Export', 'export_opml' => 'Exportovat seznam kanálů (OPML)', 'export_starred' => 'Exportovat oblíbené', + 'export_labelled' => 'Export your labelled articles', //TODO 'feed_list' => 'Seznam %s článků', 'file_to_import' => 'Soubor k importu<br />(OPML, JSON nebo ZIP)', 'file_to_import_no_zip' => 'Soubor k importu<br />(OPML nebo JSON)', diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php index aa408e8c7..27e893177 100644 --- a/app/i18n/de/sub.php +++ b/app/i18n/de/sub.php @@ -72,6 +72,7 @@ return array( 'export' => 'Exportieren', 'export_opml' => 'Liste der Feeds exportieren (OPML)', 'export_starred' => 'Ihre Favoriten exportieren', + 'export_labelled' => 'Export your labelled articles', //TODO 'feed_list' => 'Liste von %s Artikeln', 'file_to_import' => 'Zu importierende Datei<br />(OPML, JSON oder ZIP)', 'file_to_import_no_zip' => 'Zu importierende Datei<br />(OPML oder JSON)', diff --git a/app/i18n/en/sub.php b/app/i18n/en/sub.php index 9acbcbf33..4efd81ba4 100644 --- a/app/i18n/en/sub.php +++ b/app/i18n/en/sub.php @@ -72,6 +72,7 @@ return array( 'export' => 'Export', 'export_opml' => 'Export list of feeds (OPML)', 'export_starred' => 'Export your favourites', + 'export_labelled' => 'Export your labelled articles', 'feed_list' => 'List of %s articles', 'file_to_import' => 'File to import<br />(OPML, JSON or ZIP)', 'file_to_import_no_zip' => 'File to import<br />(OPML or JSON)', diff --git a/app/i18n/es/sub.php b/app/i18n/es/sub.php index 64e420dc1..854984891 100755 --- a/app/i18n/es/sub.php +++ b/app/i18n/es/sub.php @@ -72,6 +72,7 @@ return array( 'export' => 'Exportar', 'export_opml' => 'Exportar la lista de fuentes (OPML)', 'export_starred' => 'Exportar tus favoritos', + 'export_labelled' => 'Export your labelled articles', //TODO 'feed_list' => 'Lista de %s artículos', 'file_to_import' => 'Archivo a importar<br />(OPML, JSON o ZIP)', 'file_to_import_no_zip' => 'Archivo a importar<br />(OPML o JSON)', diff --git a/app/i18n/fr/sub.php b/app/i18n/fr/sub.php index 6cb31414d..d9964ac6e 100644 --- a/app/i18n/fr/sub.php +++ b/app/i18n/fr/sub.php @@ -72,6 +72,7 @@ return array( 'export' => 'Exporter', 'export_opml' => 'Exporter la liste des flux (OPML)', 'export_starred' => 'Exporter les favoris', + 'export_labelled' => 'Exporter les articles étiquetés', 'feed_list' => 'Liste des articles de %s', 'file_to_import' => 'Fichier à importer<br />(OPML, JSON ou ZIP)', 'file_to_import_no_zip' => 'Fichier à importer<br />(OPML ou JSON)', diff --git a/app/i18n/he/sub.php b/app/i18n/he/sub.php index e4c487b84..6d824e349 100644 --- a/app/i18n/he/sub.php +++ b/app/i18n/he/sub.php @@ -72,6 +72,7 @@ return array( 'export' => 'ייצוא', 'export_opml' => 'ייצוא רשימת הזנות (OPML)', 'export_starred' => 'ייצוא מועדפים', + 'export_labelled' => 'Export your labelled articles', //TODO 'feed_list' => 'רשימה של %s מאמרים', 'file_to_import' => 'קובץ לייבוא<br />(OPML, Json or Zip)', 'file_to_import_no_zip' => 'קובץ לייבוא<br />(OPML or Json)', diff --git a/app/i18n/it/sub.php b/app/i18n/it/sub.php index 6faa48d63..ff7fa6f1d 100644 --- a/app/i18n/it/sub.php +++ b/app/i18n/it/sub.php @@ -72,6 +72,7 @@ return array( 'export' => 'Esporta', 'export_opml' => 'Esporta tutta la lista dei feed (OPML)', 'export_starred' => 'Esporta i tuoi preferiti', + 'export_labelled' => 'Export your labelled articles', //TODO 'feed_list' => 'Elenco di %s articoli', 'file_to_import' => 'File da importare<br />(OPML, JSON o ZIP)', 'file_to_import_no_zip' => 'File da importare<br />(OPML o JSON)', diff --git a/app/i18n/kr/sub.php b/app/i18n/kr/sub.php index 463496c57..9f8967053 100644 --- a/app/i18n/kr/sub.php +++ b/app/i18n/kr/sub.php @@ -72,6 +72,7 @@ return array( 'export' => '내보내기', 'export_opml' => '피드 목록 내보내기 (OPML)', 'export_starred' => '즐겨찾기 내보내기', + 'export_labelled' => 'Export your labelled articles', //TODO 'feed_list' => '%s 개의 글 목록', 'file_to_import' => '불러올 파일<br />(OPML, JSON 또는 ZIP)', 'file_to_import_no_zip' => '불러올 파일<br />(OPML 또는 JSON)', diff --git a/app/i18n/nl/admin.php b/app/i18n/nl/admin.php index 8a63b885b..c6fd1dc9e 100644 --- a/app/i18n/nl/admin.php +++ b/app/i18n/nl/admin.php @@ -163,8 +163,8 @@ return array( 'max-categories' => 'Categoriën limiet per gebruiker', 'max-feeds' => 'Feed limiet per gebruiker', 'cookie-duration' => array( - 'help' => 'in seconds', // @todo translate - 'number' => 'Duration to keep logged in', // @todo translate + 'help' => 'in seconden', + 'number' => 'Tijdsduur om ingelogd te blijven', ), 'registration' => array( 'help' => '0 betekent geen account limiet', diff --git a/app/i18n/nl/conf.php b/app/i18n/nl/conf.php index ba21fc889..8e6a59d56 100644 --- a/app/i18n/nl/conf.php +++ b/app/i18n/nl/conf.php @@ -162,7 +162,7 @@ return array( 'mark_read' => 'Markeer als gelezen', 'navigation' => 'Navigatie', 'navigation_help' => 'Met de "Shift" toets, kunt u navigatie verwijzingen voor feeds gebruiken.<br/>Met de "Alt" toets, kunt u navigatie verwijzingen voor categoriën gebruiken.', - 'navigation_no_mod_help' => 'The following navigation shortcuts do not support modifiers.', //TODO - Translation + 'navigation_no_mod_help' => 'De volgende navigatiesnelkoppelingen ondersteunen geen besturingstoetsen.', 'next_article' => 'Spring naar volgende artikel', 'normal_view' => 'Schakel naar gewoon aanzicht', 'other_action' => 'Andere acties', @@ -171,8 +171,8 @@ return array( 'rss_view' => 'Open RSS-aanzicht in een nieuwe tab', 'see_on_website' => 'Bekijk op originale website', 'shift_for_all_read' => '+ <code>shift</code> om alle artikelen als gelezen te markeren', - 'skip_next_article' => 'Focus next without opening', //TODO - Translation - 'skip_previous_article' => 'Focus previous without opening', //TODO - Translation + 'skip_next_article' => 'Volgend artikel focusen zonder openen', + 'skip_previous_article' => 'Vorig artikel focusen zonder openen', 'title' => 'Verwijzingen', 'user_filter' => 'Toegang gebruikers filters', 'user_filter_help' => 'Als er slechts één gebruikersfilter is, dan wordt die gebruikt. Anders zijn ze toegankelijk met hun nummer.', diff --git a/app/i18n/nl/index.php b/app/i18n/nl/index.php index c2b550647..d202b812a 100644 --- a/app/i18n/nl/index.php +++ b/app/i18n/nl/index.php @@ -53,11 +53,11 @@ return array( 'starred' => 'Laat alleen favorieten zien', 'stats' => 'Statistieken', 'subscription' => 'Abonnementen beheer', - 'tags' => 'My labels', //TODO - Translation + 'tags' => 'Mijn labels', 'unread' => 'Laat alleen ongelezen zien', ), 'share' => 'Delen', 'tag' => array( - 'related' => 'Verwante labels', //TODO - Translation + 'related' => 'Verwante labels', ), ); diff --git a/app/i18n/nl/sub.php b/app/i18n/nl/sub.php index 36c96b53f..1d6c9f806 100644 --- a/app/i18n/nl/sub.php +++ b/app/i18n/nl/sub.php @@ -27,7 +27,7 @@ return array( 'password' => 'HTTP wachtwoord', 'username' => 'HTTP gebruikers naam', ), - 'clear_cache' => 'Always clear cache', //TODO - Translation + 'clear_cache' => 'Cache altijd leegmaken', 'css_help' => 'Haalt verstoorde RSS feeds op (attentie, heeft meer tijd nodig!)', 'css_path' => 'Artikelen CSS pad op originele website', 'description' => 'Omschrijving', @@ -47,11 +47,11 @@ return array( ), 'websub' => 'Directe notificaties met WebSub', 'show' => array( - 'all' => 'Show all feeds', //TODO - Translation - 'error' => 'Show only feeds with error', //TODO - Translation + 'all' => 'Alle feeds tonen', + 'error' => 'Alleen feeds met een foutmelding tonen', ), 'showing' => array( - 'error' => 'Showing only feeds with error', //TODO - Translation + 'error' => 'Alleen feeds met een foutmelding worden getoond', ), 'ssl_verify' => 'SSL-veiligheid controleren', 'stats' => 'Statistieken', @@ -72,6 +72,7 @@ return array( 'export' => 'Exporteer', 'export_opml' => 'Exporteer lijst van feeds (OPML)', 'export_starred' => 'Exporteer je favorieten', + 'export_labelled' => 'Exporteer gelabelde artikels', 'feed_list' => 'Lijst van %s artikelen', 'file_to_import' => 'Bestand om te importeren<br />(OPML, JSON of ZIP)', 'file_to_import_no_zip' => 'Bestand om te importeren<br />(OPML of JSON)', diff --git a/app/i18n/oc/conf.php b/app/i18n/oc/conf.php index 346f796e1..ad52691da 100644 --- a/app/i18n/oc/conf.php +++ b/app/i18n/oc/conf.php @@ -94,14 +94,14 @@ return array( 'display_articles_unfolded' => 'Mostrar los articles desplegats per defaut', 'display_categories_unfolded' => 'Mostrar las categorias plegadas 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” per las imatges', + 'img_with_lazyload' => 'Utilizar lo mòde “cargament tardiu” pels imatges', 'jump_next' => 'sautar al vesin venent pas legit (flux o categoria)', 'mark_updated_article_unread' => 'Marcar los articles actualizats coma pas legits', 'number_divided_when_reader' => 'Devisat per 2 dins la vista de lectura.', 'read' => array( 'article_open_on_website' => 'quand l’article es dobèrt sul site d’origina', 'article_viewed' => 'quand l’article es mostrat', - 'scroll' => 'en davalant la pagina', + 'scroll' => 'en davalar la pagina', 'upon_reception' => 'en recebre un article novèl', 'when' => 'Marcar un article coma legit…', ), diff --git a/app/i18n/oc/gen.php b/app/i18n/oc/gen.php index ffe10941d..168ea4732 100644 --- a/app/i18n/oc/gen.php +++ b/app/i18n/oc/gen.php @@ -43,29 +43,29 @@ return array( ), ), 'date' => array( - 'Apr' => 'a\b\r\i\a\l', + 'Apr' => '\\a\\b\\r\\i\\a\\l', 'apr' => 'abr.', 'april' => 'abrial', - 'Aug' => 'a\g\o\s\t', + 'Aug' => '\\a\\g\\o\\s\\t', 'aug' => 'agost', 'august' => 'agost', 'before_yesterday' => 'Abans ièr', - 'Dec' => '\d\e\c\e\m\b\r\e', + 'Dec' => '\\d\\e\\c\\e\\m\\b\\r\\e', 'dec' => 'dec.', 'december' => 'decembre', - 'Feb' => 'f\e\b\r\i\è\r', + 'Feb' => '\\f\\e\\b\\r\\i\\è\\r', 'feb' => 'feb.', 'february' => 'febrièr', - 'format_date' => 'j %s \de\ Y', - 'format_date_hour' => 'j %s \de\ Y \a H\:i', + 'format_date' => 'j \\d\\e %s \\d\\e Y', + 'format_date_hour' => 'j \\d\\e %s \\d\\e Y \\a H\:i', 'fri' => 'dv', - 'Jan' => 'g\e\n\i\è\r', + 'Jan' => '\\g\\e\\n\\i\\è\\r', 'jan' => 'gen.', 'january' => 'genièr', - 'Jul' => 'j\u\l\h\e\t', + 'Jul' => '\\j\\u\\l\\h\\e\\t', 'jul' => 'julh', 'july' => 'julhet', - 'Jun' => 'j\u\n\h', + 'Jun' => '\\j\\u\\n\\h', 'jun' => 'junh', 'june' => 'junh', 'last_3_month' => 'Dempuèi los tres darrièrs meses', @@ -73,22 +73,22 @@ return array( 'last_month' => 'Dempuèi lo mes passat', 'last_week' => 'Dempuèi la setmana passada', 'last_year' => 'Dempuèi l’annada passada', - 'Mar' => 'm\a\r\ç', + 'Mar' => '\\m\\a\\r\\ç', 'mar' => 'març', 'march' => 'març', - 'May' => '\m\a\i', + 'May' => '\\m\\a\\i', 'may' => 'mai', 'may_' => 'mai', 'mon' => 'dl', 'month' => 'meses', - 'Nov' => '\n\o\v\e\m\b\r\e', + 'Nov' => '\\n\\o\\v\\e\\m\\b\\r\\e', 'nov' => 'nov.', 'november' => 'novembre', - 'Oct' => '\o\c\t\ò\b\r\e', + 'Oct' => '\\o\\c\\t\\ò\\b\\r\\e', 'oct' => 'oct.', 'october' => 'octòbre', 'sat' => 'ds', - 'Sep' => '\s\e\t\e\m\b\r\e', + 'Sep' => '\\s\\e\\t\\e\\m\\b\\r\\e', 'sep' => 'set.', 'september' => 'setembre', 'sun' => 'dg', @@ -111,7 +111,7 @@ return array( 'request_failed' => 'Una requèsta a fach meuca, aquò pòt venir d’un problèma de connexion Internet.', 'title_new_articles' => 'FreshRSS : nòus articles !', ), - 'new_article' => 'I a d’articles nòus disponibles, clicatz per actualizar la página.', + 'new_article' => 'I a d’articles nòus disponibles, clicatz per actualizar la pagina.', 'should_be_activated' => 'JavaScript deu èsser activat', ), 'lang' => array( @@ -183,7 +183,7 @@ return array( 'short' => array( 'attention' => 'Atencion !', 'blank_to_disable' => 'Daissar void per desactivar', - 'by_author' => 'Per <em>%s</em>', + 'by_author' => 'Per : ', 'by_default' => 'Per defaut', 'damn' => 'Zut !', 'default_category' => 'Pas triat', diff --git a/app/i18n/oc/sub.php b/app/i18n/oc/sub.php index f9ddf339a..fc5a0cc1f 100644 --- a/app/i18n/oc/sub.php +++ b/app/i18n/oc/sub.php @@ -1,7 +1,7 @@ <?php return array( 'api' => array( - 'documentation' => 'Copiar l’URL seguenta per l’utilizaire dins d’una aisina extèrna.', + 'documentation' => 'Copiatz l’URL seguenta per l’utilizaire dins d’una aisina extèrna.', 'title' => 'API', ), 'bookmarklet' => array( @@ -46,11 +46,11 @@ return array( ), 'websub' => 'Notificaciones instantáneas amb WebSub', 'show' => array( - 'all' => 'Show all feeds', //TODO - Translation - 'error' => 'Show only feeds with error', //TODO - Translation + 'all' => 'Mostrar totes los fluxes', + 'error' => 'Mostrar pas que los fluxes amb errors', ), 'showing' => array( - 'error' => 'Showing only feeds with error', //TODO - Translation + 'error' => 'Afichatge dels articles amb errors solament', ), 'ssl_verify' => 'Verificacion de la seguretat SSL', 'stats' => 'Estatisticas', @@ -64,13 +64,14 @@ return array( 'website' => 'URL del site', ), 'firefox' => array( - 'documentation' => 'Seguir las etapas descrichas <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">aquí</a> per ajustar FreshRSS a la lista dels lectors de flux de Firefox.', + 'documentation' => 'Seguissètz las etapas descrichas <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">aquí</a> per ajustar FreshRSS a la lista dels lectors de flux de Firefox.', 'title' => 'Lector de flux de Firefox', ), 'import_export' => array( 'export' => 'Exportar', 'export_opml' => 'Exportar la lista de fluxes (OPML)', 'export_starred' => 'Exportar los favorits', + 'export_labelled' => 'Exportar los articles etiquetats', 'feed_list' => 'Lista dels %s articles', 'file_to_import' => 'Fichièr d’importar<br />(OPML, JSON o ZIP)', 'file_to_import_no_zip' => 'Fichièr d’importar<br />(OPML o JSON)', @@ -86,7 +87,7 @@ return array( 'subscription_tools' => 'Aisinas d’abonament', ), 'title' => array( - '_' => 'Gestión dels abonaments', + '_' => 'Gestion dels abonaments', 'feed_management' => 'Gestion dels fluxes RSS', 'subscription_tools' => 'Aisinas d’abonament', ), diff --git a/app/i18n/pt-br/sub.php b/app/i18n/pt-br/sub.php index 78684c14c..58b2fc1f9 100644 --- a/app/i18n/pt-br/sub.php +++ b/app/i18n/pt-br/sub.php @@ -68,6 +68,7 @@ return array( 'export' => 'Exportar', 'export_opml' => 'Exporta a lista dos feeds (OPML)', 'export_starred' => 'Exportar seus favoritos', + 'export_labelled' => 'Export your labelled articles', //TODO 'feed_list' => 'Lista dos %s artigos', 'file_to_import' => 'Arquivo para importar<br />(OPML, JSON or ZIP)', 'file_to_import_no_zip' => 'Arquivo para importar<br />(OPML or JSON)', diff --git a/app/i18n/ru/sub.php b/app/i18n/ru/sub.php index 7de80586b..62f8a8e3a 100644 --- a/app/i18n/ru/sub.php +++ b/app/i18n/ru/sub.php @@ -72,6 +72,7 @@ return array( 'export' => 'Export', //TODO - Translation 'export_opml' => 'Export list of feeds (OPML)', //TODO - Translation 'export_starred' => 'Export your favourites', //TODO - Translation + 'export_labelled' => 'Export your labelled articles', //TODO 'feed_list' => 'List of %s articles', //TODO - Translation 'file_to_import' => 'File to import<br />(OPML, JSON or ZIP)', //TODO - Translation 'file_to_import_no_zip' => 'File to import<br />(OPML or JSON)', //TODO - Translation diff --git a/app/i18n/tr/sub.php b/app/i18n/tr/sub.php index b5b56f4b8..7f29633be 100644 --- a/app/i18n/tr/sub.php +++ b/app/i18n/tr/sub.php @@ -72,6 +72,7 @@ return array( 'export' => 'Dışa aktar', 'export_opml' => 'Akış listesini dışarı aktar (OPML)', 'export_starred' => 'Favorileri dışarı aktar', + 'export_labelled' => 'Export your labelled articles', //TODO 'feed_list' => '%s makalenin listesi', 'file_to_import' => 'Dosyadan içe aktar<br />(OPML, JSON or ZIP)', 'file_to_import_no_zip' => 'Dosyadan içe aktar<br />(OPML or JSON)', diff --git a/app/i18n/zh-cn/sub.php b/app/i18n/zh-cn/sub.php index e1c176bc6..3a9623468 100644 --- a/app/i18n/zh-cn/sub.php +++ b/app/i18n/zh-cn/sub.php @@ -72,6 +72,7 @@ return array( 'export' => '导出', 'export_opml' => '导出 RSS 源列表 (OPML)', 'export_starred' => '导出你的收藏', + 'export_labelled' => 'Export your labelled articles', //TODO 'feed_list' => '%s 文章列表', 'file_to_import' => '需要导入的文件<br />(OPML, JSON 或 ZIP)', 'file_to_import_no_zip' => '需要导入的文件<br />(OPML 或 JSON)', diff --git a/app/install.php b/app/install.php index dc79c2388..b15cc76ea 100644 --- a/app/install.php +++ b/app/install.php @@ -7,7 +7,8 @@ header("Content-Security-Policy: default-src 'self'"); require(LIB_PATH . '/lib_install.php'); session_name('FreshRSS'); -session_set_cookie_params(0, dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirname($_SERVER['REQUEST_URI'])), null, false, true); +$forwardedPrefix = empty($_SERVER['HTTP_X_FORWARDED_PREFIX']) ? '' : rtrim($_SERVER['HTTP_X_FORWARDED_PREFIX'], '/ '); +session_set_cookie_params(0, $forwardedPrefix . dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirname($_SERVER['REQUEST_URI'])), null, false, true); session_start(); if (isset($_GET['step'])) { diff --git a/app/layout/header.phtml b/app/layout/header.phtml index 6b538851b..410ac1ff0 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -16,7 +16,7 @@ if (FreshRSS_Auth::accessNeedsAction()) { <div class="item title"> <h1> <a href="<?php echo _url('index', 'index'); ?>"> - <img class="logo" src="<?php echo _i('icon', true); ?>" alt="⊚" /> + <img class="logo" src="<?php echo _i('icon', true); ?>" alt="" /> <?php echo FreshRSS_Context::$system_conf->title; ?> </a> </h1> diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index c6c08e3bc..58c4e219a 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -72,7 +72,7 @@ </div> <div class="form-group"> - <label class="group-name" for="theme"><?php echo _t('conf.display.icon.entry'); ?></label> + <label class="group-name"><?php echo _t('conf.display.icon.entry'); ?></label> <table> <thead> <tr> diff --git a/app/views/helpers/export/articles.phtml b/app/views/helpers/export/articles.phtml index 59a2c7ad7..2d1fcd133 100644 --- a/app/views/helpers/export/articles.phtml +++ b/app/views/helpers/export/articles.phtml @@ -16,14 +16,12 @@ $articles = array( echo rtrim(json_encode($articles, $options), " ]}\n\r\t"), "\n"; $first = true; -$tagDAO = FreshRSS_Factory::createTagDao(); -$entryIdsTagNames = $tagDAO->getEntryIdsTagNames($this->entriesRaw); -if ($entryIdsTagNames == false) { - $entryIdsTagNames = array(); +if (empty($this->entryIdsTagNames)) { + $this->entryIdsTagNames = array(); } foreach ($this->entriesRaw as $entryRaw) { - if (empty($entryRaw)) { + if ($entryRaw == null) { continue; } $entry = FreshRSS_EntryDAO::daoToEntry($entryRaw); @@ -58,10 +56,11 @@ foreach ($this->entriesRaw as $entryRaw) { 'feedUrl' => $feed == null ? '' : $feed->url(), ) ); + $article['categories'][] = $entry->isRead() ? 'user/-/state/com.google/read' : 'user/-/state/com.google/unread'; if ($entry->isFavorite()) { $article['categories'][] = 'user/-/state/com.google/starred'; } - $tagNames = isset($entryIdsTagNames['e_' . $entry->id()]) ? $entryIdsTagNames['e_' . $entry->id()] : array(); + $tagNames = isset($this->entryIdsTagNames['e_' . $entry->id()]) ? $this->entryIdsTagNames['e_' . $entry->id()] : array(); foreach ($tagNames as $tagName) { $article['categories'][] = 'user/-/label/' . $tagName; } diff --git a/app/views/importExport/index.phtml b/app/views/importExport/index.phtml index c5049e3ea..139e715c5 100644 --- a/app/views/importExport/index.phtml +++ b/app/views/importExport/index.phtml @@ -33,6 +33,11 @@ <?php echo _t('sub.import_export.export_opml'); ?> </label> + <label class="checkbox" for="export_labelled"> + <input type="checkbox" name="export_labelled" id="export_labelled" value="1" <?php echo extension_loaded('zip') ? 'checked="checked"' : ''; ?> /> + <?php echo _t('sub.import_export.export_labelled'); ?> + </label> + <label class="checkbox" for="export_starred"> <input type="checkbox" name="export_starred" id="export_starred" value="1" <?php echo extension_loaded('zip') ? 'checked="checked"' : ''; ?> /> <?php echo _t('sub.import_export.export_starred'); ?> diff --git a/cli/_cli.php b/cli/_cli.php index 72629171c..e8fb6ae42 100644 --- a/cli/_cli.php +++ b/cli/_cli.php @@ -4,7 +4,7 @@ if (php_sapi_name() !== 'cli') { } require(__DIR__ . '/../constants.php'); -require(LIB_PATH . '/lib_rss.php'); +require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader require(LIB_PATH . '/lib_install.php'); Minz_Configuration::register('system', diff --git a/cli/export-opml-for-user.php b/cli/export-opml-for-user.php index fd0993c35..076cffe74 100755 --- a/cli/export-opml-for-user.php +++ b/cli/export-opml-for-user.php @@ -17,7 +17,7 @@ fwrite(STDERR, 'FreshRSS exporting OPML for user “' . $username . "”…\n"); $importController = new FreshRSS_importExport_Controller(); $ok = false; -$ok = $importController->exportFile(true, false, array(), 0, $username); +$ok = $importController->exportFile(true, false, false, array(), 0, $username); invalidateHttpCache($username); diff --git a/cli/export-zip-for-user.php b/cli/export-zip-for-user.php index 916ecbb45..86113d9fa 100755 --- a/cli/export-zip-for-user.php +++ b/cli/export-zip-for-user.php @@ -19,7 +19,7 @@ $importController = new FreshRSS_importExport_Controller(); $ok = false; try { - $ok = $importController->exportFile(true, true, true, + $ok = $importController->exportFile(true, true, true, true, empty($options['max-feed-entries']) ? 100 : intval($options['max-feed-entries']), $username); } catch (FreshRSS_ZipMissing_Exception $zme) { diff --git a/constants.php b/constants.php index 1f498b153..8a49d7c01 100644 --- a/constants.php +++ b/constants.php @@ -2,7 +2,7 @@ //NB: Do not edit; use ./constants.local.php instead. //<Not customisable> -define('FRESHRSS_VERSION', '1.13.0'); +define('FRESHRSS_VERSION', '1.13.1'); define('FRESHRSS_WEBSITE', 'https://freshrss.org'); define('FRESHRSS_WIKI', 'https://freshrss.github.io/FreshRSS/'); @@ -32,6 +32,9 @@ safe_define('FRESHRSS_USERAGENT', 'FreshRSS/' . FRESHRSS_VERSION . ' (' . PHP_OS // PHP text output compression http://php.net/ob_gzhandler (better to do it at Web server level) safe_define('PHP_COMPRESSION', false); +// For cases when syslog is not available +safe_define('COPY_SYSLOG_TO_STDERR', isset($_SERVER['COPY_SYSLOG_TO_STDERR']) ? filter_var($_SERVER['COPY_SYSLOG_TO_STDERR'], FILTER_VALIDATE_BOOLEAN) : false); + // Maximum log file size in Bytes, before it will be divided by two safe_define('MAX_LOG_SIZE', 1048576); diff --git a/data/.gitignore b/data/.gitignore index 76314fc12..0410f3797 100644 --- a/data/.gitignore +++ b/data/.gitignore @@ -1,3 +1,4 @@ +.htpasswd config.php config.php.bak.php force-https.txt diff --git a/docs/en/users/06_Mobile_access.md b/docs/en/users/06_Mobile_access.md index d1b310db3..4a2068404 100644 --- a/docs/en/users/06_Mobile_access.md +++ b/docs/en/users/06_Mobile_access.md @@ -29,7 +29,7 @@ See the [page about the Fever compatible API](06_Fever_API.md) for another possi * If you get *Service Unavailable!*, then check from step 1 again. * With __Apache__: * If you get *FAIL getallheaders!*, the combination of your PHP version and your Web server does not provide access to [`getallheaders`](http://php.net/getallheaders) - * Update to PHP 5.4+, or use PHP as module instead of CGI. Otherwise turn on Apache `mod_rewrite`: + * Update to PHP 5.4+, or use PHP as module instead of CGI. Otherwise turn on Apache `mod_setenvif` (often enabled by default), or `mod_rewrite` with the following procedure: * Allow [`FileInfo` in `.htaccess`](http://httpd.apache.org/docs/trunk/mod/core.html#allowoverride): see the [server setup](../admins/02_Installation.md) again. * Enable [`mod_rewrite`](http://httpd.apache.org/docs/trunk/mod/mod_rewrite.html): * With Debian / Ubuntu: `sudo a2enmod rewrite` diff --git a/docs/fr/users/06_Mobile_access.md b/docs/fr/users/06_Mobile_access.md index 6f7d92ade..4f5d24157 100644 --- a/docs/fr/users/06_Mobile_access.md +++ b/docs/fr/users/06_Mobile_access.md @@ -29,7 +29,7 @@ Voir la [page sur l’API compatible Fever](06_Fever_API.md) pour une autre poss * Si vous obtenez *Service Unavailable!*, retourner à l’étape 6. * Avec __Apache__: * Si vous obtenez *FAIL getallheaders!*, alors la combinaison de votre version de PHP et de votre serveur Web ne permet pas l’accès à [`getallheaders`](http://php.net/getallheaders) - * Utilisez au moins PHP 5.4+, ou utilisez PHP en tant que module plutôt que CGI. Sinon, activer Apache `mod_rewrite` : + * Utilisez au moins PHP 5.4+, ou utilisez PHP en tant que module plutôt que CGI. Sinon, activer Apache `mod_setenvif` (souvent activé par défault), ou `mod_rewrite` avec la procédure suivante : * Autoriser [`FileInfo` dans `.htaccess`](http://httpd.apache.org/docs/trunk/mod/core.html#allowoverride) : revoir [l’installation du serveur](01_Installation.md). * Activer [`mod_rewrite`](http://httpd.apache.org/docs/trunk/mod/mod_rewrite.html) : * Sur Debian / Ubuntu : `sudo a2enmod rewrite` @@ -72,7 +72,7 @@ Tout client supportant une API de type Google Reader. Sélection : # API compatible Google Reader -Exemples de requêtes simples: +Exemples de requêtes simples : ```sh # Authentification utilisant le mot de passe API (Email et Passwd peuvent être passés en GET, ou POST - mieux) diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 3e486d68e..aae3accc6 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -27,23 +27,16 @@ class Minz_Configuration { /** * Parse a file and return its data. * - * If the file does not contain a valid PHP code returning an array, an - * empty array is returned anyway. - * * @param $filename the name of the file to parse. * @return an array of values - * @throws Minz_FileNotExistException if the file does not exist. + * @throws Minz_FileNotExistException if the file does not exist or is invalid. */ public static function load($filename) { - if (!file_exists($filename)) { - throw new Minz_FileNotExistException($filename); - } - - $data = include($filename); + $data = @include($filename); if (is_array($data)) { return $data; } else { - return array(); + throw new Minz_FileNotExistException($filename); } } @@ -117,7 +110,7 @@ class Minz_Configuration { $this->default_filename = $default_filename; $this->_configurationSetter($configuration_setter); - if (!is_null($this->default_filename)) { + if ($this->default_filename != null) { $this->data = self::load($this->default_filename); } @@ -126,7 +119,7 @@ class Minz_Configuration { $this->data, self::load($this->config_filename) ); } catch (Minz_FileNotExistException $e) { - if (is_null($this->default_filename)) { + if ($this->default_filename == null) { throw $e; } } diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index c94f2b646..97b15c4d0 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -61,7 +61,11 @@ class Minz_Session { public static function getCookieDir() { // Get the script_name (e.g. /p/i/index.php) and keep only the path. - $cookie_dir = empty($_SERVER['REQUEST_URI']) ? '/' : $_SERVER['REQUEST_URI']; + $cookie_dir = ''; + if (!empty($_SERVER['HTTP_X_FORWARDED_PREFIX'])) { + $cookie_dir .= rtrim($_SERVER['HTTP_X_FORWARDED_PREFIX'], '/ '); + } + $cookie_dir .= empty($_SERVER['REQUEST_URI']) ? '/' : $_SERVER['REQUEST_URI']; if (substr($cookie_dir, -1) !== '/') { $cookie_dir = dirname($cookie_dir) . '/'; } diff --git a/lib/favicons.php b/lib/favicons.php index fe2e65f1f..7a2d1187e 100644 --- a/lib/favicons.php +++ b/lib/favicons.php @@ -22,6 +22,7 @@ function isImgMime($content) { } function downloadHttp(&$url, $curlOptions = array()) { + prepareSyslog(); syslog(LOG_INFO, 'FreshRSS Favicon GET ' . $url); if (substr($url, 0, 2) === '//') { $url = 'https:' . $url; diff --git a/lib/lib_install.php b/lib/lib_install.php index d51a37e58..6e4df4e9c 100644 --- a/lib/lib_install.php +++ b/lib/lib_install.php @@ -81,6 +81,7 @@ function generateSalt() { function checkDb(&$dbOptions) { $dsn = ''; $driver_options = null; + prepareSyslog(); try { switch ($dbOptions['type']) { case 'mysql': diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 333920c8c..89e9ddfea 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -202,12 +202,19 @@ function html_only_entity_decode($text) { return strtr($text, $htmlEntitiesOnly); } +function prepareSyslog() { + return COPY_SYSLOG_TO_STDERR ? openlog("FreshRSS", LOG_PERROR | LOG_PID, LOG_USER) : false; +} + function customSimplePie($attributes = array()) { $system_conf = Minz_Configuration::get('system'); $limits = $system_conf->limits; $simplePie = new SimplePie(); $simplePie->set_useragent(FRESHRSS_USERAGENT); $simplePie->set_syslog($system_conf->simplepie_syslog_enabled); + if ($system_conf->simplepie_syslog_enabled) { + prepareSyslog(); + } $simplePie->set_cache_location(CACHE_PATH); $simplePie->set_cache_duration($limits['cache_duration']); @@ -364,9 +371,9 @@ function get_user_configuration($username) { join_path(FRESHRSS_PATH, 'config-user.default.php')); } catch (Minz_ConfigurationNamespaceException $e) { // namespace already exists, do nothing. - Minz_Log::warning($e->getMessage()); + Minz_Log::warning($e->getMessage(), USERS_PATH . '/_/log.txt'); } catch (Minz_FileNotExistException $e) { - Minz_Log::warning($e->getMessage()); + Minz_Log::warning($e->getMessage(), USERS_PATH . '/_/log.txt'); return null; } @@ -375,14 +382,13 @@ function get_user_configuration($username) { function httpAuthUser() { - if (isset($_SERVER['REMOTE_USER'])) { + if (!empty($_SERVER['REMOTE_USER'])) { return $_SERVER['REMOTE_USER']; - } - - if (isset($_SERVER['REDIRECT_REMOTE_USER'])) { + } elseif (!empty($_SERVER['REDIRECT_REMOTE_USER'])) { return $_SERVER['REDIRECT_REMOTE_USER']; + } elseif (!empty($_SERVER['HTTP_X_WEBAUTH_USER'])) { + return $_SERVER['HTTP_X_WEBAUTH_USER']; } - return ''; } diff --git a/p/api/.htaccess b/p/api/.htaccess index 41b653d96..937983ec9 100644 --- a/p/api/.htaccess +++ b/p/api/.htaccess @@ -1,4 +1,9 @@ -<IfModule mod_rewrite.c> - RewriteEngine on - RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +<IfModule mod_setenvif.c> + SetEnvIf "^Authorization$" "(.*)" HTTP_AUTHORIZATION=$1 +</IfModule> +<IfModule !mod_setenvif.c> + <IfModule mod_rewrite.c> + RewriteEngine on + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + </IfModule> </IfModule> diff --git a/p/api/greader.php b/p/api/greader.php index 7cd312f2c..d41430d3c 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -143,14 +143,11 @@ function checkCompatibility() { Minz_Log::warning('checkCompatibility() ' . debugInfo(), API_LOG); header('Content-Type: text/plain; charset=UTF-8'); if (PHP_INT_SIZE < 8 && !function_exists('gmp_init')) { - die('FAIL 64-bit or GMP extension!'); + die('FAIL 64-bit or GMP extension! Wrong PHP configuration.'); } - if ((!array_key_exists('HTTP_AUTHORIZATION', $_SERVER)) && //Apache mod_rewrite trick should be fine - (!array_key_exists('REDIRECT_HTTP_AUTHORIZATION', $_SERVER)) && //Apache mod_rewrite with FCGI - (empty($_SERVER['SERVER_SOFTWARE']) || (stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') === false)) && //nginx should be fine - (empty($_SERVER['SERVER_SOFTWARE']) || (stripos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') === false)) && //lighttpd should be fine - ((!function_exists('getallheaders')) || (stripos(php_sapi_name(), 'cgi') !== false))) { //Main problem is Apache/CGI mode - die('FAIL getallheaders! (probably)'); + $headerAuth = headerVariable('Authorization', 'GoogleLogin_auth'); + if ($headerAuth == '') { + die('FAIL get HTTP Authorization header! Wrong Web server configuration.'); } echo 'PASS'; exit(); @@ -913,6 +910,10 @@ FreshRSS_Context::$system_conf = Minz_Configuration::get('system'); if (!FreshRSS_Context::$system_conf->api_enabled) { serviceUnavailable(); +} elseif (count($pathInfos) < 3) { + badRequest(); +} elseif ($pathInfos[1] === 'check' && $pathInfos[2] === 'compatibility') { + checkCompatibility(); } ini_set('session.use_cookies', '0'); @@ -927,9 +928,7 @@ if ($user !== '') { Minz_Session::_param('currentUser', $user); -if (count($pathInfos) < 3) { - badRequest(); -} elseif ($pathInfos[1] === 'accounts') { +if ($pathInfos[1] === 'accounts') { if (($pathInfos[2] === 'ClientLogin') && isset($_REQUEST['Email']) && isset($_REQUEST['Passwd'])) { clientLogin($_REQUEST['Email'], $_REQUEST['Passwd']); } @@ -1088,8 +1087,6 @@ if (count($pathInfos) < 3) { userInfo(); break; } -} elseif ($pathInfos[1] === 'check' && $pathInfos[2] === 'compatibility') { - checkCompatibility(); } badRequest(); diff --git a/p/api/index.php b/p/api/index.php index ee37b794b..d441099d7 100644 --- a/p/api/index.php +++ b/p/api/index.php @@ -5,6 +5,18 @@ <title>FreshRSS API endpoints</title> <meta name="robots" content="noindex" /> <link rel="start" href="../i/" /> +<script src="../scripts/api.js" defer="defer"></script> +<script id="jsonVars" type="application/json"> +<?php +require(__DIR__ . '/../../constants.php'); +require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader +Minz_Configuration::register('system', DATA_PATH . '/config.php', FRESHRSS_PATH . '/config.default.php'); +echo json_encode(array( + 'greader' => Minz_Url::display('/api/greader.php', 'php', true), + 'fever' => Minz_Url::display('/api/fever.php', 'php', true), + )); +?> +</script> </head> <body> @@ -14,17 +26,11 @@ <dl> <dt>Your API address:</dt> <dd><?php -require(__DIR__ . '/../../constants.php'); -require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader -Minz_Configuration::register('system', DATA_PATH . '/config.php', FRESHRSS_PATH . '/config.default.php'); echo Minz_Url::display('/api/greader.php', 'html', true); ?></dd> +<dt>Google Reader API configuration test:</dt> +<dd id="greaderOutput">?</dd> </dl> -<ul> -<li><a href="greader.php/check%2Fcompatibility" rel="nofollow">Check full server configuration</a></li> -<li><a href="greader.php/check/compatibility" rel="nofollow">Check partial server -configuration (without <code>%2F</code> support)</a></li> -</ul> <h2>Fever compatible API</h2> <dl> @@ -32,10 +38,9 @@ configuration (without <code>%2F</code> support)</a></li> <dd><?php echo Minz_Url::display('/api/fever.php', 'html', true); ?></dd> +<dt>Fever API configuration test:</dt> +<dd id="feverOutput">?</dd> </dl> -<ul> -<li><a href="fever.php?api" rel="nofollow">Test</a></li> -</ul> </body> </html> diff --git a/p/i/.gitignore b/p/i/.gitignore index 03c88fd7a..b76d11b5a 100644 --- a/p/i/.gitignore +++ b/p/i/.gitignore @@ -1 +1 @@ -.htaccess +.ht* diff --git a/p/i/index.php b/p/i/index.php index 5bc9c0d76..8a4f529d4 100755 --- a/p/i/index.php +++ b/p/i/index.php @@ -50,5 +50,7 @@ if (file_exists(DATA_PATH . '/do-install.txt')) { echo '### Fatal error! ###<br />', "\n"; Minz_Log::error($e->getMessage()); echo 'See logs files.'; + prepareSyslog(); + syslog(LOG_INFO, 'FreshRSS Fatal error! ' . $e->getMessage()); } } diff --git a/p/scripts/api.js b/p/scripts/api.js new file mode 100644 index 000000000..841b16a6a --- /dev/null +++ b/p/scripts/api.js @@ -0,0 +1,62 @@ +"use strict"; +/* jshint esversion:6, strict:global */ + +function check(url, next) { + if (!url || !next) { + return; + } + const req = new XMLHttpRequest(); + req.open('GET', url, true); + req.setRequestHeader('Authorization', 'GoogleLogin auth=test/1'); + req.onerror = function (e) { + next('FAIL: HTTP ' + e); + }; + req.onload = function () { + if (this.status == 200) { + next(this.response); + } else { + next('FAIL: HTTP error ' + this.status + ' ' + this.statusText); + } + }; + req.send(); +} + +const jsonVars = JSON.parse(document.getElementById('jsonVars').innerHTML); + +check(jsonVars.greader + '/check/compatibility', function next(result1) { + const greaderOutput = document.getElementById('greaderOutput'); + if (result1 === 'PASS') { + greaderOutput.innerHTML = '✔️ ' + result1; + } else { + check(jsonVars.greader + '/check%2Fcompatibility', function next(result2) { + if (result2 === 'PASS') { + greaderOutput.innerHTML = '⚠️ WARN: no <code>%2F</code> support, so some clients will not work!'; + } else { + check('./greader.php/check/compatibility', function next(result3) { + if (result3 === 'PASS') { + greaderOutput.innerHTML = '⚠️ WARN: Probable invalid base URL in ./data/config.php'; + } else { + greaderOutput.innerHTML = '❌ ' + result1; + } + }); + } + }); + } + }); + +check(jsonVars.fever + '?api', function next(result1) { + const feverOutput = document.getElementById('feverOutput'); + try { + JSON.parse(result1); + feverOutput.innerHTML = '✔️ PASS'; + } catch (ex) { + check('./fever.php?api', function next(result2) { + try { + JSON.parse(result2); + feverOutput.innerHTML = '⚠️ WARN: Probable invalid base URL in ./data/config.php'; + } catch (ex) { + feverOutput.innerHTML = '❌ ' + result1; + } + }); + } + }); diff --git a/p/scripts/main.js b/p/scripts/main.js index 4ba329dc1..6cab2e55a 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -237,6 +237,9 @@ function mark_favorite(active) { }); } +var freshrssOpenArticleEvent = document.createEvent('Event'); +freshrssOpenArticleEvent.initEvent('freshrss:openArticle', true, true); + function toggleContent(new_active, old_active, skipping) { // If skipping, move current without activating or marking as read if (new_active.length === 0) { @@ -299,8 +302,11 @@ function toggleContent(new_active, old_active, skipping) { } } - if (context.auto_mark_article && new_active.hasClass('active') && !skipping) { - mark_read(new_active, true); + if (new_active.hasClass('active') && !skipping) { + if (context.auto_mark_article) { + mark_read(new_active, true); + } + new_active[0].dispatchEvent(freshrssOpenArticleEvent); } } @@ -543,7 +549,7 @@ function init_column_categories() { } }); $(this).parent().next(".tree-folder-items").slideToggle(300, function () { - //Workaround for Gecko bug in Firefox 64-65(+?): + //Workaround for Gecko bug 1514498 in Firefox 64 var sidebar = document.getElementById('sidebar'); if (sidebar && sidebar.scrollHeight > sidebar.clientHeight && //if needs scrollbar sidebar.scrollWidth >= sidebar.offsetWidth) { //but no scrollbar |
