summaryrefslogtreecommitdiff
path: root/classes
diff options
context:
space:
mode:
Diffstat (limited to 'classes')
-rwxr-xr-xclasses/article.php78
-rw-r--r--classes/backend.php22
-rw-r--r--classes/db/prefs.php4
-rw-r--r--classes/dbupdater.php19
-rw-r--r--classes/digest.php15
-rw-r--r--classes/dlg.php52
-rw-r--r--classes/feedparser.php58
-rwxr-xr-xclasses/feeds.php234
-rwxr-xr-xclasses/handler/public.php121
-rwxr-xr-xclasses/logger/sql.php8
-rw-r--r--classes/mailer.php3
-rw-r--r--classes/opml.php72
-rw-r--r--classes/plugin.php19
-rwxr-xr-xclasses/pluginhost.php10
-rwxr-xr-xclasses/pref/feeds.php392
-rwxr-xr-xclasses/pref/filters.php178
-rw-r--r--classes/pref/labels.php54
-rw-r--r--classes/pref/prefs.php192
-rw-r--r--classes/pref/users.php147
-rwxr-xr-xclasses/rpc.php20
-rwxr-xr-xclasses/rssutils.php87
21 files changed, 836 insertions, 949 deletions
diff --git a/classes/article.php b/classes/article.php
index fd693a4fe..c23a1b820 100755
--- a/classes/article.php
+++ b/classes/article.php
@@ -100,15 +100,12 @@ class Article extends Handler_Protected {
$pluginhost->load_all(PluginHost::KIND_ALL, $owner_uid);
$pluginhost->load_data();
- $af_readability = $pluginhost->get_plugin("Af_Readability");
+ foreach ($pluginhost->get_hooks(PluginHost::HOOK_GET_FULL_TEXT) as $p) {
+ $extracted_content = $p->hook_get_full_text($url);
- if ($af_readability) {
- $enable_share_anything = $pluginhost->get($af_readability, "enable_share_anything");
-
- if ($enable_share_anything) {
- $extracted_content = $af_readability->extract_content($url);
-
- if ($extracted_content) $content = $extracted_content;
+ if ($extracted_content) {
+ $content = $extracted_content;
+ break;
}
}
}
@@ -151,6 +148,16 @@ class Article extends Handler_Protected {
content = ?, content_hash = ? WHERE id = ?");
$sth->execute([$content, $content_hash, $ref_id]);
+ if (DB_TYPE == "pgsql"){
+ $sth = $pdo->prepare("UPDATE ttrss_entries
+ SET tsvector_combined = to_tsvector( :ts_content)
+ WHERE id = :id");
+ $params = [
+ ":ts_content" => mb_substr(strip_tags($content ), 0, 900000),
+ ":id" => $ref_id];
+ $sth->execute($params);
+ }
+
$sth = $pdo->prepare("UPDATE ttrss_user_entries SET published = true,
last_published = NOW() WHERE
int_id = ? AND owner_uid = ?");
@@ -186,7 +193,15 @@ class Article extends Handler_Protected {
if ($row = $sth->fetch()) {
$ref_id = $row["id"];
-
+ if (DB_TYPE == "pgsql"){
+ $sth = $pdo->prepare("UPDATE ttrss_entries
+ SET tsvector_combined = to_tsvector( :ts_content)
+ WHERE id = :id");
+ $params = [
+ ":ts_content" => mb_substr(strip_tags($content ), 0, 900000),
+ ":id" => $ref_id];
+ $sth->execute($params);
+ }
$sth = $pdo->prepare("INSERT INTO ttrss_user_entries
(ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache,
last_read, note, unread, last_published)
@@ -211,8 +226,6 @@ class Article extends Handler_Protected {
function editArticleTags() {
- print __("Tags for this article (separated by commas):")."<br>";
-
$param = clean($_REQUEST['param']);
$tags = Article::get_article_tags($param);
@@ -223,23 +236,22 @@ class Article extends Handler_Protected {
print_hidden("op", "article");
print_hidden("method", "setArticleTags");
- print "<table width='100%'><tr><td>";
+ print "<header class='horizontal'>" . __("Tags for this article (separated by commas):")."</header>";
- print "<textarea dojoType=\"dijit.form.SimpleTextarea\" rows='4'
- style='height : 100px; font-size : 12px; width : 98%' id=\"tags_str\"
+ print "<section>";
+ print "<textarea dojoType='dijit.form.SimpleTextarea' rows='4'
+ style='height : 100px; font-size : 12px; width : 98%' id='tags_str'
name='tags_str'>$tags_str</textarea>
- <div class=\"autocomplete\" id=\"tags_choices\"
- style=\"display:none\"></div>";
-
- print "</td></tr></table>";
-
- print "<div class='dlgButtons'>";
-
- print "<button dojoType=\"dijit.form.Button\"
- onclick=\"dijit.byId('editTagsDlg').execute()\">".__('Save')."</button> ";
- print "<button dojoType=\"dijit.form.Button\"
+ <div class='autocomplete' id='tags_choices'
+ style='display:none'></div>";
+ print "</section>";
+
+ print "<footer>";
+ print "<button dojoType='dijit.form.Button'
+ type='submit' class='alt-primary' onclick=\"dijit.byId('editTagsDlg').execute()\">".__('Save')."</button> ";
+ print "<button dojoType='dijit.form.Button'
onclick=\"dijit.byId('editTagsDlg').hide()\">".__('Cancel')."</button>";
- print "</div>";
+ print "</footer>";
}
@@ -519,7 +531,7 @@ class Article extends Handler_Protected {
$rv .= "<br clear='both'/>";
}
- $rv .= "<div class=\"attachments\" dojoType=\"dijit.form.DropDownButton\">".
+ $rv .= "<div class=\"attachments\" dojoType=\"fox.form.DropDownButton\">".
"<span>" . __('Attachments')."</span>";
$rv .= "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
@@ -700,17 +712,17 @@ class Article extends Handler_Protected {
$ids_qmarks = arr_qmarks($ids);
- if ($cmode == 0) {
+ if ($cmode == 1) {
$sth = $pdo->prepare("UPDATE ttrss_user_entries SET
- unread = false,last_read = NOW()
- WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
- } else if ($cmode == 1) {
+ unread = true
+ WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
+ } else if ($cmode == 2) {
$sth = $pdo->prepare("UPDATE ttrss_user_entries SET
- unread = true
- WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
+ unread = NOT unread,last_read = NOW()
+ WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
} else {
$sth = $pdo->prepare("UPDATE ttrss_user_entries SET
- unread = NOT unread,last_read = NOW()
+ unread = false,last_read = NOW()
WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
}
diff --git a/classes/backend.php b/classes/backend.php
index bd6b1ff19..5bd724728 100644
--- a/classes/backend.php
+++ b/classes/backend.php
@@ -7,13 +7,18 @@ class Backend extends Handler {
}
function digestTest() {
- header("Content-type: text/html");
-
- $rv = Digest::prepare_headlines_digest($_SESSION['uid'], 1, 1000);
+ if (isset($_SESSION['uid'])) {
+ header("Content-type: text/html");
- $rv[3] = "<pre>" . $rv[3] . "</pre>";
+ $rv = Digest::prepare_headlines_digest($_SESSION['uid'], 1, 1000);
- print_r($rv);
+ print "<h1>HTML</h1>";
+ print $rv[0];
+ print "<h1>Plain text</h1>";
+ print "<pre>".$rv[3]."</pre>";
+ } else {
+ print error_json(6);
+ }
}
private function display_main_help() {
@@ -149,13 +154,10 @@ class Backend extends Handler {
print "</ul>";
}
- print "<div class='dlgButtons'>";
- print "<button dojoType='dijit.form.Button' style='float : left' class='alt-info' onclick='window.open(\"https://tt-rss.org/wiki/InterfaceTips\")'>
- <i class='material-icons'>help</i> ".__("More info...")."</button>";
-
+ print "<footer class='text-center'>";
print "<button dojoType='dijit.form.Button'
onclick=\"return dijit.byId('helpDlg').hide()\">".__('Close this window')."</button>";
- print "</div>";
+ print "</footer>";
}
}
diff --git a/classes/db/prefs.php b/classes/db/prefs.php
index 1fddd27c5..fbe7e0eea 100644
--- a/classes/db/prefs.php
+++ b/classes/db/prefs.php
@@ -90,8 +90,10 @@ class Db_Prefs {
return $this->convert($value, $type_name);
+ } else if ($die_on_error) {
+ user_error("Fatal error, unknown preferences key: $pref_name (owner: $user_id)", E_USER_ERROR);
+ return null;
} else {
- user_error("Fatal error, unknown preferences key: $pref_name (owner: $user_id)", $die_on_error ? E_USER_ERROR : E_USER_WARNING);
return null;
}
}
diff --git a/classes/dbupdater.php b/classes/dbupdater.php
index c32afedee..94307aea0 100644
--- a/classes/dbupdater.php
+++ b/classes/dbupdater.php
@@ -42,15 +42,22 @@ class DbUpdater {
foreach ($lines as $line) {
if (strpos($line, "--") !== 0 && $line) {
- if (!$this->pdo->query($line)) {
+
+ if ($html_output)
+ print "<pre>$line</pre>";
+ else
+ Debug::log("> $line");
+
+ try {
+ $this->pdo->query($line); // PDO returns errors as exceptions now
+ } catch (PDOException $e) {
if ($html_output) {
- print_notice("Query: $line");
- print_error("Error: " . implode(", ", $this->pdo->errorInfo()));
+ print "<div class='text-error'>Error: " . $e->getMessage() . "</div>";
} else {
- Debug::log("Query: $line");
- Debug::log("Error: " . implode(", ", $this->pdo->errorInfo()));
+ Debug::log("Error: " . $e->getMessage());
}
+ $this->pdo->rollBack();
return false;
}
}
@@ -73,4 +80,4 @@ class DbUpdater {
}
}
-} \ No newline at end of file
+}
diff --git a/classes/digest.php b/classes/digest.php
index 0d2cac77e..f2533d160 100644
--- a/classes/digest.php
+++ b/classes/digest.php
@@ -166,6 +166,15 @@ class Digest
$line['feed_title'] = $line['cat_title'] . " / " . $line['feed_title'];
}
+ $article_labels = Article::get_article_labels($line["ref_id"], $user_id);
+ $article_labels_formatted = "";
+
+ if (is_array($article_labels) && count($article_labels) > 0) {
+ $article_labels_formatted = implode(", ", array_map(function($a) {
+ return $a[1];
+ }, $article_labels));
+ }
+
$tpl->setVariable('FEED_TITLE', $line["feed_title"]);
$tpl->setVariable('ARTICLE_TITLE', $line["title"]);
$tpl->setVariable('ARTICLE_LINK', $line["link"]);
@@ -174,6 +183,7 @@ class Digest
truncate_string(strip_tags($line["content"]), 300));
// $tpl->setVariable('ARTICLE_CONTENT',
// strip_tags($article_content));
+ $tpl->setVariable('ARTICLE_LABELS', $article_labels_formatted, true);
$tpl->addBlock('article');
@@ -181,8 +191,9 @@ class Digest
$tpl_t->setVariable('ARTICLE_TITLE', $line["title"]);
$tpl_t->setVariable('ARTICLE_LINK', $line["link"]);
$tpl_t->setVariable('ARTICLE_UPDATED', $updated);
-// $tpl_t->setVariable('ARTICLE_EXCERPT',
-// truncate_string(strip_tags($line["excerpt"]), 100));
+ $tpl_t->setVariable('ARTICLE_LABELS', $article_labels_formatted, true);
+ $tpl_t->setVariable('ARTICLE_EXCERPT',
+ truncate_string(strip_tags($line["content"]), 300, "..."), true);
$tpl_t->addBlock('article');
diff --git a/classes/dlg.php b/classes/dlg.php
index 89b88825e..4489af51a 100644
--- a/classes/dlg.php
+++ b/classes/dlg.php
@@ -14,7 +14,7 @@ class Dlg extends Handler_Protected {
}
function importOpml() {
- print __("If you have imported labels and/or filters, you might need to reload preferences to see your new data.") . "</p>";
+ print_notice("If you have imported labels and/or filters, you might need to reload preferences to see your new data.");
print "<div class='panel panel-scrollable'>";
@@ -24,11 +24,11 @@ class Dlg extends Handler_Protected {
print "</div>";
- print "<div align='center'>";
- print "<button dojoType=\"dijit.form.Button\"
+ print "<footer class='text-center'>";
+ print "<button dojoType='dijit.form.Button'
onclick=\"dijit.byId('opmlImportDlg').execute()\">".
__('Close this window')."</button>";
- print "</div>";
+ print "</footer>";
print "</div>";
@@ -38,25 +38,25 @@ class Dlg extends Handler_Protected {
function pubOPMLUrl() {
$url_path = Opml::opml_publish_url();
- print "<div class='dlgSec'>" . __("Your Public OPML URL is:") . "</div>";
+ print "<header>" . __("Your Public OPML URL is:") . "</header>";
- print "<div class='dlgSecCont'>";
+ print "<section>";
print "<div class='panel text-center'>";
print "<a id='pub_opml_url' href='$url_path' target='_blank'>$url_path</a>";
print "</div>";
- print "</div>";
+ print "</section>";
- print "<div align='center'>";
+ print "<footer class='text-center'>";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"return Helpers.OPML.changeKey()\">".
+ print "<button dojoType='dijit.form.Button' onclick=\"return Helpers.OPML.changeKey()\">".
__('Generate new URL')."</button> ";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"return CommonDialogs.closeInfoBox()\">".
+ print "<button dojoType='dijit.form.Button' onclick=\"return CommonDialogs.closeInfoBox()\">".
__('Close this window')."</button>";
- print "</div>";
+ print "</footer>";
//return;
}
@@ -84,12 +84,10 @@ class Dlg extends Handler_Protected {
print "</div>";
- print "<div align='center'>";
-
+ print "<footer class='text-center'>";
print "<button onclick=\"return CommonDialogs.closeInfoBox()\">".
__('Close this window')."</button>";
-
- print "</div>";
+ print "</footer>";
//return;
}
@@ -149,11 +147,11 @@ class Dlg extends Handler_Protected {
print "</div>";
- print "<div align='center'>";
- print "<button dojoType=\"dijit.form.Button\"
+ print "<footer class='text-center'>";
+ print "<button dojoType='dijit.form.Button'
onclick=\"return CommonDialogs.closeInfoBox()\">".
__('Close this window')."</button>";
- print "</div>";
+ print "</footer>";
}
@@ -169,15 +167,15 @@ class Dlg extends Handler_Protected {
$feed_title = Feeds::getFeedTitle($feed_id, $is_cat);
- print "<div class='dlgSec'>".T_sprintf("%s can be accessed via the following secret URL:", $feed_title)."</div>";
+ print "<header>".T_sprintf("%s can be accessed via the following secret URL:", $feed_title)."</header>";
- print "<div class='dlgSecCont'>";
+ print "<section>";
print "<div class='panel text-center'>";
print "<a id='gen_feed_url' href='$url_path' target='_blank'>$url_path</a>";
print "</div>";
- print "</div>";
+ print "</section>";
- print "<div class='dlgButtons'>";
+ print "<footer>";
print "<button dojoType='dijit.form.Button' style='float : left' class='alt-info' onclick='window.open(\"https://tt-rss.org/wiki/GeneratedFeeds\")'>
<i class='material-icons'>help</i> ".__("More info...")."</button>";
@@ -188,7 +186,7 @@ class Dlg extends Handler_Protected {
print "<button dojoType='dijit.form.Button' onclick=\"return CommonDialogs.closeInfoBox()\">".
__('Close this window')."</button>";
- print "</div>";
+ print "</footer>";
//return;
}
@@ -197,12 +195,12 @@ class Dlg extends Handler_Protected {
print_warning(__("You are using default tt-rss password. Please change it in the Preferences (Personal data / Authentication)."));
- print "<div align='center'>";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"document.location.href = 'prefs.php'\">".
+ print "<footer class='text-center'>";
+ print "<button dojoType='dijit.form.Button' onclick=\"document.location.href = 'prefs.php'\">".
__('Open Preferences')."</button> ";
- print "<button dojoType=\"dijit.form.Button\"
+ print "<button dojoType='dijit.form.Button'
onclick=\"return CommonDialogs.closeInfoBox()\">".
__('Close this window')."</button>";
- print "</div>";
+ print "</footeer>";
}
}
diff --git a/classes/feedparser.php b/classes/feedparser.php
index dc67e204e..9677164d3 100644
--- a/classes/feedparser.php
+++ b/classes/feedparser.php
@@ -13,20 +13,6 @@ class FeedParser {
const FEED_RSS = 1;
const FEED_ATOM = 2;
- function normalize_encoding($data) {
- if (preg_match('/^(<\?xml[\t\n\r ].*?encoding[\t\n\r ]*=[\t\n\r ]*["\'])(.+?)(["\'].*?\?>)/s', $data, $matches) === 1) {
-
- $encoding = strtolower($matches[2]);
-
- if (in_array($encoding, array_map('strtolower', mb_list_encodings())))
- $data = mb_convert_encoding($data, 'UTF-8', $encoding);
-
- $data = preg_replace('/^<\?xml[\t\n\r ].*?\?>/s', $matches[1] . "UTF-8" . $matches[3] , $data);
- }
-
- return $data;
- }
-
function __construct($data) {
libxml_use_internal_errors(true);
libxml_clear_errors();
@@ -37,46 +23,6 @@ class FeedParser {
$error = libxml_get_last_error();
- // libxml compiled without iconv?
- if ($error && $error->code == 32) {
- $data = $this->normalize_encoding($data);
-
- if ($data) {
- libxml_clear_errors();
-
- $this->doc = new DOMDocument();
- $this->doc->loadXML($data);
-
- $error = libxml_get_last_error();
- }
- }
-
- // some terrible invalid unicode entity?
- if ($error) {
- foreach (libxml_get_errors() as $err) {
- if ($err->code == 9) {
- // if the source feed is not in utf8, next conversion will fail
- $data = $this->normalize_encoding($data);
-
- // remove dangling bytes
- $data = mb_convert_encoding($data, 'UTF-8', 'UTF-8');
-
- // apparently not all UTF-8 characters are valid for XML
- $data = preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $data);
-
- if ($data) {
- libxml_clear_errors();
-
- $this->doc = new DOMDocument();
- $this->doc->loadXML($data);
-
- $error = libxml_get_last_error();
- }
- break;
- }
- }
- }
-
if ($error) {
foreach (libxml_get_errors() as $error) {
if ($error->level == LIBXML_ERR_FATAL) {
@@ -237,10 +183,12 @@ class FeedParser {
}
}
+ // libxml may have invalid unicode data in error messages
function error() {
- return $this->error;
+ return UConverter::transcode($this->error, 'UTF-8', 'UTF-8');
}
+ // WARNING: may return invalid unicode data
function errors() {
return $this->libxml_errors;
}
diff --git a/classes/feeds.php b/classes/feeds.php
index aac9b627f..86fa45ea7 100755
--- a/classes/feeds.php
+++ b/classes/feeds.php
@@ -6,7 +6,7 @@ class Feeds extends Handler_Protected {
private $params;
function csrf_ignore($method) {
- $csrf_ignored = array("index", "feedbrowser", "quickaddfeed", "search");
+ $csrf_ignored = array("index", "quickaddfeed", "search");
return array_search($method, $csrf_ignored) !== false;
}
@@ -54,7 +54,7 @@ class Feeds extends Handler_Protected {
$reply .= "<span class=\"right\">";
$reply .= "<span id='selected_prompt'></span>";
$reply .= "&nbsp;";
- $reply .= "<select dojoType=\"dijit.form.Select\"
+ $reply .= "<select dojoType=\"fox.form.Select\"
onchange=\"Headlines.onActionChanged(this)\">";
$reply .= "<option value=\"0\" disabled='1'>".__('Select...')."</option>";
@@ -210,6 +210,8 @@ class Feeds extends Handler_Protected {
$highlight_words = $qfh_ret[5];
$reply['first_id'] = $qfh_ret[6];
$reply['is_vfeed'] = $qfh_ret[7];
+ $query_error_override = $qfh_ret[8];
+
$reply['search_query'] = [$search, $search_language];
$reply['vfeed_group_enabled'] = $vfeed_group_enabled;
@@ -387,28 +389,32 @@ class Feeds extends Handler_Protected {
if (is_object($result)) {
- switch ($view_mode) {
- case "unread":
- $message = __("No unread articles found to display.");
- break;
- case "updated":
- $message = __("No updated articles found to display.");
- break;
- case "marked":
- $message = __("No starred articles found to display.");
- break;
- default:
- if ($feed < LABEL_BASE_INDEX) {
- $message = __("No articles found to display. You can assign articles to labels manually from article header context menu (applies to all selected articles) or use a filter.");
- } else {
- $message = __("No articles found to display.");
- }
+ if ($query_error_override) {
+ $message = $query_error_override;
+ } else {
+ switch ($view_mode) {
+ case "unread":
+ $message = __("No unread articles found to display.");
+ break;
+ case "updated":
+ $message = __("No updated articles found to display.");
+ break;
+ case "marked":
+ $message = __("No starred articles found to display.");
+ break;
+ default:
+ if ($feed < LABEL_BASE_INDEX) {
+ $message = __("No articles found to display. You can assign articles to labels manually from article header context menu (applies to all selected articles) or use a filter.");
+ } else {
+ $message = __("No articles found to display.");
+ }
+ }
}
if (!$offset && $message) {
$reply['content'] = "<div class='whiteBox'>$message";
- $reply['content'] .= "<p><span class=\"insensitive\">";
+ $reply['content'] .= "<p><span class=\"text-muted\">";
$sth = $this->pdo->prepare("SELECT " . SUBSTRING_FOR_DATE . "(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds
WHERE owner_uid = ?");
@@ -428,7 +434,7 @@ class Feeds extends Handler_Protected {
if ($num_errors > 0) {
$reply['content'] .= "<br/>";
- $reply['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"CommonDialogs.showFeedsWithErrors()\">" .
+ $reply['content'] .= "<a class=\"text-muted\" href=\"#\" onclick=\"CommonDialogs.showFeedsWithErrors()\">" .
__('Some feeds have update errors (click for details)') . "</a>";
}
$reply['content'] .= "</span></p></div>";
@@ -585,7 +591,7 @@ class Feeds extends Handler_Protected {
$reply['headlines']['content'] = "<div class='whiteBox'>".__('No feed selected.');
- $reply['headlines']['content'] .= "<p><span class=\"insensitive\">";
+ $reply['headlines']['content'] .= "<p><span class=\"text-muted\">";
$sth = $this->pdo->prepare("SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds
WHERE owner_uid = ?");
@@ -605,7 +611,7 @@ class Feeds extends Handler_Protected {
if ($num_errors > 0) {
$reply['headlines']['content'] .= "<br/>";
- $reply['headlines']['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"CommonDialogs.showFeedsWithErrors()\">".
+ $reply['headlines']['content'] .= "<a class=\"text-muted\" href=\"#\" onclick=\"CommonDialogs.showFeedsWithErrors()\">".
__('Some feeds have update errors (click for details)')."</a>";
}
$reply['headlines']['content'] .= "</span></p>";
@@ -645,132 +651,72 @@ class Feeds extends Handler_Protected {
print_notice("Provided URL is a HTML page referencing multiple feeds, please select required feed from the dropdown menu below.");
print "<p></div>";
- //print "<div class=\"dlgSec\">".__("Feed or site URL")."</div>";
- print "<div class=\"dlgSecCont\">";
-
- print "<div style='float : right'>
- <img style='display : none'
- id='feed_add_spinner' src='images/indicator_white.gif'></div>";
+ print "<section>";
print "<fieldset>";
-
- print "<input style=\"font-size : 16px; width : 540px;\"
+ print "<div style='float : right'><img style='display : none' id='feed_add_spinner' src='images/indicator_white.gif'></div>";
+ print "<input style='font-size : 16px; width : 500px;'
placeHolder=\"".__("Feed or site URL")."\"
- dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" name=\"feed\" id=\"feedDlg_feedUrl\">";
+ dojoType='dijit.form.ValidationTextBox' required='1' name='feed' id='feedDlg_feedUrl'>";
print "</fieldset>";
print "<fieldset>";
if (get_pref('ENABLE_FEED_CATS')) {
- print "<label>" . __('Place in category:') . "</label> ";
- print_feed_cat_select("cat", false, 'dojoType="dijit.form.Select"');
+ print "<label class='inline'>" . __('Place in category:') . "</label> ";
+ print_feed_cat_select("cat", false, 'dojoType="fox.form.Select"');
}
print "</fieldset>";
- print "</div>";
+ print "</section>";
print '<div id="feedDlg_feedsContainer" style="display : none">
-
- <div class="dlgSec">' . __('Available feeds') . '</div>
- <div class="dlgSecCont">
+ <header>' . __('Available feeds') . '</header>
+ <section>
<fieldset>
<select id="feedDlg_feedContainerSelect"
- dojoType="dijit.form.Select" size="3">
+ dojoType="fox.form.Select" size="3">
<script type="dojo/method" event="onChange" args="value">
dijit.byId("feedDlg_feedUrl").attr("value", value);
</script>
</select>
</fieldset>
- </div></div>';
+ </section>
+ </div>';
print "<div id='feedDlg_loginContainer' style='display : none'>
-
- <div class=\"dlgSec\">".__("Authentication")."</div>
- <div class=\"dlgSecCont\">
+ <section>
<fieldset>
<input dojoType=\"dijit.form.TextBox\" name='login'\"
placeHolder=\"".__("Login")."\"
autocomplete=\"new-password\"
style=\"width : 10em;\">
- </fieldset><fieldset>
<input
placeHolder=\"".__("Password")."\"
dojoType=\"dijit.form.TextBox\" type='password'
autocomplete=\"new-password\"
style=\"width : 10em;\" name='pass'\">
</fieldset>
- </div></div>";
-
+ </section>
+ </div>";
- print "<div style=\"clear : both\">
- <input type=\"checkbox\" name=\"need_auth\" dojoType=\"dijit.form.CheckBox\" id=\"feedDlg_loginCheck\"
+ print "<section>";
+ print "<label>
+ <label class='checkbox'><input type='checkbox' name='need_auth' dojoType='dijit.form.CheckBox' id='feedDlg_loginCheck'
onclick='displayIfChecked(this, \"feedDlg_loginContainer\")'>
- <label for=\"feedDlg_loginCheck\">".
- __('This feed requires authentication.')."</div>";
-
- print "<div class=\"dlgButtons\">
- <button dojoType=\"dijit.form.Button\" class=\"alt-primary\" type=\"submit\" onclick=\"return dijit.byId('feedAddDlg').execute()\">".__('Subscribe')."</button>";
+ ".__('This feed requires authentication.')."</label>";
+ print "</section>";
- if (!(defined('_DISABLE_FEED_BROWSER') && _DISABLE_FEED_BROWSER)) {
- print "<button dojoType=\"dijit.form.Button\" onclick=\"return CommonDialogs.feedBrowser()\">".__('More feeds')."</button>";
- }
+ print "<footer>";
+ print "<button dojoType='dijit.form.Button' class='alt-primary' type='submit'
+ onclick=\"return dijit.byId('feedAddDlg').execute()\">".__('Subscribe')."</button>";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedAddDlg').hide()\">".__('Cancel')."</button>
- </div>";
+ print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('feedAddDlg').hide()\">".__('Cancel')."</button>";
+ print "</footer>";
print "</form>";
-
- //return;
- }
-
- function feedBrowser() {
- if (defined('_DISABLE_FEED_BROWSER') && _DISABLE_FEED_BROWSER) return;
-
- $browser_search = $_REQUEST["search"];
-
- print_hidden("op", "rpc");
- print_hidden("method", "updateFeedBrowser");
-
- print "<div dojoType=\"dijit.Toolbar\">
- <div style='float : right'>
- <img style='display : none'
- id='feed_browser_spinner' src='images/indicator_white.gif'>
- <input name=\"search\" dojoType=\"dijit.form.TextBox\" size=\"20\" type=\"search\"
- onchange=\"dijit.byId('feedBrowserDlg').update()\" value=\"$browser_search\">
- <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('feedBrowserDlg').update()\">".__('Search')."</button>
- </div>";
-
- print " <select name=\"mode\" dojoType=\"dijit.form.Select\" onchange=\"dijit.byId('feedBrowserDlg').update()\">
- <option value='1'>" . __('Popular feeds') . "</option>
- <option value='2'>" . __('Feed archive') . "</option>
- </select> ";
-
- print __("limit:");
-
- print " <select dojoType=\"dijit.form.Select\" name=\"limit\" onchange=\"dijit.byId('feedBrowserDlg').update()\">";
-
- foreach (array(25, 50, 100, 200) as $l) {
- //$issel = ($l == $limit) ? "selected=\"1\"" : "";
- print "<option value=\"$l\">$l</option>";
- }
-
- print "</select> ";
-
- print "</div>";
-
- require_once "feedbrowser.php";
-
- print "<ul class='browseFeedList' id='browseFeedList'>";
- print make_feed_browser("", 25);
- print "</ul>";
-
- print "<div align='center'>
- <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('feedBrowserDlg').execute()\">".__('Subscribe')."</button>
- <button dojoType=\"dijit.form.Button\" style='display : none' id='feed_archive_remove' onclick=\"dijit.byId('feedBrowserDlg').removeFromArchive()\">".__('Remove')."</button>
- <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('feedBrowserDlg').hide()\" >".__('Cancel')."</button></div>";
-
}
function search() {
@@ -781,38 +727,36 @@ class Feeds extends Handler_Protected {
print "<form onsubmit='return false;'>";
- //print "<div class=\"dlgSec\">".__('Look for')."</div>";
-
- print "<div class=\"dlgSecCont\">";
+ print "<section>";
print "<fieldset>";
- print "<input dojoType=\"dijit.form.ValidationTextBox\"
- style=\"font-size : 16px; width : 540px;\"
+ print "<input dojoType='dijit.form.ValidationTextBox' id='search_query'
+ style='font-size : 16px; width : 540px;'
placeHolder=\"".T_sprintf("Search %s...", $this->getFeedTitle($active_feed_id, $is_cat))."\"
- required=\"1\" name=\"query\" type=\"search\" value=''>";
+ name='query' type='search' value=''>";
print "</fieldset>";
-
if (DB_TYPE == "pgsql") {
print "<fieldset>";
- print "<label>" . __("Language:") . "</label>";
- print_select("search_language", "", Pref_Feeds::get_ts_languages(),
- "dojoType='dijit.form.Select' title=\"".__('Used for word stemming')."\"");
+ print "<label class='inline'>" . __("Language:") . "</label>";
+ print_select("search_language", get_pref('DEFAULT_SEARCH_LANGUAGE'), Pref_Feeds::get_ts_languages(),
+ "dojoType='fox.form.Select' title=\"".__('Used for word stemming')."\"");
print "</fieldset>";
}
- print "</div>";
+ print "</section>";
- print "<div class=\"dlgButtons\">";
+ print "<footer>";
if (count(PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH)) == 0) {
print "<button dojoType='dijit.form.Button' style='float : left' class='alt-info' onclick='window.open(\"https://tt-rss.org/wiki/SearchSyntax\")'>
<i class='material-icons'>help</i> ".__("Search syntax")."</button>";
}
- print "<button dojoType=\"dijit.form.Button\" type=\"submit\" class=\"alt-primary\" onclick=\"dijit.byId('searchDlg').execute()\">".__('Search')."</button>
- <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('searchDlg').hide()\">".__('Cancel')."</button>
- </div>";
+ print "<button dojoType='dijit.form.Button' type='submit' class='alt-primary' onclick=\"dijit.byId('searchDlg').execute()\">".__('Search')."</button>
+ <button dojoType='dijit.form.Button' onclick=\"dijit.byId('searchDlg').hide()\">".__('Cancel')."</button>";
+
+ print "</footer>";
print "</form>";
}
@@ -839,6 +783,7 @@ class Feeds extends Handler_Protected {
$rehash_checked = isset($_REQUEST["force_rehash"]) ? "checked" : "";
?>
+ <!DOCTYPE html>
<html>
<head>
<?php echo stylesheet_tag("css/default.css") ?>
@@ -905,9 +850,23 @@ class Feeds extends Handler_Protected {
$pdo = Db::pdo();
- // Todo: all this interval stuff needs some generic generator function
+ if (is_array($search) && $search[0]) {
+ $search_qpart = "";
- $search_qpart = is_array($search) && $search[0] ? search_to_sql($search[0], $search[1])[0] : 'true';
+ foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH) as $plugin) {
+ list($search_qpart, $search_words) = $plugin->hook_search($search[0]);
+ break;
+ }
+
+ // fall back in case of no plugins
+ if (!$search_qpart) {
+ list($search_qpart, $search_words) = search_to_sql($search[0], $search[1]);
+ }
+ } else {
+ $search_qpart = "true";
+ }
+
+ // TODO: all this interval stuff needs some generic generator function
switch ($mode) {
case "1day":
@@ -1172,6 +1131,7 @@ class Feeds extends Handler_Protected {
global $fetch_last_error;
global $fetch_last_error_content;
+ global $fetch_last_content_type;
$pdo = Db::pdo();
@@ -1193,7 +1153,7 @@ class Feeds extends Handler_Protected {
return array("code" => 5, "message" => $fetch_last_error);
}
- if (is_html($contents)) {
+ if (mb_strpos($fetch_last_content_type, "html") !== FALSE && is_html($contents)) {
$feedUrls = get_feeds_from_html($url, $contents);
if (count($feedUrls) == 0) {
@@ -1482,10 +1442,13 @@ class Feeds extends Handler_Protected {
$ext_tables_part = "";
$limit_query_part = "";
+ $query_error_override = "";
- $search_words = array();
+ $search_words = [];
if ($search) {
+ $search_query_part = "";
+
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH) as $plugin) {
list($search_query_part, $search_words) = $plugin->hook_search($search);
break;
@@ -1495,6 +1458,21 @@ class Feeds extends Handler_Protected {
if (!$search_query_part) {
list($search_query_part, $search_words) = search_to_sql($search, $search_language);
}
+
+ if (DB_TYPE == "pgsql") {
+ $test_sth = $pdo->prepare("select $search_query_part
+ FROM ttrss_entries, ttrss_user_entries WHERE id = ref_id limit 1");
+
+ try {
+ $test_sth->execute();
+ } catch (PDOException $e) {
+ // looks like tsquery syntax is invalid
+ $search_query_part = "false";
+
+ $query_error_override = T_sprintf("Incorrect search syntax: %s.", implode(" ", $search_words));
+ }
+ }
+
$search_query_part .= " AND ";
} else {
$search_query_part = "";
@@ -1786,7 +1764,7 @@ class Feeds extends Handler_Protected {
$first_id = (int)$row["id"];
if ($offset > 0 && $first_id && $check_first_id && $first_id != $check_first_id) {
- return array(-1, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id, $vfeed_query_part != "");
+ return array(-1, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id, $vfeed_query_part != "", $query_error_override);
}
}
}
@@ -1875,7 +1853,7 @@ class Feeds extends Handler_Protected {
$res = $pdo->query($query);
}
- return array($res, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id, $vfeed_query_part != "");
+ return array($res, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id, $vfeed_query_part != "", $query_error_override);
}
diff --git a/classes/handler/public.php b/classes/handler/public.php
index 37fe8612a..318cecd72 100755
--- a/classes/handler/public.php
+++ b/classes/handler/public.php
@@ -329,7 +329,7 @@ class Handler_Public extends Handler {
if (!$og_image) {
$tmpdoc = new DOMDocument();
- if (@$tmpdoc->loadHTML(mb_substr($content, 0, 131070))) {
+ if (@$tmpdoc->loadHTML('<?xml encoding="UTF-8">' . mb_substr($content, 0, 131070))) {
$tmpxpath = new DOMXPath($tmpdoc);
$imgs = $tmpxpath->query("//img");
@@ -388,23 +388,29 @@ class Handler_Public extends Handler {
$rv .= "<!DOCTYPE html>
<html><head>
- <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
+ <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
<title>".$line["title"]."</title>".
stylesheet_tag("css/default.css")."
- <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\">
- <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\">";
-
- $rv .= "<meta property=\"og:title\" content=\"".htmlspecialchars($line["title"])."\"/>\n";
- $rv .= "<meta property=\"og:site_name\" content=\"".htmlspecialchars($line["feed_title"])."\"/>\n";
- $rv .= "<meta property=\"og:description\" content=\"".
- htmlspecialchars(truncate_string(strip_tags($line["content"]), 500, "..."))."\"/>\n";
+ <link rel='shortcut icon' type='image/png' href='images/favicon.png'>
+ <link rel='icon' type='image/png' sizes='72x72' href='images/favicon-72px.png'>";
+
+ $rv .= "<meta property='og:title' content=\"".htmlspecialchars(html_entity_decode($line["title"], ENT_NOQUOTES | ENT_HTML401))."\"/>\n";
+ $rv .= "<meta property='og:description' content=\"".
+ htmlspecialchars(
+ truncate_string(
+ preg_replace("/[\r\n\t]/", "",
+ preg_replace("/ {1,}/", " ",
+ strip_tags(html_entity_decode($line["content"], ENT_NOQUOTES | ENT_HTML401))
+ )
+ ), 500, "...")
+ )."\"/>\n";
$rv .= "</head>";
$og_image = $this->get_article_image($enclosures, $line['content'], $line["site_url"]);
if ($og_image) {
- $rv .= "<meta property=\"og:image\" content=\"" . htmlspecialchars($og_image) . "\"/>";
+ $rv .= "<meta property='og:image' content=\"" . htmlspecialchars($og_image) . "\"/>";
}
$rv .= "<body class='flat ttrss_utility ttrss_zoom'>";
@@ -439,7 +445,7 @@ class Handler_Public extends Handler {
/* content */
$lang = $line['lang'] ? $line['lang'] : "en";
- $rv .= "<div class=\"content\" lang=\"$lang\">";
+ $rv .= "<div class='content' lang='$lang'>";
/* content body */
@@ -525,6 +531,7 @@ class Handler_Public extends Handler {
header('Content-Type: text/html; charset=utf-8');
?>
+ <!DOCTYPE html>
<html>
<head>
<title><?php echo __("Share with Tiny Tiny RSS") ?> ?></title>
@@ -612,7 +619,7 @@ class Handler_Public extends Handler {
<fieldset>
<button dojoType='dijit.form.Button' class="alt-primary" type="submit"><?php echo __('Share') ?></button>
<button dojoType='dijit.form.Button' onclick="return window.close()"><?php echo __('Cancel') ?></button>
- <span class="insensitive small"><?php echo __("Shared article will appear in the Published feed.") ?></span>
+ <span class="text-muted small"><?php echo __("Shared article will appear in the Published feed.") ?></span>
</fieldset>
</form>
@@ -622,7 +629,7 @@ class Handler_Public extends Handler {
} else {
- $return = urlencode($_SERVER["REQUEST_URI"])
+ $return = urlencode(make_self_url());
?>
@@ -711,7 +718,9 @@ class Handler_Public extends Handler {
user_error("Failed login attempt for $login from {$_SERVER['REMOTE_ADDR']}", E_USER_WARNING);
}
- if (clean($_REQUEST['return'])) {
+ $return = clean($_REQUEST['return']);
+
+ if ($_REQUEST['return'] && mb_strpos($return, SELF_URL_PATH) === 0) {
header("Location: " . clean($_REQUEST['return']));
} else {
header("Location: " . get_self_url_prefix());
@@ -742,6 +751,7 @@ class Handler_Public extends Handler {
header('Content-Type: text/html; charset=utf-8');
?>
+ <!DOCTYPE html>
<html>
<head>
<title>Tiny Tiny RSS</title>
@@ -770,7 +780,21 @@ class Handler_Public extends Handler {
<?php
if (!$feed_url) {
- print_error("No feed to subscribe to.");
+ ?>
+ <form method="post">
+ <input type="hidden" name="op" value="subscribe">
+ <fieldset>
+ <label>Feed or site URL:</label>
+ <input style="width: 300px" dojoType="dijit.form.ValidationTextBox" required="1" name="feed_url">
+ </fieldset>
+
+ <button class="alt-primary" dojoType="dijit.form.Button" type="submit">
+ <?php echo __("Subscribe") ?>
+ </button>
+
+ <a href="index.php"><?php echo __("Return to Tiny Tiny RSS") ?></a>
+ </form>
+ <?php
} else {
$rc = Feeds::subscribe_to_feed($feed_url);
@@ -814,9 +838,11 @@ class Handler_Public extends Handler {
}
print "</select>";
- print "<button class='alt-primary' dojoType='dijit.form.Button' type='submit'>".__("Subscribe to selected feed")."</button>";
print "</fieldset>";
+ print "<button class='alt-primary' dojoType='dijit.form.Button' type='submit'>".__("Subscribe to selected feed")."</button>";
+ print "<a href='index.php'>".__("Return to Tiny Tiny RSS")."</a>";
+
print "</form>";
}
@@ -832,20 +858,18 @@ class Handler_Public extends Handler {
} else {
$feed_id = 0;
}
- print "<p>";
if ($feed_id) {
- print "<form method='GET' style='float : left' action=\"$tp_uri\">
+ print "<form method='GET' action=\"$tp_uri\">
<input type='hidden' name='tab' value='feedConfig'>
<input type='hidden' name='method' value='editfeed'>
<input type='hidden' name='methodparam' value='$feed_id'>
<button dojoType='dijit.form.Button' class='alt-info' type='submit'>".__("Edit subscription options")."</button>
+ <a href='index.php'>".__("Return to Tiny Tiny RSS")."</a>
</form>";
}
}
- print "<a href='index.php'>".__("Return to Tiny Tiny RSS")."</a>";
-
print "</div></div></body></html>";
} else {
@@ -866,7 +890,7 @@ class Handler_Public extends Handler {
header('Content-Type: text/html; charset=utf-8');
?>
-
+ <!DOCTYPE html>
<html>
<head>
<title>Tiny Tiny RSS</title>
@@ -941,12 +965,12 @@ class Handler_Public extends Handler {
print "<form method='POST' action='public.php'>
<input type='hidden' name='method' value='do'>
<input type='hidden' name='op' value='forgotpass'>
-
+
<fieldset>
<label>".__("Login:")."</label>
<input dojoType='dijit.form.TextBox' type='text' name='login' value='' required>
</fieldset>
-
+
<fieldset>
<label>".__("Email:")."</label>
<input dojoType='dijit.form.TextBox' type='email' name='email' value='' required>
@@ -959,13 +983,13 @@ class Handler_Public extends Handler {
<label>".T_sprintf("How much is %d + %d:", $_SESSION["pwdreset:testvalue1"], $_SESSION["pwdreset:testvalue2"])."</label>
<input dojoType='dijit.form.TextBox' type='text' name='test' value='' required>
</fieldset>
-
+
<hr/>
<fieldset>
<button dojoType='dijit.form.Button' type='submit' class='alt-danger'>".__("Reset password")."</button>
<a href='index.php'>".__("Return to Tiny Tiny RSS")."</a>
</fieldset>
-
+
</form>";
} else if ($method == 'do') {
@@ -1018,7 +1042,7 @@ class Handler_Public extends Handler {
$mailer = new Mailer();
- $rc = $mailer->mail(["to_name" => $login,
+ $rc = $mailer->mail(["to_name" => $login,
"to_address" => $email,
"subject" => __("[tt-rss] Password reset request"),
"message" => $message]);
@@ -1033,8 +1057,6 @@ class Handler_Public extends Handler {
$sth->execute([$resetpass_token_full, $login, $email]);
- //Pref_Users::resetUserPassword($id, false);
-
} else {
print_error("User ID not found.");
}
@@ -1071,6 +1093,7 @@ class Handler_Public extends Handler {
}
?>
+ <!DOCTYPE html>
<html>
<head>
<title>Database Updater</title>
@@ -1116,32 +1139,35 @@ class Handler_Public extends Handler {
if ($op == "performupdate") {
if ($updater->isUpdateRequired()) {
- print "<h2>" . __("Performing updates") . "</h2>";
-
- print "<h3>" . T_sprintf("Updating to schema version %d", SCHEMA_VERSION) . "</h3>";
-
- print "<ul>";
+ print "<h2>" . T_sprintf("Performing updates to version %d", SCHEMA_VERSION) . "</h2>";
for ($i = $updater->getSchemaVersion() + 1; $i <= SCHEMA_VERSION; $i++) {
- print "<li>" . T_sprintf("Performing update up to version %d...", $i);
+ print "<ul>";
+
+ print "<li class='text-info'>" . T_sprintf("Updating to version %d", $i) . "</li>";
+ print "<li>";
$result = $updater->performUpdateTo($i, true);
+ print "</li>";
if (!$result) {
- print "<span class='err'>".__("FAILED!")."</span></li></ul>";
+ print "</ul>";
- print_warning("One of the updates failed. Either retry the process or perform updates manually.");
+ print_error("One of the updates failed. Either retry the process or perform updates manually.");
- print "<a href='index.php'>".__("Return to Tiny Tiny RSS")."</a>";
+ print "<form method='POST'>
+ <input type='hidden' name='subop' value='performupdate'>
+ <button type='submit' dojoType='dijit.form.Button' class='alt-danger' onclick='return confirmOP()'>".__("Try again")."</button>
+ <a href='index.php'>".__("Return to Tiny Tiny RSS")."</a>
+ </form>";
return;
} else {
- print "<span class='ok'>".__("OK!")."</span></li>";
+ print "<li class='text-success'>" . __("Completed.") . "</li>";
+ print "</ul>";
}
}
- print "</ul>";
-
print_notice("Your Tiny Tiny RSS database is now updated to the latest version.");
print "<a href='index.php'>".__("Return to Tiny Tiny RSS")."</a>";
@@ -1154,14 +1180,15 @@ class Handler_Public extends Handler {
} else {
if ($updater->isUpdateRequired()) {
- print "<h2>" . __("Database update required") . "</h2>";
-
- print_notice("<h4>".
- sprintf("Your Tiny Tiny RSS database needs update to the latest version: %d to %d.",
- $updater->getSchemaVersion(), SCHEMA_VERSION).
- "</h4>");
+ print "<h2>".T_sprintf("Tiny Tiny RSS database needs update to the latest version (%d to %d).",
+ $updater->getSchemaVersion(), SCHEMA_VERSION)."</h2>";
- print_warning("Please backup your database before proceeding.");
+ if (DB_TYPE == "mysql") {
+ print_error("<strong>READ THIS:</strong> Due to MySQL limitations, your database is not completely protected while updating. ".
+ "Errors may put it in an inconsistent state requiring manual rollback. <strong>BACKUP YOUR DATABASE BEFORE CONTINUING.</strong>");
+ } else {
+ print_warning("Please backup your database before proceeding.");
+ }
print "<form method='POST'>
<input type='hidden' name='subop' value='performupdate'>
diff --git a/classes/logger/sql.php b/classes/logger/sql.php
index 73552c143..352d71324 100755
--- a/classes/logger/sql.php
+++ b/classes/logger/sql.php
@@ -12,6 +12,14 @@ class Logger_SQL {
$owner_uid = $_SESSION["uid"] ? $_SESSION["uid"] : null;
+ if (DB_TYPE == "mysql")
+ $context = substr($context, 0, 65534);
+
+ // passed error message may contain invalid unicode characters, failing to insert an error here
+ // would break the execution entirely by generating an actual fatal error instead of a E_WARNING etc
+ $errstr = UConverter::transcode($errstr, 'UTF-8', 'UTF-8');
+ $context = UConverter::transcode($context, 'UTF-8', 'UTF-8');
+
$sth = $this->pdo->prepare("INSERT INTO ttrss_error_log
(errno, errstr, filename, lineno, context, owner_uid, created_at) VALUES
(?, ?, ?, ?, ?, ?, NOW())");
diff --git a/classes/mailer.php b/classes/mailer.php
index fbdf111a7..2919eec79 100644
--- a/classes/mailer.php
+++ b/classes/mailer.php
@@ -19,7 +19,8 @@ class Mailer {
$from_combined = $from_name ? "$from_name <$from_address>" : $from_address;
$to_combined = $to_name ? "$to_name <$to_address>" : $to_address;
- Logger::get()->log("Sending mail from $from_combined to $to_combined <$to_address> [$subject]: $message");
+ if (defined('_LOG_SENT_MAIL') && _LOG_SENT_MAIL)
+ Logger::get()->log("Sending mail from $from_combined to $to_combined [$subject]: $message");
// HOOK_SEND_MAIL plugin instructions:
// 1. return 1 or true if mail is handled
diff --git a/classes/opml.php b/classes/opml.php
index fe43a096c..720798065 100644
--- a/classes/opml.php
+++ b/classes/opml.php
@@ -9,10 +9,10 @@ class Opml extends Handler_Protected {
function export() {
$output_name = "tt-rss_".date("Y-m-d").".opml";
- $show_settings = $_REQUEST["include_settings"];
+ $include_settings = $_REQUEST["include_settings"] == "1";
$owner_uid = $_SESSION["uid"];
- $rc = $this->opml_export($output_name, $owner_uid, false, ($show_settings == 1));
+ $rc = $this->opml_export($output_name, $owner_uid, false, $include_settings);
return $rc;
}
@@ -48,7 +48,7 @@ class Opml extends Handler_Protected {
// Export
- private function opml_export_category($owner_uid, $cat_id, $hide_private_feeds=false) {
+ private function opml_export_category($owner_uid, $cat_id, $hide_private_feeds = false, $include_settings = true) {
$cat_id = (int) $cat_id;
@@ -59,15 +59,25 @@ class Opml extends Handler_Protected {
$out = "";
+ $ttrss_specific_qpart = "";
+
if ($cat_id) {
- $sth = $this->pdo->prepare("SELECT title FROM ttrss_feed_categories WHERE id = ?
- AND owner_uid = ?");
+ $sth = $this->pdo->prepare("SELECT title,order_id
+ FROM ttrss_feed_categories WHERE id = ?
+ AND owner_uid = ?");
$sth->execute([$cat_id, $owner_uid]);
$row = $sth->fetch();
$cat_title = htmlspecialchars($row['title']);
+
+ if ($include_settings) {
+ $order_id = (int)$row["order_id"];
+ $ttrss_specific_qpart = "ttrssSortOrder=\"$order_id\"";
+ }
+ } else {
+ $cat_title = "";
}
- if ($cat_title) $out .= "<outline text=\"$cat_title\">\n";
+ if ($cat_title) $out .= "<outline text=\"$cat_title\" $ttrss_specific_qpart>\n";
$sth = $this->pdo->prepare("SELECT id,title
FROM ttrss_feed_categories WHERE
@@ -77,10 +87,10 @@ class Opml extends Handler_Protected {
$sth->execute([':cat' => $cat_id, ':uid' => $owner_uid]);
while ($line = $sth->fetch()) {
- $out .= $this->opml_export_category($owner_uid, $line["id"], $hide_private_feeds);
+ $out .= $this->opml_export_category($owner_uid, $line["id"], $hide_private_feeds, $include_settings);
}
- $fsth = $this->pdo->prepare("select title, feed_url, site_url
+ $fsth = $this->pdo->prepare("select title, feed_url, site_url, update_interval, order_id
FROM ttrss_feeds WHERE
(cat_id = :cat OR (:cat = 0 AND cat_id IS NULL)) AND owner_uid = :uid AND $hide_qpart
ORDER BY order_id, title");
@@ -92,13 +102,22 @@ class Opml extends Handler_Protected {
$url = htmlspecialchars($fline["feed_url"]);
$site_url = htmlspecialchars($fline["site_url"]);
+ if ($include_settings) {
+ $update_interval = (int)$fline["update_interval"];
+ $order_id = (int)$fline["order_id"];
+
+ $ttrss_specific_qpart = "ttrssSortOrder=\"$order_id\" ttrssUpdateInterval=\"$update_interval\"";
+ } else {
+ $ttrss_specific_qpart = "";
+ }
+
if ($site_url) {
$html_url_qpart = "htmlUrl=\"$site_url\"";
} else {
$html_url_qpart = "";
}
- $out .= "<outline type=\"rss\" text=\"$title\" xmlUrl=\"$url\" $html_url_qpart/>\n";
+ $out .= "<outline type=\"rss\" text=\"$title\" xmlUrl=\"$url\" $ttrss_specific_qpart $html_url_qpart/>\n";
}
if ($cat_title) $out .= "</outline>\n";
@@ -106,7 +125,7 @@ class Opml extends Handler_Protected {
return $out;
}
- function opml_export($name, $owner_uid, $hide_private_feeds=false, $include_settings=true) {
+ function opml_export($name, $owner_uid, $hide_private_feeds = false, $include_settings = true) {
if (!$owner_uid) return;
if (!isset($_REQUEST["debug"])) {
@@ -125,7 +144,7 @@ class Opml extends Handler_Protected {
</head>";
$out .= "<body>";
- $out .= $this->opml_export_category($owner_uid, 0, $hide_private_feeds);
+ $out .= $this->opml_export_category($owner_uid, 0, $hide_private_feeds, $include_settings);
# export tt-rss settings
@@ -176,7 +195,7 @@ class Opml extends Handler_Protected {
WHERE filter_id = ?");
$tmph->execute([$line['id']]);
- while ($tmp_line = $tmph->fetch()) {
+ while ($tmp_line = $tmph->fetch(PDO::FETCH_ASSOC)) {
unset($tmp_line["id"]);
unset($tmp_line["filter_id"]);
@@ -224,7 +243,7 @@ class Opml extends Handler_Protected {
WHERE filter_id = ?");
$tmph->execute([$line['id']]);
- while ($tmp_line = $tmph->fetch()) {
+ while ($tmp_line = $tmph->fetch(PDO::FETCH_ASSOC)) {
unset($tmp_line["id"]);
unset($tmp_line["filter_id"]);
@@ -298,11 +317,17 @@ class Opml extends Handler_Protected {
if (!$cat_id) $cat_id = null;
+ $update_interval = (int) $attrs->getNamedItem('ttrssUpdateInterval')->nodeValue;
+ if (!$update_interval) $update_interval = 0;
+
+ $order_id = (int) $attrs->getNamedItem('ttrssSortOrder')->nodeValue;
+ if (!$order_id) $order_id = 0;
+
$sth = $this->pdo->prepare("INSERT INTO ttrss_feeds
- (title, feed_url, owner_uid, cat_id, site_url, order_id) VALUES
- (?, ?, ?, ?, ?, 0)");
+ (title, feed_url, owner_uid, cat_id, site_url, order_id, update_interval) VALUES
+ (?, ?, ?, ?, ?, ?, ?)");
- $sth->execute([$feed_title, $feed_url, $owner_uid, $cat_id, $site_url]);
+ $sth->execute([$feed_title, $feed_url, $owner_uid, $cat_id, $site_url, $order_id, $update_interval]);
} else {
$this->opml_notice(T_sprintf("Duplicate feed: %s", $feed_title == '[Unknown]' ? $feed_url : $feed_title));
@@ -370,7 +395,7 @@ class Opml extends Handler_Protected {
$filter_id = $row['id'];
if ($filter_id) {
- $this->opml_notice(T_sprintf("Adding filter..."));
+ $this->opml_notice(T_sprintf("Adding filter %s...", $title));
foreach ($filter["rules"] as $rule) {
$feed_id = null;
@@ -387,8 +412,6 @@ class Opml extends Handler_Protected {
array_push($match_on, ($is_cat ? "CAT:" : "") . $name);
} else {
- $match_id = false;
-
if (!$is_cat) {
$tsth = $this->pdo->prepare("SELECT id FROM ttrss_feeds
WHERE title = ? AND owner_uid = ?");
@@ -397,6 +420,8 @@ class Opml extends Handler_Protected {
if ($row = $tsth->fetch()) {
$match_id = $row['id'];
+
+ array_push($match_on, $match_id);
}
} else {
$tsth = $this->pdo->prepare("SELECT id FROM ttrss_feed_categories
@@ -405,10 +430,10 @@ class Opml extends Handler_Protected {
if ($row = $tsth->fetch()) {
$match_id = $row['id'];
+
+ array_push($match_on, "CAT:$match_id");
}
}
-
- if ($match_id) array_push($match_on, $match_id);
}
}
@@ -487,7 +512,10 @@ class Opml extends Handler_Protected {
$cat_id = $this->get_feed_category($cat_title, $parent_id);
if ($cat_id === false) {
- add_feed_category($cat_title, $parent_id);
+ $order_id = (int) $root_node->attributes->getNamedItem('ttrssSortOrder')->nodeValue;
+ if (!$order_id) $order_id = 0;
+
+ add_feed_category($cat_title, $parent_id, $order_id);
$cat_id = $this->get_feed_category($cat_title, $parent_id);
}
diff --git a/classes/plugin.php b/classes/plugin.php
index b90c603b7..5ac4a6a9a 100644
--- a/classes/plugin.php
+++ b/classes/plugin.php
@@ -40,4 +40,21 @@ abstract class Plugin {
function api_version() {
return Plugin::API_VERSION_COMPAT;
}
-} \ No newline at end of file
+
+ /* gettext-related helpers */
+
+ function __($msgid) {
+ return _dgettext(PluginHost::object_to_domain($this), $msgid);
+ }
+
+ function _ngettext($singular, $plural, $number) {
+ return _dngettext(PluginHost::object_to_domain($this), $singular, $plural, $number);
+ }
+
+ function T_sprintf() {
+ $args = func_get_args();
+ $msgid = array_shift($args);
+
+ return vsprintf($this->__($msgid), $args);
+ }
+}
diff --git a/classes/pluginhost.php b/classes/pluginhost.php
index 96b1ce499..a3c12ecae 100755
--- a/classes/pluginhost.php
+++ b/classes/pluginhost.php
@@ -58,11 +58,16 @@ class PluginHost {
const HOOK_UNSUBSCRIBE_FEED = 38;
const HOOK_SEND_MAIL = 39;
const HOOK_FILTER_TRIGGERED = 40;
+ const HOOK_GET_FULL_TEXT = 41;
const KIND_ALL = 1;
const KIND_SYSTEM = 2;
const KIND_USER = 3;
+ static function object_to_domain($plugin) {
+ return strtolower(get_class($plugin));
+ }
+
function __construct() {
$this->pdo = Db::pdo();
@@ -211,6 +216,11 @@ class PluginHost {
continue;
}
+ if (file_exists(dirname($file) . "/locale")) {
+ _bindtextdomain($class, dirname($file) . "/locale");
+ _bind_textdomain_codeset($class, "UTF-8");
+ }
+
$this->last_registered = $class;
switch ($kind) {
diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php
index a033bf04f..6cbf15a58 100755
--- a/classes/pref/feeds.php
+++ b/classes/pref/feeds.php
@@ -66,9 +66,9 @@ class Pref_Feeds extends Handler_Protected {
$cat['items'] = array();
$cat['checkbox'] = false;
$cat['type'] = 'category';
- $cat['unread'] = 0;
- $cat['child_unread'] = 0;
- $cat['auxcounter'] = 0;
+ $cat['unread'] = -1;
+ $cat['child_unread'] = -1;
+ $cat['auxcounter'] = -1;
$cat['parent_id'] = $cat_id;
$cat['items'] = $this->get_category_items($line['id']);
@@ -95,10 +95,10 @@ class Pref_Feeds extends Handler_Protected {
$feed = array();
$feed['id'] = 'FEED:' . $feed_line['id'];
$feed['bare_id'] = (int)$feed_line['id'];
- $feed['auxcounter'] = 0;
+ $feed['auxcounter'] = -1;
$feed['name'] = $feed_line['title'];
$feed['checkbox'] = false;
- $feed['unread'] = 0;
+ $feed['unread'] = -1;
$feed['error'] = $feed_line['last_error'];
$feed['icon'] = Feeds::getFeedIcon($feed_line['id']);
$feed['param'] = make_local_datetime(
@@ -153,14 +153,14 @@ class Pref_Feeds extends Handler_Protected {
$item = array();
$item['id'] = 'FEED:' . $feed_id;
$item['bare_id'] = (int)$feed_id;
- $item['auxcounter'] = 0;
+ $item['auxcounter'] = -1;
$item['name'] = $feed['title'];
$item['checkbox'] = false;
$item['error'] = '';
$item['icon'] = $feed['icon'];
$item['param'] = '';
- $item['unread'] = 0; //$feed['sender']->get_unread($feed['id']);
+ $item['unread'] = -1;
$item['type'] = 'feed';
array_push($cat['items'], $item);
@@ -218,13 +218,13 @@ class Pref_Feeds extends Handler_Protected {
$cat = array();
$cat['id'] = 'CAT:' . $line['id'];
$cat['bare_id'] = (int)$line['id'];
- $cat['auxcounter'] = 0;
+ $cat['auxcounter'] = -1;
$cat['name'] = $line['title'];
$cat['items'] = array();
$cat['checkbox'] = false;
$cat['type'] = 'category';
- $cat['unread'] = 0;
- $cat['child_unread'] = 0;
+ $cat['unread'] = -1;
+ $cat['child_unread'] = -1;
$cat['items'] = $this->get_category_items($line['id']);
@@ -242,13 +242,13 @@ class Pref_Feeds extends Handler_Protected {
$cat = array();
$cat['id'] = 'CAT:0';
$cat['bare_id'] = 0;
- $cat['auxcounter'] = 0;
+ $cat['auxcounter'] = -1;
$cat['name'] = __("Uncategorized");
$cat['items'] = array();
$cat['type'] = 'category';
$cat['checkbox'] = false;
- $cat['unread'] = 0;
- $cat['child_unread'] = 0;
+ $cat['unread'] = -1;
+ $cat['child_unread'] = -1;
$fsth = $this->pdo->prepare("SELECT id, title,last_error,
".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated, update_interval
@@ -263,14 +263,14 @@ class Pref_Feeds extends Handler_Protected {
$feed = array();
$feed['id'] = 'FEED:' . $feed_line['id'];
$feed['bare_id'] = (int)$feed_line['id'];
- $feed['auxcounter'] = 0;
+ $feed['auxcounter'] = -1;
$feed['name'] = $feed_line['title'];
$feed['checkbox'] = false;
$feed['error'] = $feed_line['last_error'];
$feed['icon'] = Feeds::getFeedIcon($feed_line['id']);
$feed['param'] = make_local_datetime(
$feed_line['last_updated'], true);
- $feed['unread'] = 0;
+ $feed['unread'] = -1;
$feed['type'] = 'feed';
$feed['updates_disabled'] = (int)($feed_line['update_interval'] < 0);
@@ -298,14 +298,14 @@ class Pref_Feeds extends Handler_Protected {
$feed = array();
$feed['id'] = 'FEED:' . $feed_line['id'];
$feed['bare_id'] = (int)$feed_line['id'];
- $feed['auxcounter'] = 0;
+ $feed['auxcounter'] = -1;
$feed['name'] = $feed_line['title'];
$feed['checkbox'] = false;
$feed['error'] = $feed_line['last_error'];
$feed['icon'] = Feeds::getFeedIcon($feed_line['id']);
$feed['param'] = make_local_datetime(
$feed_line['last_updated'], true);
- $feed['unread'] = 0;
+ $feed['unread'] = -1;
$feed['type'] = 'feed';
$feed['updates_disabled'] = (int)($feed_line['update_interval'] < 0);
@@ -473,6 +473,7 @@ class Pref_Feeds extends Handler_Protected {
$icon_file = $tmp_file;
$feed_id = clean($_REQUEST["feed_id"]);
+ $rc = 2; // failed
if (is_file($icon_file) && $feed_id) {
if (filesize($icon_file) < 65535) {
@@ -492,21 +493,15 @@ class Pref_Feeds extends Handler_Protected {
$rc = 0;
}
- } else {
- $rc = 2;
}
} else {
$rc = 1;
}
- } else {
- $rc = 2;
}
- @unlink($icon_file);
+ if (is_file($icon_file)) @unlink($icon_file);
- print "<script type=\"text/javascript\">";
- print "parent.CommonDialogs.uploadIconHandler($rc);";
- print "</script>";
+ print $rc;
return;
}
@@ -531,16 +526,16 @@ class Pref_Feeds extends Handler_Protected {
print_hidden("op", "pref-feeds");
print_hidden("method", "editSave");
- print "<div class=\"dlgSec\">".__("Feed")."</div>";
- print "<div class=\"dlgSecCont\">";
+ print "<header>".__("Feed")."</header>";
+ print "<section>";
/* Title */
print "<fieldset>";
- print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
+ print "<input dojoType='dijit.form.ValidationTextBox' required='1'
placeHolder=\"".__("Feed Title")."\"
- style=\"font-size : 16px; width: 500px\" name=\"title\" value=\"$title\">";
+ style='font-size : 16px; width: 500px' name='title' value=\"$title\">";
print "</fieldset>";
@@ -551,10 +546,10 @@ class Pref_Feeds extends Handler_Protected {
print "<fieldset>";
print "<label>" . __('URL:') . "</label> ";
- print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
+ print "<input dojoType='dijit.form.ValidationTextBox' required='1'
placeHolder=\"".__("Feed URL")."\"
- regExp='^(http|https)://.*' style=\"width : 300px\"
- name=\"feed_url\" value=\"$feed_url\">";
+ regExp='^(http|https)://.*' style='width : 300px'
+ name='feed_url' value=\"$feed_url\">";
$last_error = $row["last_error"];
@@ -576,7 +571,7 @@ class Pref_Feeds extends Handler_Protected {
print "<label>" . __('Place in category:') . "</label> ";
print_feed_cat_select("cat_id", $cat_id,
- 'dojoType="dijit.form.Select"');
+ 'dojoType="fox.form.Select"');
print "</fieldset>";
}
@@ -588,10 +583,10 @@ class Pref_Feeds extends Handler_Protected {
print "<fieldset>";
print "<label>" . __('Site URL:') . "</label> ";
- print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
+ print "<input dojoType='dijit.form.ValidationTextBox' required='1'
placeHolder=\"".__("Site URL")."\"
- regExp='^(http|https)://.*' style=\"width : 300px\"
- name=\"site_url\" value=\"$site_url\">";
+ regExp='^(http|https)://.*' style='width : 300px'
+ name='site_url' value=\"$site_url\">";
print "</fieldset>";
@@ -600,19 +595,22 @@ class Pref_Feeds extends Handler_Protected {
if (DB_TYPE == "pgsql") {
$feed_language = $row["feed_language"];
+ if (!$feed_language)
+ $feed_language = get_pref('DEFAULT_SEARCH_LANGUAGE');
+
print "<fieldset>";
print "<label>" . __('Language:') . "</label> ";
print_select("feed_language", $feed_language, $this::get_ts_languages(),
- 'dojoType="dijit.form.Select"');
+ 'dojoType="fox.form.Select"');
print "</fieldset>";
}
- print "</div>";
+ print "</section>";
- print "<div class=\"dlgSec\">".__("Update")."</div>";
- print "<div class=\"dlgSecCont\">";
+ print "<header>".__("Update")."</header>";
+ print "<section>";
/* Update Interval */
@@ -623,7 +621,7 @@ class Pref_Feeds extends Handler_Protected {
print "<label>".__("Interval:")."</label> ";
print_select_hash("update_interval", $update_interval, $update_intervals,
- 'dojoType="dijit.form.Select"');
+ 'dojoType="fox.form.Select"');
print "</fieldset>";
@@ -636,12 +634,12 @@ class Pref_Feeds extends Handler_Protected {
print "<label>" . __('Article purging:') . "</label> ";
print_select_hash("purge_interval", $purge_interval, $purge_intervals,
- 'dojoType="dijit.form.Select" ' .
+ 'dojoType="fox.form.Select" ' .
((FORCE_ARTICLE_PURGE == 0) ? "" : 'disabled="1"'));
print "</fieldset>";
- print "</div>";
+ print "</section>";
$auth_login = htmlspecialchars($row["auth_login"]);
$auth_pass = htmlspecialchars($row["auth_pass"]);
@@ -650,58 +648,40 @@ class Pref_Feeds extends Handler_Protected {
$auth_style = $auth_enabled ? '' : 'display: none';
print "<div id='feedEditDlg_loginContainer' style='$auth_style'>";
- print "<div class=\"dlgSec\">".__("Authentication")."</div>";
- print "<div class=\"dlgSecCont\">";
+ print "<header>".__("Authentication")."</header>";
+ print "<section>";
print "<fieldset>";
- print "<input dojoType=\"dijit.form.TextBox\" id=\"feedEditDlg_login\"
- placeHolder=\"".__("Login")."\"
- autocomplete=\"new-password\"
- name=\"auth_login\" value=\"$auth_login\">";
+ print "<input dojoType='dijit.form.TextBox' id='feedEditDlg_login'
+ placeHolder='".__("Login")."'
+ autocomplete='new-password'
+ name='auth_login' value=\"$auth_login\">";
- print "</fieldset>";
- print "<fieldset>";
+ print "</fieldset><fieldset>";
- print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\"
- autocomplete=\"new-password\"
- placeHolder=\"".__("Password")."\"
+ print "<input dojoType='dijit.form.TextBox' type='password' name='auth_pass'
+ autocomplete='new-password'
+ placeHolder='".__("Password")."'
value=\"$auth_pass\">";
- print "<div dojoType=\"dijit.Tooltip\" connectId=\"feedEditDlg_login\" position=\"below\">
+ print "<div dojoType='dijit.Tooltip' connectId='feedEditDlg_login' position='below'>
".__('<b>Hint:</b> you need to fill in your login information if your feed requires authentication, except for Twitter feeds.')."
</div>";
print "</fieldset>";
- print "</div></div>";
+ print "</section></div>";
$auth_checked = $auth_enabled ? 'checked' : '';
- print "<div style=\"clear : both\">
- <input type=\"checkbox\" $auth_checked name=\"need_auth\" dojoType=\"dijit.form.CheckBox\" id=\"feedEditDlg_loginCheck\"
+ print "<label class='checkbox'>
+ <input type='checkbox' $auth_checked name='need_auth' dojoType='dijit.form.CheckBox' id='feedEditDlg_loginCheck'
onclick='displayIfChecked(this, \"feedEditDlg_loginContainer\")'>
- <label for=\"feedEditDlg_loginCheck\">".
- __('This feed requires authentication.')."</div>";
+ ".__('This feed requires authentication.')."</label>";
print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Options').'">';
- //print "<div class=\"dlgSec\">".__("Options")."</div>";
- print "<div class=\"dlgSecSimple\">";
-
- $private = $row["private"];
-
- if ($private) {
- $checked = "checked=\"1\"";
- } else {
- $checked = "";
- }
-
- print "<fieldset class='narrow'>";
-
- print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"private\" id=\"private\"
- $checked> ".__('Hide from Popular feeds')."</label>";
-
- print "</fieldset>";
+ print "<section class='narrow'>";
$include_in_digest = $row["include_in_digest"];
@@ -745,9 +725,8 @@ class Pref_Feeds extends Handler_Protected {
print "<fieldset class='narrow'>";
- print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"hide_images\"
- name=\"hide_images\"
- $checked> ".__('Do not embed media')."</label>";
+ print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='hide_images'
+ name='hide_images' $checked> ".__('Do not embed media')."</label>";
print "</fieldset>";
@@ -761,9 +740,8 @@ class Pref_Feeds extends Handler_Protected {
print "<fieldset class='narrow'>";
- print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"cache_images\"
- name=\"cache_images\"
- $checked> ". __('Cache media')."</label>";
+ print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='cache_images'
+ name='cache_images' $checked> ". __('Cache media')."</label>";
print "</fieldset>";
@@ -777,41 +755,32 @@ class Pref_Feeds extends Handler_Protected {
print "<fieldset class='narrow'>";
- print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"mark_unread_on_update\"
- name=\"mark_unread_on_update\"
- $checked> ".__('Mark updated articles as unread')."</label>";
+ print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='mark_unread_on_update'
+ name='mark_unread_on_update' $checked> ".__('Mark updated articles as unread')."</label>";
print "</fieldset>";
- print "</div>";
-
print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Icon').'">';
/* Icon */
- print "<div class=\"dlgSecSimple\">";
-
- print "<img class=\"feedIcon\" src=\"".Feeds::getFeedIcon($feed_id)."\">";
+ print "<img class='feedIcon feed-editor-icon' src=\"".Feeds::getFeedIcon($feed_id)."\">";
- print "<iframe name=\"icon_upload_iframe\"
- style=\"width: 400px; height: 100px; display: none;\"></iframe>";
-
- print "<form style='display : block' target=\"icon_upload_iframe\"
- enctype=\"multipart/form-data\" method=\"POST\"
- action=\"backend.php\">
- <label class=\"dijitButton\">".__("Choose file...")."
- <input style=\"display: none\" id=\"icon_file\" size=\"10\" name=\"icon_file\" type=\"file\">
+ print "<form onsubmit='return false;' id='feed_icon_upload_form'
+ enctype='multipart/form-data' method='POST'>
+ <label class='dijitButton'>".__("Choose file...")."
+ <input style='display: none' id='icon_file' size='10' name='icon_file' type='file'>
</label>
- <input type=\"hidden\" name=\"op\" value=\"pref-feeds\">
- <input type=\"hidden\" name=\"feed_id\" value=\"$feed_id\">
- <input type=\"hidden\" name=\"method\" value=\"uploadicon\">
- <button class=\"\" dojoType=\"dijit.form.Button\" onclick=\"return CommonDialogs.uploadFeedIcon();\"
- type=\"submit\">".__('Replace')."</button>
- <button class=\"alt-danger\" dojoType=\"dijit.form.Button\" onclick=\"return CommonDialogs.removeFeedIcon($feed_id);\"
- type=\"submit\">".__('Remove')."</button>
+ <input type='hidden' name='op' value='pref-feeds'>
+ <input type='hidden' name='feed_id' value='$feed_id'>
+ <input type='hidden' name='method' value='uploadicon'>
+ <button dojoType='dijit.form.Button' onclick=\"return CommonDialogs.uploadFeedIcon();\"
+ type='submit'>".__('Replace')."</button>
+ <button class='alt-danger' dojoType='dijit.form.Button' onclick=\"return CommonDialogs.removeFeedIcon($feed_id);\"
+ type='submit'>".__('Remove')."</button>
</form>";
- print "</div>";
+ print "</section>";
print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Plugins').'">';
@@ -822,16 +791,12 @@ class Pref_Feeds extends Handler_Protected {
$title = htmlspecialchars($title, ENT_QUOTES);
- print "<div class='dlgButtons'>
- <div style=\"float : left\">
- <button class=\"alt-danger\" dojoType=\"dijit.form.Button\" onclick='return CommonDialogs.unsubscribeFeed($feed_id, \"$title\")'>".
- __('Unsubscribe')."</button>";
-
- print "</div>";
-
- print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').execute()\">".__('Save')."</button>
- <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').hide()\">".__('Cancel')."</button>
- </div>";
+ print "<footer>
+ <button style='float : left' class='alt-danger' dojoType='dijit.form.Button' onclick='return CommonDialogs.unsubscribeFeed($feed_id, \"$title\")'>".
+ __('Unsubscribe')."</button>
+ <button dojoType='dijit.form.Button' class='alt-primary' onclick=\"return dijit.byId('feedEditDlg').execute()\">".__('Save')."</button>
+ <button dojoType='dijit.form.Button' onclick=\"return dijit.byId('feedEditDlg').hide()\">".__('Cancel')."</button>
+ </footer>";
}
}
@@ -849,8 +814,8 @@ class Pref_Feeds extends Handler_Protected {
print_hidden("op", "pref-feeds");
print_hidden("method", "batchEditSave");
- print "<div class=\"dlgSec\">".__("Feed")."</div>";
- print "<div class=\"dlgSecCont\">";
+ print "<header>".__("Feed")."</header>";
+ print "<section>";
/* Category */
@@ -861,7 +826,7 @@ class Pref_Feeds extends Handler_Protected {
print "<label>" . __('Place in category:') . "</label> ";
print_feed_cat_select("cat_id", false,
- 'disabled="1" dojoType="dijit.form.Select"');
+ 'disabled="1" dojoType="fox.form.Select"');
$this->batch_edit_cbox("cat_id");
@@ -875,17 +840,17 @@ class Pref_Feeds extends Handler_Protected {
print "<label>" . __('Language:') . "</label> ";
print_select("feed_language", "", $this::get_ts_languages(),
- 'disabled="1" dojoType="dijit.form.Select"');
+ 'disabled="1" dojoType="fox.form.Select"');
$this->batch_edit_cbox("feed_language");
print "</fieldset>";
}
- print "</div>";
+ print "</section>";
- print "<div class=\"dlgSec\">".__("Update")."</div>";
- print "<div class=\"dlgSecCont\">";
+ print "<header>".__("Update")."</header>";
+ print "<section>";
/* Update Interval */
@@ -894,7 +859,7 @@ class Pref_Feeds extends Handler_Protected {
print "<label>".__("Interval:")."</label> ";
print_select_hash("update_interval", "", $update_intervals,
- 'disabled="1" dojoType="dijit.form.Select"');
+ 'disabled="1" dojoType="fox.form.Select"');
$this->batch_edit_cbox("update_interval");
@@ -909,100 +874,85 @@ class Pref_Feeds extends Handler_Protected {
print "<label>" . __('Article purging:') . "</label> ";
print_select_hash("purge_interval", "", $purge_intervals,
- 'disabled="1" dojoType="dijit.form.Select"');
+ 'disabled="1" dojoType="fox.form.Select"');
$this->batch_edit_cbox("purge_interval");
print "</fieldset>";
}
- print "</div>";
- print "<div class=\"dlgSec\">".__("Authentication")."</div>";
- print "<div class=\"dlgSecCont\">";
+ print "</section>";
+ print "<header>".__("Authentication")."</header>";
+ print "<section>";
print "<fieldset>";
- print "<input dojoType=\"dijit.form.TextBox\"
- placeHolder=\"".__("Login")."\" disabled=\"1\"
- autocomplete=\"new-password\"
- name=\"auth_login\" value=\"\">";
+ print "<input dojoType='dijit.form.TextBox'
+ placeHolder=\"".__("Login")."\" disabled='1'
+ autocomplete='new-password'
+ name='auth_login' value=''>";
$this->batch_edit_cbox("auth_login");
- print "</fieldset>";
- print "<fieldset>";
-
- print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\"
- autocomplete=\"new-password\"
- placeHolder=\"".__("Password")."\" disabled=\"1\"
- value=\"\">";
+ print "<input dojoType='dijit.form.TextBox' type='password' name='auth_pass'
+ autocomplete='new-password'
+ placeHolder=\"".__("Password")."\" disabled='1'
+ value=''>";
$this->batch_edit_cbox("auth_pass");
print "</fieldset>";
- print "</div>";
- print "<div class=\"dlgSec\">".__("Options")."</div>";
- print "<div class=\"dlgSecCont\">";
+ print "</section>";
+ print "<header>".__("Options")."</header>";
+ print "<section>";
print "<fieldset class='narrow'>";
- print "<label class='checkbox'><input disabled=\"1\" type=\"checkbox\" name=\"private\" id=\"private\"
- dojoType=\"dijit.form.CheckBox\">&nbsp;".__('Hide from Popular feeds')."</label>";
-
- print "&nbsp;"; $this->batch_edit_cbox("private", "private_l");
-
- print "</fieldset><fieldset class='narrow'>";
-
- print "<label class='checkbox'><input disabled=\"1\" type=\"checkbox\" id=\"include_in_digest\"
- name=\"include_in_digest\"
- dojoType=\"dijit.form.CheckBox\">&nbsp;".__('Include in e-mail digest')."</label>";
+ print "<label class='checkbox'><input disabled='1' type='checkbox' id='include_in_digest'
+ name='include_in_digest' dojoType='dijit.form.CheckBox'>&nbsp;".__('Include in e-mail digest')."</label>";
print "&nbsp;"; $this->batch_edit_cbox("include_in_digest", "include_in_digest_l");
print "</fieldset><fieldset class='narrow'>";
- print "<label class='checkbox'><input disabled=\"1\" type=\"checkbox\" id=\"always_display_enclosures\"
- name=\"always_display_enclosures\"
- dojoType=\"dijit.form.CheckBox\">&nbsp;".__('Always display image attachments')."</label>";
+ print "<label class='checkbox'><input disabled='1' type='checkbox' id='always_display_enclosures'
+ name='always_display_enclosures' dojoType='dijit.form.CheckBox'>&nbsp;".__('Always display image attachments')."</label>";
print "&nbsp;"; $this->batch_edit_cbox("always_display_enclosures", "always_display_enclosures_l");
print "</fieldset><fieldset class='narrow'>";
- print "<label class='checkbox'><input disabled=\"1\" type=\"checkbox\" id=\"hide_images\"
- name=\"hide_images\"
- dojoType=\"dijit.form.CheckBox\">&nbsp;". __('Do not embed media')."</label>";
+ print "<label class='checkbox'><input disabled='1' type='checkbox' id='hide_images'
+ name='hide_images' dojoType='dijit.form.CheckBox'>&nbsp;". __('Do not embed media')."</label>";
print "&nbsp;"; $this->batch_edit_cbox("hide_images", "hide_images_l");
print "</fieldset><fieldset class='narrow'>";
- print "<label class='checkbox'><input disabled=\"1\" type=\"checkbox\" id=\"cache_images\"
- name=\"cache_images\"
- dojoType=\"dijit.form.CheckBox\">&nbsp;".__('Cache media')."</label>";
+ print "<label class='checkbox'><input disabled='1' type='checkbox' id='cache_images'
+ name='cache_images' dojoType='dijit.form.CheckBox'>&nbsp;".__('Cache media')."</label>";
print "&nbsp;"; $this->batch_edit_cbox("cache_images", "cache_images_l");
print "</fieldset><fieldset class='narrow'>";
- print "<label class='checkbox'><input disabled=\"1\" type=\"checkbox\" id=\"mark_unread_on_update\"
- name=\"mark_unread_on_update\"
- dojoType=\"dijit.form.CheckBox\">&nbsp;".__('Mark updated articles as unread')."</label>";
+ print "<label class='checkbox'><input disabled='1' type='checkbox' id='mark_unread_on_update'
+ name='mark_unread_on_update' dojoType='dijit.form.CheckBox'>&nbsp;".__('Mark updated articles as unread')."</label>";
print "&nbsp;"; $this->batch_edit_cbox("mark_unread_on_update", "mark_unread_on_update_l");
print "</fieldset>";
- print "</div>";
+ print "</section>";
- print "<div class='dlgButtons'>
- <button dojoType=\"dijit.form.Button\" type='submit' class='alt-primary'
+ print "<footer>
+ <button dojoType='dijit.form.Button' type='submit' class='alt-primary'
onclick=\"return dijit.byId('feedEditDlg').execute()\">".
__('Save')."</button>
- <button dojoType=\"dijit.form.Button\"
+ <button dojoType='dijit.form.Button'
onclick=\"return dijit.byId('feedEditDlg').hide()\">".
__('Cancel')."</button>
- </div>";
+ </footer>";
return;
}
@@ -1258,7 +1208,7 @@ class Pref_Feeds extends Handler_Protected {
print '<div dojoType="dijit.layout.BorderContainer" gutters="false">';
- print "<div region='top' dojoType=\"dijit.Toolbar\">"; #toolbar
+ print "<div region='top' dojoType=\"fox.Toolbar\">"; #toolbar
print "<div style='float : right; padding-right : 4px;'>
<input dojoType=\"dijit.form.TextBox\" id=\"feed_search\" size=\"20\" type=\"search\"
@@ -1267,7 +1217,7 @@ class Pref_Feeds extends Handler_Protected {
__('Search')."</button>
</div>";
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType=\"fox.form.DropDownButton\">".
"<span>" . __('Select')."</span>";
print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
print "<div onclick=\"dijit.byId('feedTree').model.setAllChecked(true)\"
@@ -1276,7 +1226,7 @@ class Pref_Feeds extends Handler_Protected {
dojoType=\"dijit.MenuItem\">".__('None')."</div>";
print "</div></div>";
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType=\"fox.form.DropDownButton\">".
"<span>" . __('Feeds')."</span>";
print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
print "<div onclick=\"CommonDialogs.quickAddFeed()\"
@@ -1292,7 +1242,7 @@ class Pref_Feeds extends Handler_Protected {
print "</div></div>";
if (get_pref('ENABLE_FEED_CATS')) {
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType=\"fox.form.DropDownButton\">".
"<span>" . __('Categories')."</span>";
print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
print "<div onclick=\"dijit.byId('feedTree').createCategory()\"
@@ -1499,16 +1449,14 @@ class Pref_Feeds extends Handler_Protected {
ORDER BY last_article");
$sth->execute([$_SESSION['uid']]);
- print "<p" .__("These feeds have not been updated with new content for 3 months (oldest first):") . "</p>";
-
- print "<div dojoType=\"dijit.Toolbar\">";
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType='fox.Toolbar'>";
+ print "<div dojoType='fox.form.DropDownButton'>".
"<span>" . __('Select')."</span>";
- print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div dojoType='dijit.Menu' style='display: none'>";
print "<div onclick=\"Tables.select('inactive-feeds-list', true)\"
- dojoType=\"dijit.MenuItem\">".__('All')."</div>";
+ dojoType='dijit.MenuItem'>".__('All')."</div>";
print "<div onclick=\"Tables.select('inactive-feeds-list', false)\"
- dojoType=\"dijit.MenuItem\">".__('None')."</div>";
+ dojoType='dijit.MenuItem'>".__('None')."</div>";
print "</div></div>";
print "</div>"; #toolbar
@@ -1524,16 +1472,16 @@ class Pref_Feeds extends Handler_Protected {
print "<tr data-row-id='$feed_id'>";
print "<td width='5%' align='center'><input
- onclick='Tables.onRowChecked(this);' dojoType=\"dijit.form.CheckBox\"
- type=\"checkbox\"></td>";
+ onclick='Tables.onRowChecked(this);' dojoType='dijit.form.CheckBox'
+ type='checkbox'></td>";
print "<td>";
- print "<a class=\"visibleLink\" href=\"#\" ".
+ print "<a href='#' ".
"title=\"".__("Click to edit feed")."\" ".
"onclick=\"CommonDialogs.editFeed(".$line["id"].")\">".
htmlspecialchars($line["title"])."</a>";
- print "</td><td class=\"insensitive\" align='right'>";
+ print "</td><td class='text-muted' align='right'>";
print make_local_datetime($line['last_article'], false);
print "</td>";
print "</tr>";
@@ -1544,16 +1492,12 @@ class Pref_Feeds extends Handler_Protected {
print "</table>";
print "</div>";
- print "<div class='dlgButtons'>";
- print "<div style='float : left'>";
- print "<button class=\"alt-danger\" dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('inactiveFeedsDlg').removeSelected()\">"
- .__('Unsubscribe from selected feeds')."</button> ";
- print "</div>";
-
- print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('inactiveFeedsDlg').hide()\">".
- __('Close this window')."</button>";
-
- print "</div>";
+ print "<footer>
+ <button style='float : left' class=\"alt-danger\" dojoType='dijit.form.Button' onclick=\"dijit.byId('inactiveFeedsDlg').removeSelected()\">"
+ .__('Unsubscribe from selected feeds')."</button>
+ <button dojoType='dijit.form.Button' onclick=\"dijit.byId('inactiveFeedsDlg').hide()\">"
+ .__('Close this window')."</button>
+ </footer>";
}
@@ -1562,8 +1506,8 @@ class Pref_Feeds extends Handler_Protected {
FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?");
$sth->execute([$_SESSION['uid']]);
- print "<div dojoType=\"dijit.Toolbar\">";
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType=\"fox.Toolbar\">";
+ print "<div dojoType=\"fox.form.DropDownButton\">".
"<span>" . __('Select')."</span>";
print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
print "<div onclick=\"Tables.select('error-feeds-list', true)\"
@@ -1594,7 +1538,7 @@ class Pref_Feeds extends Handler_Protected {
"onclick=\"CommonDialogs.editFeed(".$line["id"].")\">".
htmlspecialchars($line["title"])."</a>: ";
- print "<span class=\"insensitive\">";
+ print "<span class=\"text-muted\">";
print htmlspecialchars($line["last_error"]);
print "</span>";
@@ -1607,16 +1551,14 @@ class Pref_Feeds extends Handler_Protected {
print "</table>";
print "</div>";
- print "<div class='dlgButtons'>";
- print "<div style='float : left'>";
- print "<button class=\"alt-danger\" dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('errorFeedsDlg').removeSelected()\">"
+ print "<footer>";
+ print "<button style='float : left' class=\"alt-danger\" dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('errorFeedsDlg').removeSelected()\">"
.__('Unsubscribe from selected feeds')."</button> ";
- print "</div>";
print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('errorFeedsDlg').hide()\">".
__('Close this window')."</button>";
- print "</div>";
+ print "</footer>";
}
private function remove_feed_category($id, $owner_uid) {
@@ -1665,8 +1607,8 @@ class Pref_Feeds extends Handler_Protected {
$new_feed_id = (int)$row['id'] + 1;
$sth = $pdo->prepare("INSERT INTO ttrss_archived_feeds
- (id, owner_uid, title, feed_url, site_url)
- SELECT ?, owner_uid, title, feed_url, site_url from ttrss_feeds
+ (id, owner_uid, title, feed_url, site_url, created)
+ SELECT ?, owner_uid, title, feed_url, site_url, NOW() from ttrss_feeds
WHERE id = ?");
$sth->execute([$new_feed_id, $id]);
@@ -1710,9 +1652,8 @@ class Pref_Feeds extends Handler_Protected {
print_hidden("op", "pref-feeds");
print_hidden("method", "batchaddfeeds");
- print "<div class='dlgSecHoriz'>".__("One valid feed per line (no detection is done)")."</div>";
-
- print "<div class='dlgSecCont'>";
+ print "<header class='horizontal'>".__("One valid feed per line (no detection is done)")."</header>";
+ print "<section>";
print "<textarea
style='font-size : 12px; width : 98%; height: 200px;'
@@ -1721,20 +1662,22 @@ class Pref_Feeds extends Handler_Protected {
if (get_pref('ENABLE_FEED_CATS')) {
print "<fieldset>";
print "<label>" . __('Place in category:') . "</label> ";
- print_feed_cat_select("cat", false, 'dojoType="dijit.form.Select"');
+ print_feed_cat_select("cat", false, 'dojoType="fox.form.Select"');
print "</fieldset>";
}
+ print "</section>";
+
print "<div id='feedDlg_loginContainer' style='display : none'>";
- print "<div class='dlgSec'>" . __("Authentication") . "</div>";
- print "<div class='dlgSecCont'>";
+ print "<header>" . __("Authentication") . "</header>";
+ print "<section>";
print "<input dojoType='dijit.form.TextBox' name='login' placeHolder=\"".__("Login")."\">
<input placeHolder=\"".__("Password")."\" dojoType=\"dijit.form.TextBox\" type='password'
autocomplete='new-password' name='pass''></div>";
- print "</div>";
+ print "</section>";
print "</div>";
print "<fieldset class='narrow'>
@@ -1743,10 +1686,10 @@ class Pref_Feeds extends Handler_Protected {
__('Feeds require authentication.')."</label></div>";
print "</fieldset>";
- print "<div class='dlgButtons'>
- <button dojoType='dijit.form.Button' type='submit' class='alt-primary' onclick=\"return dijit.byId('batchSubDlg').execute()\">".__('Subscribe')."</button>
+ print "<footer>
+ <button dojoType='dijit.form.Button' type='submit' class='alt-primary'>".__('Subscribe')."</button>
<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('batchSubDlg').hide()\">".__('Cancel')."</button>
- </div>";
+ </footer>";
}
function batchAddFeeds() {
@@ -1755,6 +1698,13 @@ class Pref_Feeds extends Handler_Protected {
$login = clean($_REQUEST['login']);
$pass = trim(clean($_REQUEST['pass']));
+ $csth = $this->pdo->prepare("SELECT id FROM ttrss_feeds
+ WHERE feed_url = ? AND owner_uid = ?");
+
+ $isth = $this->pdo->prepare("INSERT INTO ttrss_feeds
+ (owner_uid,feed_url,title,cat_id,auth_login,auth_pass,update_method,auth_pass_encrypted)
+ VALUES (?, ?, '[Unknown]', ?, ?, ?, 0, false)");
+
foreach ($feeds as $feed) {
$feed = trim($feed);
@@ -1762,16 +1712,10 @@ class Pref_Feeds extends Handler_Protected {
$this->pdo->beginTransaction();
- $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds
- WHERE feed_url = ? AND owner_uid = ?");
- $sth->execute([$feed, $_SESSION['uid']]);
-
- if (!$sth->fetch()) {
- $sth = $this->pdo->prepare("INSERT INTO ttrss_feeds
- (owner_uid,feed_url,title,cat_id,auth_login,auth_pass,update_method,auth_pass_encrypted)
- VALUES (?, ?, '[Unknown]', ?, ?, ?, 0, false)");
+ $csth->execute([$feed, $_SESSION['uid']]);
- $sth->execute([$_SESSION['uid'], $feed, $cat_id ? $cat_id : null, $login, $pass]);
+ if (!$csth->fetch()) {
+ $isth->execute([$_SESSION['uid'], $feed, $cat_id ? $cat_id : null, $login, $pass]);
}
$this->pdo->commit();
diff --git a/classes/pref/filters.php b/classes/pref/filters.php
index 99bb885cf..041951b35 100755
--- a/classes/pref/filters.php
+++ b/classes/pref/filters.php
@@ -148,7 +148,7 @@ class Pref_Filters extends Handler_Protected {
$tmp = "<li><span class='title'>" . $line["title"] . "</span><br/>" .
"<span class='feed'>" . $line['feed_title'] . "</span>, <span class='date'>" . mb_substr($line["date_entered"], 0, 16) . "</span>" .
- "<div class='preview insensitive'>" . $content_preview . "</div>" .
+ "<div class='preview text-muted'>" . $content_preview . "</div>" .
"</li>";
array_push($rv, $tmp);
@@ -170,10 +170,10 @@ class Pref_Filters extends Handler_Protected {
print "<ul class='panel panel-scrollable list list-unstyled' id='prefFilterTestResultList'>";
print "</ul>";
- print "<div style='text-align : center'>";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('filterTestDlg').hide()\">".
+ print "<footer class='text-center'>";
+ print "<button dojoType='dijit.form.Button' onclick=\"dijit.byId('filterTestDlg').hide()\">".
__('Close this window')."</button>";
- print "</div>";
+ print "</footer>";
}
@@ -346,19 +346,17 @@ class Pref_Filters extends Handler_Protected {
print_hidden("method", "editSave");
print_hidden("csrf_token", $_SESSION['csrf_token']);
- print "<div class='dlgSec'>".__("Caption")."</div>";
- print "<div class='dlgSecCont'>";
-
+ print "<header>".__("Caption")."</header>";
+ print "<section>";
print "<input required=\"true\" dojoType=\"dijit.form.ValidationTextBox\" style=\"width : 20em;\" name=\"title\" value=\"$title\">";
+ print "</section>";
- print "</div>";
-
- print "<div class='dlgSecHoriz'>".__("Match")."</div>";
- print "<div class='dlgSecCont'>";
+ print "<header class='horizontal'>".__("Match")."</header>";
+ print "<section>";
- print "<div dojoType=\"dijit.Toolbar\">";
+ print "<div dojoType=\"fox.Toolbar\">";
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType=\"fox.form.DropDownButton\">".
"<span>" . __('Select')."</span>";
print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
print "<div onclick=\"dijit.byId('filterEditDlg').selectRules(true)\"
@@ -410,15 +408,15 @@ class Pref_Filters extends Handler_Protected {
print "</ul>";
- print "</div>";
+ print "</section>";
- print "<div class='dlgSecHoriz'>".__("Apply actions")."</div>";
+ print "<header class='horizontal'>".__("Apply actions")."</header>";
- print "<div class='dlgSecCont'>";
+ print "<section>";
- print "<div dojoType=\"dijit.Toolbar\">";
+ print "<div dojoType=\"fox.Toolbar\">";
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType=\"fox.form.DropDownButton\">".
"<span>" . __('Select')."</span>";
print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
print "<div onclick=\"dijit.byId('filterEditDlg').selectActions(true)\"
@@ -456,10 +454,10 @@ class Pref_Filters extends Handler_Protected {
print "</ul>";
- print "</div>";
+ print "</section>";
- print "<div class='dlgSec'>".__("Options")."</div>";
- print "<div class='dlgSecCont'>";
+ print "<header>".__("Options")."</header>";
+ print "<section>";
if ($enabled) {
$checked = "checked=\"1\"";
@@ -495,9 +493,9 @@ class Pref_Filters extends Handler_Protected {
print "</fieldset>";
- print "</div>";
+ print "</section>";
- print "<div class=\"dlgButtons\">";
+ print "<footer>";
print "<div style=\"float : left\">";
print "<button dojoType=\"dijit.form.Button\" class=\"alt-danger\" onclick=\"return dijit.byId('filterEditDlg').removeFilter()\">".
@@ -513,7 +511,7 @@ class Pref_Filters extends Handler_Protected {
print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').hide()\">".
__('Cancel')."</button>";
- print "</div>";
+ print "</footer>";
print "</form>";
}
@@ -759,7 +757,7 @@ class Pref_Filters extends Handler_Protected {
print "<div dojoType='dijit.layout.BorderContainer' gutters='false'>";
print "<div style='padding : 0px' dojoType='dijit.layout.ContentPane' region='top'>";
- print "<div dojoType='dijit.Toolbar'>";
+ print "<div dojoType='fox.Toolbar'>";
if (array_key_exists("search", $_REQUEST)) {
$_SESSION["prefs_filter_search"] = $filter_search;
@@ -774,7 +772,7 @@ class Pref_Filters extends Handler_Protected {
__('Search')."</button>
</div>";
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType=\"fox.form.DropDownButton\">".
"<span>" . __('Select')."</span>";
print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
print "<div onclick=\"dijit.byId('filterTree').model.setAllChecked(true)\"
@@ -849,31 +847,30 @@ class Pref_Filters extends Handler_Protected {
print_hidden("method", "add");
print_hidden("csrf_token", $_SESSION['csrf_token']);
- print "<div class='dlgSec'>".__("Caption")."</div>";
+ print "<header>".__("Caption")."</header>";
- print "<div class='dlgSecCont'>";
- print "<input required=\"true\" dojoType=\"dijit.form.ValidationTextBox\" style=\"width : 20em;\" name=\"title\" value=\"\">";
- print "</div>";
-
- print "<div class='dlgSecHoriz'>".__("Match")."</div>";
+ print "<section>";
+ print "<input required='true' dojoType='dijit.form.ValidationTextBox' style='width : 20em;' name='title' value=''>";
+ print "</section>";
- print "<div class='dlgSecCont'>";
+ print "<header class='horizontal'>".__("Match")."</header >";
+ print "<section>";
- print "<div dojoType=\"dijit.Toolbar\">";
+ print "<div dojoType='fox.Toolbar'>";
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType='fox.form.DropDownButton'>".
"<span>" . __('Select')."</span>";
- print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div dojoType='dijit.Menu' style='display: none'>";
print "<div onclick=\"dijit.byId('filterEditDlg').selectRules(true)\"
- dojoType=\"dijit.MenuItem\">".__('All')."</div>";
+ dojoType='dijit.MenuItem'>".__('All')."</div>";
print "<div onclick=\"dijit.byId('filterEditDlg').selectRules(false)\"
- dojoType=\"dijit.MenuItem\">".__('None')."</div>";
+ dojoType='dijit.MenuItem'>".__('None')."</div>";
print "</div></div>";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').addRule()\">".
+ print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterEditDlg').addRule()\">".
__('Add')."</button> ";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').deleteRule()\">".
+ print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterEditDlg').deleteRule()\">".
__('Delete')."</button> ";
print "</div>";
@@ -882,74 +879,69 @@ class Pref_Filters extends Handler_Protected {
# print "<li>No rules</li>";
print "</ul>";
- print "</div>";
- print "</div>";
+ print "</section>";
- print "<div class='dlgSecHoriz'>".__("Apply actions")."</div>";
+ print "<header class='horizontal'>".__("Apply actions")."</header>";
- print "<div class='dlgSecCont'>";
+ print "<section>";
- print "<div dojoType=\"dijit.Toolbar\">";
+ print "<div dojoType='fox.Toolbar'>";
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType='fox.form.DropDownButton'>".
"<span>" . __('Select')."</span>";
- print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div dojoType='dijit.Menu' style='display: none'>";
print "<div onclick=\"dijit.byId('filterEditDlg').selectActions(true)\"
- dojoType=\"dijit.MenuItem\">".__('All')."</div>";
+ dojoType='dijit.MenuItem'>".__('All')."</div>";
print "<div onclick=\"dijit.byId('filterEditDlg').selectActions(false)\"
- dojoType=\"dijit.MenuItem\">".__('None')."</div>";
+ dojoType='dijit.MenuItem'>".__('None')."</div>";
print "</div></div>";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').addAction()\">".
+ print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterEditDlg').addAction()\">".
__('Add')."</button> ";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').deleteAction()\">".
+ print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterEditDlg').deleteAction()\">".
__('Delete')."</button> ";
print "</div>";
-
print "<ul id='filterDlg_Actions'>";
# print "<li>No actions</li>";
print "</ul>";
- print "</div>";
- print "<div class='dlgSec'>".__("Options")."</div>";
+ print "</section>";
- print "<div class='dlgSecCont'>";
+ print "<header>".__("Options")."</header>";
+
+ print "<section>";
print "<fieldset class='narrow'>";
- print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"enabled\" id=\"enabled\" checked=\"1\">
+ print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' name='enabled' id='enabled' checked='1'>
".__('Enabled')."</label>";
print "</fieldset><fieldset class='narrow'>";
- print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"match_any_rule\" id=\"match_any_rule\">
+ print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' name='match_any_rule' id='match_any_rule'>
".__('Match any rule')."</label>";
print "</fieldset><fieldset class='narrow'>";
- print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"inverse\" id=\"inverse\">
+ print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' name='inverse' id='inverse'>
".__('Inverse matching')."</label>";
print "</fieldset>";
- print "</div>";
-
-// print "</div>";
+ print "</section>";
- print "<div class=\"dlgButtons\">";
+ print "<footer>";
- print "<button dojoType=\"dijit.form.Button\" class=\"alt-info\" onclick=\"return dijit.byId('filterEditDlg').test()\">".
+ print "<button dojoType='dijit.form.Button' class='alt-info' onclick=\"return dijit.byId('filterEditDlg').test()\">".
__('Test')."</button> ";
-
- print "<button dojoType=\"dijit.form.Button\" type=\"submit\" class=\"alt-primary\" onclick=\"return dijit.byId('filterEditDlg').execute()\">".
+ print "<button dojoType='dijit.form.Button' type='submit' class='alt-primary' onclick=\"return dijit.byId('filterEditDlg').execute()\">".
__('Create')."</button> ";
-
- print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').hide()\">".
+ print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterEditDlg').hide()\">".
__('Cancel')."</button>";
- print "</div>";
+ print "</footer>";
}
@@ -979,9 +971,9 @@ class Pref_Filters extends Handler_Protected {
$filter_types[$line["id"]] = __($line["description"]);
}
- print "<div class=\"dlgSec\">".__("Match")."</div>";
+ print "<header>".__("Match")."</header>";
- print "<div class=\"dlgSecCont\">";
+ print "<section>";
print "<input dojoType=\"dijit.form.ValidationTextBox\"
required=\"true\" id=\"filterDlg_regExp\"
@@ -1001,7 +993,7 @@ class Pref_Filters extends Handler_Protected {
print "<fieldset>";
print "<label style='display : inline'>". __("on field") . "</label> ";
print_select_hash("filter_type", $filter_type, $filter_types,
- 'dojoType="dijit.form.Select"');
+ 'dojoType="fox.form.Select"');
print "<label style='padding-left : 10px; display : inline'>" . __("in") . "</label> ";
print "</fieldset>";
@@ -1015,9 +1007,9 @@ class Pref_Filters extends Handler_Protected {
print "</fieldset>";
- print "</div>";
+ print "</section>";
- print "<div class='dlgButtons'>";
+ print "<footer>";
print "<button dojoType='dijit.form.Button' style='float : left' class='alt-info' onclick='window.open(\"https://tt-rss.org/wiki/ContentFilters\")'>
<i class='material-icons'>help</i> ".__("More info...")."</button>";
@@ -1028,7 +1020,7 @@ class Pref_Filters extends Handler_Protected {
print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterNewRuleDlg').hide()\">".
__('Cancel')."</button>";
- print "</div>";
+ print "</footer>";
print "</form>";
}
@@ -1046,12 +1038,12 @@ class Pref_Filters extends Handler_Protected {
print "<form name='filter_new_action_form' id='filter_new_action_form' onsubmit='return false;'>";
- print "<div class=\"dlgSec\">".__("Perform Action")."</div>";
+ print "<header>".__("Perform Action")."</header>";
- print "<div class=\"dlgSecCont\">";
+ print "<section>";
- print "<select name=\"action_id\" dojoType=\"dijit.form.Select\"
- onchange=\"Filters.filterDlgCheckAction(this)\">";
+ print "<select name='action_id' dojoType='fox.form.Select'
+ onchange='Filters.filterDlgCheckAction(this)'>";
$res = $this->pdo->query("SELECT id,description FROM ttrss_filter_actions
ORDER BY name");
@@ -1072,16 +1064,16 @@ class Pref_Filters extends Handler_Protected {
$label_param_hidden = ($action_id == 7) ? "" : "display : none";
$plugin_param_hidden = ($action_id == 9) ? "" : "display : none";
- print "<span id=\"filterDlg_paramBox\" style=\"$param_box_hidden\">";
+ print "<span id='filterDlg_paramBox' style=\"$param_box_hidden\">";
print " ";
//print " " . __("with parameters:") . " ";
- print "<input dojoType=\"dijit.form.TextBox\"
- id=\"filterDlg_actionParam\" style=\"$param_hidden\"
- name=\"action_param\" value=\"$action_param\">";
+ print "<input dojoType='dijit.form.TextBox'
+ id='filterDlg_actionParam' style=\"$param_hidden\"
+ name='action_param' value=\"$action_param\">";
print_label_select("action_param_label", $action_param,
- "id=\"filterDlg_actionParamLabel\" style=\"$label_param_hidden\"
- dojoType=\"dijit.form.Select\"");
+ "id='filterDlg_actionParamLabel' style=\"$label_param_hidden\"
+ dojoType='fox.form.Select'");
$filter_actions = PluginHost::getInstance()->get_filter_actions();
$filter_action_hash = array();
@@ -1104,24 +1096,24 @@ class Pref_Filters extends Handler_Protected {
}
print_select_hash("filterDlg_actionParamPlugin", $action_param, $filter_action_hash,
- "style=\"$plugin_param_hidden\" dojoType=\"dijit.form.Select\" $filter_plugin_disabled",
+ "style=\"$plugin_param_hidden\" dojoType='fox.form.Select' $filter_plugin_disabled",
"action_param_plugin");
print "</span>";
print "&nbsp;"; // tiny layout hack
- print "</div>";
+ print "</section>";
- print "<div class=\"dlgButtons\">";
+ print "<footer>";
- print "<button dojoType=\"dijit.form.Button\" class=\"alt-primary\" type=\"submit\" onclick=\"return dijit.byId('filterNewActionDlg').execute()\">".
+ print "<button dojoType='dijit.form.Button' class='alt-primary' type='submit' onclick=\"return dijit.byId('filterNewActionDlg').execute()\">".
($action ? __("Save action") : __('Add action'))."</button> ";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterNewActionDlg').hide()\">".
+ print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterNewActionDlg').hide()\">".
__('Cancel')."</button>";
- print "</div>";
+ print "</footer>";
print "</form>";
}
@@ -1129,11 +1121,11 @@ class Pref_Filters extends Handler_Protected {
private function getFilterName($id) {
$sth = $this->pdo->prepare(
- "SELECT title,match_any_rule,COUNT(DISTINCT r.id) AS num_rules,COUNT(DISTINCT a.id) AS num_actions
+ "SELECT title,match_any_rule,f.inverse AS inverse,COUNT(DISTINCT r.id) AS num_rules,COUNT(DISTINCT a.id) AS num_actions
FROM ttrss_filters2 AS f LEFT JOIN ttrss_filters2_rules AS r
ON (r.filter_id = f.id)
LEFT JOIN ttrss_filters2_actions AS a
- ON (a.filter_id = f.id) WHERE f.id = ? GROUP BY f.title, f.match_any_rule");
+ ON (a.filter_id = f.id) WHERE f.id = ? GROUP BY f.title, f.match_any_rule, f.inverse");
$sth->execute([$id]);
if ($row = $sth->fetch()) {
@@ -1142,6 +1134,7 @@ class Pref_Filters extends Handler_Protected {
$num_rules = $row["num_rules"];
$num_actions = $row["num_actions"];
$match_any_rule = $row["match_any_rule"];
+ $inverse = $row["inverse"];
if (!$title) $title = __("[No caption]");
@@ -1160,6 +1153,7 @@ class Pref_Filters extends Handler_Protected {
}
if ($match_any_rule) $title .= " (" . __("matches any rule") . ")";
+ if ($inverse) $title .= " (" . __("inverse") . ")";
if ($num_actions > 0)
$actions = sprintf(_ngettext("%s (+%d action)", "%s (+%d actions)", (int) $num_actions), $actions, $num_actions);
diff --git a/classes/pref/labels.php b/classes/pref/labels.php
index 28befe42b..ec9667441 100644
--- a/classes/pref/labels.php
+++ b/classes/pref/labels.php
@@ -22,9 +22,9 @@ class Pref_Labels extends Handler_Protected {
print "<form onsubmit='return false;'>";
- print "<div class=\"dlgSec\">".__("Caption")."</div>";
+ print "<header>".__("Caption")."</header>";
- print "<div class=\"dlgSecCont\">";
+ print "<section>";
$fg_color = $line['fg_color'];
$bg_color = $line['bg_color'] ? $line['bg_color'] : '#fff7d5';
@@ -33,53 +33,47 @@ class Pref_Labels extends Handler_Protected {
id='labelEdit_caption' name='caption' dojoType='dijit.form.ValidationTextBox'
required='true' value=\"".htmlspecialchars($line['caption'])."\">";
- print "</div>";
- print "<div class=\"dlgSec\">" . __("Colors") . "</div>";
- print "<div class=\"dlgSecCont\">";
+ print "</section>";
- print "<table cellspacing=\"0\">";
-
- print "<tr><th style='text-align : left'>".__("Foreground:")."</th><th style='text-align : left'>".__("Background:").
- "</td></tr>";
+ print "<header>" . __("Colors") . "</header>";
+ print "<section>";
+ print "<table>";
+ print "<tr><th style='text-align : left'>".__("Foreground:")."</th><th style='text-align : left'>".__("Background:")."</th></tr>";
print "<tr><td style='padding-right : 10px'>";
- print "<input dojoType=\"dijit.form.TextBox\"
- style=\"display : none\" id=\"labelEdit_fgColor\"
- name=\"fg_color\" value=\"$fg_color\">";
- print "<input dojoType=\"dijit.form.TextBox\"
- style=\"display : none\" id=\"labelEdit_bgColor\"
- name=\"bg_color\" value=\"$bg_color\">";
+ print "<input dojoType='dijit.form.TextBox'
+ style='display : none' id='labelEdit_fgColor'
+ name='fg_color' value='$fg_color'>";
+ print "<input dojoType='dijit.form.TextBox'
+ style='display : none' id='labelEdit_bgColor'
+ name='bg_color' value='$bg_color'>";
- print "<div dojoType=\"dijit.ColorPalette\">
- <script type=\"dojo/method\" event=\"onChange\" args=\"fg_color\">
+ print "<div dojoType='dijit.ColorPalette'>
+ <script type='dojo/method' event='onChange' args='fg_color'>
dijit.byId('labelEdit_fgColor').attr('value', fg_color);
dijit.byId('labelEdit_caption').domNode.setStyle({color: fg_color});
</script>
</div>";
- print "</div>";
print "</td><td>";
- print "<div dojoType=\"dijit.ColorPalette\">
- <script type=\"dojo/method\" event=\"onChange\" args=\"bg_color\">
+ print "<div dojoType='dijit.ColorPalette'>
+ <script type='dojo/method' event='onChange' args='bg_color'>
dijit.byId('labelEdit_bgColor').attr('value', bg_color);
dijit.byId('labelEdit_caption').domNode.setStyle({backgroundColor: bg_color});
</script>
</div>";
- print "</div>";
print "</td></tr></table>";
- print "</div>";
-
-# print "</form>";
+ print "</section>";
- print "<div class=\"dlgButtons\">";
- print "<button dojoType=\"dijit.form.Button\" type=\"submit\" class=\"alt-primary\" onclick=\"dijit.byId('labelEditDlg').execute()\">".
+ print "<footer>";
+ print "<button dojoType='dijit.form.Button' type='submit' class='alt-primary' onclick=\"dijit.byId('labelEditDlg').execute()\">".
__('Save')."</button>";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('labelEditDlg').hide()\">".
+ print "<button dojoType='dijit.form.Button' onclick=\"dijit.byId('labelEditDlg').hide()\">".
__('Cancel')."</button>";
- print "</div>";
+ print "</footer>";
print "</form>";
}
@@ -257,9 +251,9 @@ class Pref_Labels extends Handler_Protected {
print "<div dojoType='dijit.layout.BorderContainer' gutters='false'>";
print "<div style='padding : 0px' dojoType='dijit.layout.ContentPane' region='top'>";
- print "<div dojoType='dijit.Toolbar'>";
+ print "<div dojoType='fox.Toolbar'>";
- print "<div dojoType='dijit.form.DropDownButton'>".
+ print "<div dojoType='fox.form.DropDownButton'>".
"<span>" . __('Select')."</span>";
print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
print "<div onclick=\"dijit.byId('labelTree').model.setAllChecked(true)\"
diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php
index fb1c1db06..cb19998ce 100644
--- a/classes/pref/prefs.php
+++ b/classes/pref/prefs.php
@@ -28,6 +28,7 @@ class Pref_Prefs extends Handler_Protected {
__('Feeds') => [
'DEFAULT_UPDATE_INTERVAL',
'FRESH_ARTICLE_MAX_AGE',
+ 'DEFAULT_SEARCH_LANGUAGE',
'BLOCK_SEPARATOR',
'ENABLE_FEED_CATS',
'BLOCK_SEPARATOR',
@@ -68,6 +69,7 @@ class Pref_Prefs extends Handler_Protected {
$this->pref_help = [
"ALLOW_DUPLICATE_POSTS" => array(__("Allow duplicate articles"), ""),
"BLACKLISTED_TAGS" => array(__("Blacklisted tags"), __("Never apply these tags automatically (comma-separated list).")),
+ "DEFAULT_SEARCH_LANGUAGE" => array(__("Default language"), __("Used for full-text search")),
"CDM_AUTO_CATCHUP" => array(__("Mark read on scroll"), __("Mark articles as read as you scroll past them")),
"CDM_EXPANDED" => array(__("Always expand articles")),
"COMBINED_DISPLAY_MODE" => array(__("Combined mode"), __("Show flat list of articles instead of separate panels")),
@@ -560,11 +562,15 @@ class Pref_Prefs extends Handler_Protected {
continue;
}
+ if ($pref_name == "DEFAULT_SEARCH_LANGUAGE" && DB_TYPE != "pgsql") {
+ continue;
+ }
+
if ($item = $prefs_available[$pref_name]) {
- print "<fieldset class='prefs-set'>";
+ print "<fieldset class='prefs'>";
- print "<label for='CB_$pref_name' style='width : 300px'>";
+ print "<label for='CB_$pref_name'>";
print $item['short_desc'] . ":";
print "</label>";
@@ -573,7 +579,7 @@ class Pref_Prefs extends Handler_Protected {
if ($pref_name == "USER_LANGUAGE") {
print_select_hash($pref_name, $value, get_translations(),
- "style='width : 220px; margin : 0px' dojoType='dijit.form.Select'");
+ "style='width : 220px; margin : 0px' dojoType='fox.form.Select'");
} else if ($pref_name == "USER_TIMEZONE") {
@@ -589,7 +595,7 @@ class Pref_Prefs extends Handler_Protected {
if (!theme_exists($value)) $value = "default.php";
- print "<select name='$pref_name' id='$pref_name' dojoType='dijit.form.Select'>";
+ print "<select name='$pref_name' id='$pref_name' dojoType='fox.form.Select'>";
$issel = $value == "default.php" ? "selected='selected'" : "";
print "<option $issel value='default.php'>".__("default")."</option>";
@@ -612,7 +618,11 @@ class Pref_Prefs extends Handler_Protected {
global $update_intervals_nodefault;
print_select_hash($pref_name, $value, $update_intervals_nodefault,
- 'dojoType="dijit.form.Select"');
+ 'dojoType="fox.form.Select"');
+ } else if ($pref_name == "DEFAULT_SEARCH_LANGUAGE") {
+
+ print_select($pref_name, $value, Pref_Feeds::get_ts_languages(),
+ 'dojoType="fox.form.Select"');
} else if ($type_name == "bool") {
@@ -685,7 +695,7 @@ class Pref_Prefs extends Handler_Protected {
}
if ($item['help_text'])
- print "<div class='help-text insensitive'><label for='CB_$pref_name'>".$item['help_text']."</label></div>";
+ print "<div class='help-text text-muted'><label for='CB_$pref_name'>".$item['help_text']."</label></div>";
print "</fieldset>";
}
@@ -705,7 +715,7 @@ class Pref_Prefs extends Handler_Protected {
print_hidden("op", "pref-prefs");
print_hidden("method", "saveconfig");
- print "<div dojoType=\"dijit.form.ComboButton\" type=\"submit\" class=\"alt-primary\">
+ print "<div dojoType=\"fox.form.ComboButton\" type=\"submit\" class=\"alt-primary\">
<span>".__('Save configuration')."</span>
<div dojoType=\"dijit.DropDownMenu\">
<div dojoType=\"dijit.MenuItem\"
@@ -763,18 +773,8 @@ class Pref_Prefs extends Handler_Protected {
print_warning("Your PHP configuration has open_basedir restrictions enabled. Some plugins relying on CURL for functionality may not work correctly.");
}
- print "<table width='100%' class='prefPluginsList'>";
-
- print "<tr><td colspan='5'><h2>".__("System plugins")."</h2>".
- format_notice(__("System plugins are enabled in <strong>config.php</strong> for all users.")).
- "</td></tr>";
-
- print "<tr>
- <th width=\"5%\">&nbsp;</th>
- <th width='10%'>".__('Plugin')."</th>
- <th width=''>".__('Description')."</th>
- <th width='5%'>".__('Version')."</th>
- <th width='10%'>".__('Author')."</th></tr>";
+ print "<h2>".__("System plugins")."</h2>";
+ print_notice("System plugins are enabled in <strong>config.php</strong> for all users.");
$system_enabled = array_map("trim", explode(",", PLUGINS));
$user_enabled = array_map("trim", explode(",", get_pref("_ENABLED_PLUGINS")));
@@ -793,102 +793,73 @@ class Pref_Prefs extends Handler_Protected {
$checked = "";
}
- print "<tr>";
-
- print "<td align='center'><input disabled='1'
- dojoType=\"dijit.form.CheckBox\" $checked
- type=\"checkbox\"></td>";
-
- $icon_class = $checked ? "plugin-enabled" : "plugin-disabled";
+ print "<fieldset class='prefs plugin'>
+ <label>$name:</label>
+ <label class='checkbox description text-muted' id='PLABEL-$name'>
+ <input disabled='1'
+ dojoType='dijit.form.CheckBox' $checked type='checkbox'>
+ ".htmlspecialchars($about[1]). "</label>";
- print "<td><label><i class='material-icons $icon_class'>extension</i> $name</label></td>";
- print "<td>" . htmlspecialchars($about[1]);
- if (@$about[4]) {
- print " &mdash; <a target=\"_blank\" rel=\"noopener noreferrer\" class=\"visibleLink\"
- href=\"".htmlspecialchars($about[4])."\">".__("more info")."</a>";
- }
- print "</td>";
- print "<td>" . htmlspecialchars(sprintf("%.2f", $about[0])) . "</td>";
- print "<td>" . htmlspecialchars($about[2]) . "</td>";
-
- if (count($tmppluginhost->get_all($plugin)) > 0) {
- if (in_array($name, $system_enabled)) {
- print "<td><a href='#' onclick=\"Helpers.clearPluginData('$name')\"
- class='visibleLink'>".__("Clear data")."</a></td>";
+ if (@$about[4]) {
+ print "<button dojoType='dijit.form.Button' class='alt-info'
+ onclick='window.open(\"".htmlspecialchars($about[4])."\")'>
+ <i class='material-icons'>open_in_new</i> ".__("More info...")."</button>";
}
- }
- print "</tr>";
+ print "<div dojoType='dijit.Tooltip' connectId='PLABEL-$name' position='after'>".
+ htmlspecialchars(T_sprintf("v%.2f, by %s", $about[0], $about[2])).
+ "</div>";
+
+ print "</fieldset>";
}
}
- print "<tr><td colspan='4'><br/><h2>".__("User plugins")."</h2></td></tr>";
-
- print "<tr>
- <th width=\"5%\">&nbsp;</th>
- <th width='10%'>".__('Plugin')."</th>
- <th width=''>".__('Description')."</th>
- <th width='5%'>".__('Version')."</th>
- <th width='10%'>".__('Author')."</th></tr>";
-
+ print "<h2>".__("User plugins")."</h2>";
foreach ($tmppluginhost->get_plugins() as $name => $plugin) {
$about = $plugin->about();
if (!$about[3]) {
+ $checked = "";
+ $disabled = "";
+
if (in_array($name, $system_enabled)) {
$checked = "checked='1'";
$disabled = "disabled='1'";
- $rowclass = '';
} else if (in_array($name, $user_enabled)) {
$checked = "checked='1'";
- $disabled = "";
- $rowclass = "Selected";
- } else {
- $checked = "";
- $disabled = "";
- $rowclass = '';
- }
-
- print "<tr class='$rowclass'>";
-
- $icon_class = $checked ? "plugin-enabled" : "plugin-disabled";
-
- print "<td align='center'><input id='FPCHK-$name' name='plugins[]' value='$name' onclick='Tables.onRowChecked(this);'
- dojoType=\"dijit.form.CheckBox\" $checked $disabled
- type=\"checkbox\"></td>";
-
- print "<td><label for='FPCHK-$name'><i class='material-icons $icon_class'>extension</i> $name</label></td>";
- print "<td><label for='FPCHK-$name'>" . htmlspecialchars($about[1]) . "</label>";
- if (@$about[4]) {
- print " &mdash; <a target=\"_blank\" rel=\"noopener noreferrer\" class=\"visibleLink\"
- href=\"".htmlspecialchars($about[4])."\">".__("more info")."</a>";
}
- print "</td>";
- print "<td>" . htmlspecialchars(sprintf("%.2f", $about[0])) . "</td>";
- print "<td>" . htmlspecialchars($about[2]) . "</td>";
+ print "<fieldset class='prefs plugin'>
+ <label>$name:</label>
+ <label class='checkbox description text-muted' id='PLABEL-$name'>
+ <input name='plugins[]' value='$name' dojoType='dijit.form.CheckBox' $checked $disabled type='checkbox'>
+ ".htmlspecialchars($about[1])."</label>";
if (count($tmppluginhost->get_all($plugin)) > 0) {
if (in_array($name, $system_enabled) || in_array($name, $user_enabled)) {
- print "<td><a href='#' onclick=\"Helpers.clearPluginData('$name')\" class='visibleLink'>".__("Clear data")."</a></td>";
+ print " <button dojoType='dijit.form.Button'
+ onclick=\"Helpers.clearPluginData('$name')\">
+ <i class='material-icons'>clear</i> ".__("Clear data")."</button>";
}
}
- print "</tr>";
-
+ if (@$about[4]) {
+ print " <button dojoType='dijit.form.Button' class='alt-info'
+ onclick='window.open(\"".htmlspecialchars($about[4])."\")'>
+ <i class='material-icons'>open_in_new</i> ".__("More info...")."</button>";
+ }
+ print "<div dojoType='dijit.Tooltip' connectId='PLABEL-$name' position='after'>".
+ htmlspecialchars(T_sprintf("v%.2f, by %s", $about[0], $about[2])).
+ "</div>";
+ print "</fieldset>";
}
-
}
- print "</table>";
-
- //print "<p>" . __("You will need to reload Tiny Tiny RSS for plugin changes to take effect.") . "</p>";
-
print "</div>"; #content-pane
print '<div dojoType="dijit.layout.ContentPane" region="bottom">';
@@ -904,7 +875,6 @@ class Pref_Prefs extends Handler_Protected {
print "</form>";
-
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB,
"hook_prefs_tab", "prefPrefs");
@@ -1039,35 +1009,34 @@ class Pref_Prefs extends Handler_Protected {
print_hidden("key", "USER_STYLESHEET");
print "<textarea class='panel user-css-editor' dojoType='dijit.form.SimpleTextarea'
- style='font-size : 12px;'
- name='value'>$value</textarea>";
+ style='font-size : 12px;' name='value'>$value</textarea>";
- print "<div class='dlgButtons'>";
- print "<button dojoType=\"dijit.form.Button\"
+ print "<footer>";
+ print "<button dojoType='dijit.form.Button'
onclick=\"dijit.byId('cssEditDlg').execute()\">".__('Save')."</button> ";
- print "<button dojoType=\"dijit.form.Button\"
+ print "<button dojoType='dijit.form.Button'
onclick=\"dijit.byId('cssEditDlg').hide()\">".__('Cancel')."</button>";
- print "</div>";
+ print "</footer>";
}
function editPrefProfiles() {
- print "<div dojoType=\"dijit.Toolbar\">";
+ print "<div dojoType='fox.Toolbar'>";
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType='fox.form.DropDownButton'>".
"<span>" . __('Select')."</span>";
- print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div dojoType='dijit.Menu' style='display: none'>";
print "<div onclick=\"Tables.select('pref-profiles-list', true)\"
- dojoType=\"dijit.MenuItem\">".__('All')."</div>";
+ dojoType='dijit.MenuItem'>".__('All')."</div>";
print "<div onclick=\"Tables.select('pref-profiles-list', false)\"
- dojoType=\"dijit.MenuItem\">".__('None')."</div>";
+ dojoType='dijit.MenuItem'>".__('None')."</div>";
print "</div></div>";
- print "<div style=\"float : right\">";
+ print "<div style='float : right'>";
- print "<input name=\"newprofile\" dojoType=\"dijit.form.ValidationTextBox\"
- required=\"1\">
- <button dojoType=\"dijit.form.Button\"
+ print "<input name='newprofile' dojoType='dijit.form.ValidationTextBox'
+ required='1'>
+ <button dojoType='dijit.form.Button'
onclick=\"dijit.byId('profileEditDlg').addProfile()\">".
__('Create profile')."</button></div>";
@@ -1113,10 +1082,10 @@ class Pref_Prefs extends Handler_Protected {
$is_active = "";
}
- print "<td><span dojoType=\"dijit.InlineEditBox\"
- width=\"300px\" autoSave=\"false\"
- profile-id=\"$profile_id\">" . $edit_title .
- "<script type=\"dojo/method\" event=\"onChange\" args=\"item\">
+ print "<td><span dojoType='dijit.InlineEditBox'
+ width='300px' autoSave='false'
+ profile-id='$profile_id'>" . $edit_title .
+ "<script type='dojo/method' event='onChange' args='item'>
var elem = this;
dojo.xhrPost({
url: 'backend.php',
@@ -1137,17 +1106,14 @@ class Pref_Prefs extends Handler_Protected {
print "</form>";
print "</div>";
- print "<div class='dlgButtons'>
- <div style='float : left'>
- <button class=\"alt-danger\" dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('profileEditDlg').removeSelected()\">".
+ print "<footer>
+ <button style='float : left' class='alt-danger' dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('profileEditDlg').removeSelected()\">".
__('Remove selected profiles')."</button>
- <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('profileEditDlg').activateProfile()\">".
+ <button dojoType='dijit.form.Button' class='alt-primary' type='submit' onclick=\"dijit.byId('profileEditDlg').activateProfile()\">".
__('Activate profile')."</button>
- </div>";
-
- print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('profileEditDlg').hide()\">".
- __('Close this window')."</button>";
- print "</div>";
+ <button dojoType='dijit.form.Button' onclick=\"dijit.byId('profileEditDlg').hide()\">".
+ __('Cancel')."</button>";
+ print "</footer>";
}
diff --git a/classes/pref/users.php b/classes/pref/users.php
index 86dd8aac1..680290b74 100644
--- a/classes/pref/users.php
+++ b/classes/pref/users.php
@@ -20,7 +20,7 @@ class Pref_Users extends Handler_Protected {
function edit() {
global $access_level_names;
- print "<form id=\"user_edit_form\" onsubmit='return false' dojoType=\"dijit.form.Form\">";
+ print "<form id='user_edit_form' onsubmit='return false' dojoType='dijit.form.Form'>";
print '<div dojoType="dijit.layout.TabContainer" style="height : 400px">
<div dojoType="dijit.layout.ContentPane" title="'.__('Edit user').'">';
@@ -44,53 +44,59 @@ class Pref_Users extends Handler_Protected {
$sel_disabled = ($id == $_SESSION["uid"] || $login == "admin") ? "disabled" : "";
- print "<div class=\"dlgSec\">".__("User")."</div>";
- print "<div class=\"dlgSecCont\">";
+ print "<header>".__("User")."</header>";
+ print "<section>";
if ($sel_disabled) {
print_hidden("login", "$login");
}
- print "<input size=\"30\" style=\"font-size : 16px\"
- dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
- $sel_disabled
- name=\"login\" value=\"$login\">";
+ print "<fieldset>";
+ print "<label>" . __("Login:") . "</label>";
+ print "<input style='font-size : 16px'
+ dojoType='dijit.form.ValidationTextBox' required='1'
+ $sel_disabled name='login' value=\"$login\">";
+ print "</fieldset>";
- print "</div>";
+ print "</section>";
- print "<div class=\"dlgSec\">".__("Authentication")."</div>";
- print "<div class=\"dlgSecCont\">";
+ print "<header>".__("Authentication")."</header>";
+ print "<section>";
print "<fieldset>";
- print __('Access level: ') . " ";
+ print "<label>" . __('Access level: ') . "</label> ";
if (!$sel_disabled) {
print_select_hash("access_level", $access_level, $access_level_names,
- "dojoType=\"dijit.form.Select\" $sel_disabled");
+ "dojoType=\"fox.form.Select\" $sel_disabled");
} else {
print_select_hash("", $access_level, $access_level_names,
- "dojoType=\"dijit.form.Select\" $sel_disabled");
+ "dojoType=\"fox.form.Select\" $sel_disabled");
print_hidden("access_level", "$access_level");
}
print "</fieldset>";
print "<fieldset>";
- print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" size=\"20\" placeholder=\"Change password\"
- name=\"password\">";
+ print "<label>" . __("New password:") . "</label> ";
+ print "<input dojoType='dijit.form.TextBox' type='password' size='20' placeholder='Change password'
+ name='password'>";
print "</fieldset>";
- print "</div>";
+ print "</section>";
- print "<div class=\"dlgSec\">".__("Options")."</div>";
- print "<div class=\"dlgSecCont\">";
+ print "<header>".__("Options")."</header>";
+ print "<section>";
- print "<input dojoType=\"dijit.form.TextBox\" size=\"30\" name=\"email\" placeholder=\"E-mail\"
- value=\"$email\">";
+ print "<fieldset>";
+ print "<label>" . __("E-mail:") . "</label> ";
+ print "<input dojoType='dijit.form.TextBox' size='30' name='email'
+ value=\"$email\">";
+ print "</fieldset>";
- print "</div>";
+ print "</section>";
print "</table>";
@@ -103,11 +109,12 @@ class Pref_Users extends Handler_Protected {
print '</div>';
print '</div>';
- print "<div class=\"dlgButtons\">
- <button dojoType=\"dijit.form.Button\" class=\"alt-primary\" type=\"submit\" onclick=\"dijit.byId('userEditDlg').execute()\">".
+ print "<footer>
+ <button dojoType='dijit.form.Button' class='alt-primary' type='submit' onclick=\"dijit.byId('userEditDlg').execute()\">".
__('Save')."</button>
- <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('userEditDlg').hide()\">".
- __('Cancel')."</button></div>";
+ <button dojoType='dijit.form.Button' onclick=\"dijit.byId('userEditDlg').hide()\">".
+ __('Cancel')."</button>
+ </footer>";
print "</form>";
@@ -224,7 +231,7 @@ class Pref_Users extends Handler_Protected {
function add() {
$login = trim(clean($_REQUEST["login"]));
- $tmp_user_pwd = make_password(8);
+ $tmp_user_pwd = make_password();
$salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
$pwd_hash = encrypt_password($tmp_user_pwd, $salt, true);
@@ -264,21 +271,19 @@ class Pref_Users extends Handler_Protected {
}
}
- static function resetUserPassword($uid, $show_password) {
+ static function resetUserPassword($uid, $format_output = false) {
$pdo = Db::pdo();
- $sth = $pdo->prepare("SELECT login, email
- FROM ttrss_users WHERE id = ?");
+ $sth = $pdo->prepare("SELECT login FROM ttrss_users WHERE id = ?");
$sth->execute([$uid]);
if ($row = $sth->fetch()) {
$login = $row["login"];
- $email = $row["email"];
$new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
- $tmp_user_pwd = make_password(8);
+ $tmp_user_pwd = make_password();
$pwd_hash = encrypt_password($tmp_user_pwd, $new_salt, true);
@@ -287,44 +292,19 @@ class Pref_Users extends Handler_Protected {
WHERE id = ?");
$sth->execute([$pwd_hash, $new_salt, $uid]);
- if ($show_password) {
- print_notice(T_sprintf("Changed password of user %s to %s", $login, $tmp_user_pwd));
- } else {
- print_notice(T_sprintf("Sending new password of user %s to %s", $login, $email));
-
- if ($email) {
- require_once "lib/MiniTemplator.class.php";
-
- $tpl = new MiniTemplator;
-
- $tpl->readTemplateFromFile("templates/resetpass_template.txt");
+ $message = T_sprintf("Changed password of user %s to %s", "<strong>$login</strong>", "<strong>$tmp_user_pwd</strong>");
- $tpl->setVariable('LOGIN', $login);
- $tpl->setVariable('NEWPASS', $tmp_user_pwd);
-
- $tpl->addBlock('message');
-
- $message = "";
-
- $tpl->generateOutputToString($message);
-
- $mailer = new Mailer();
-
- $rc = $mailer->mail(["to_name" => $login,
- "to_address" => $email,
- "subject" => __("[tt-rss] Password change notification"),
- "message" => $message]);
-
- if (!$rc) print_error($mailer->error());
- }
- }
+ if ($format_output)
+ print_notice($message);
+ else
+ print $message;
}
}
function resetPass() {
$uid = clean($_REQUEST["id"]);
- Pref_Users::resetUserPassword($uid, true);
+ Pref_Users::resetUserPassword($uid);
}
function index() {
@@ -333,7 +313,7 @@ class Pref_Users extends Handler_Protected {
print "<div dojoType='dijit.layout.BorderContainer' gutters='false'>";
print "<div style='padding : 0px' dojoType='dijit.layout.ContentPane' region='top'>";
- print "<div dojoType='dijit.Toolbar'>";
+ print "<div dojoType='fox.Toolbar'>";
$user_search = trim(clean($_REQUEST["search"]));
@@ -344,9 +324,9 @@ class Pref_Users extends Handler_Protected {
}
print "<div style='float : right; padding-right : 4px;'>
- <input dojoType=\"dijit.form.TextBox\" id=\"user_search\" size=\"20\" type=\"search\"
+ <input dojoType='dijit.form.TextBox' id='user_search' size='20' type='search'
value=\"$user_search\">
- <button dojoType=\"dijit.form.Button\" oncl1ick=\"Users.reload()\">".
+ <button dojoType='dijit.form.Button' onclick='Users.reload()'>".
__('Search')."</button>
</div>";
@@ -356,23 +336,23 @@ class Pref_Users extends Handler_Protected {
$sort = "login";
}
- print "<div dojoType=\"dijit.form.DropDownButton\">".
+ print "<div dojoType='fox.form.DropDownButton'>".
"<span>" . __('Select')."</span>";
- print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div dojoType='dijit.Menu' style='display: none'>";
print "<div onclick=\"Tables.select('prefUserList', true)\"
- dojoType=\"dijit.MenuItem\">".__('All')."</div>";
+ dojoType='dijit.MenuItem'>".__('All')."</div>";
print "<div onclick=\"Tables.select('prefUserList', false)\"
- dojoType=\"dijit.MenuItem\">".__('None')."</div>";
+ dojoType='dijit.MenuItem'>".__('None')."</div>";
print "</div></div>";
- print "<button dojoType=\"dijit.form.Button\" onclick=\"Users.add()\">".__('Create user')."</button>";
+ print "<button dojoType='dijit.form.Button' onclick='Users.add()'>".__('Create user')."</button>";
print "
- <button dojoType=\"dijit.form.Button\" onclick=\"Users.editSelected()\">".
+ <button dojoType='dijit.form.Button' onclick='Users.editSelected()'>".
__('Edit')."</button dojoType=\"dijit.form.Button\">
- <button dojoType=\"dijit.form.Button\" onclick=\"Users.removeSelected()\">".
+ <button dojoType='dijit.form.Button' onclick='Users.removeSelected()'>".
__('Remove')."</button dojoType=\"dijit.form.Button\">
- <button dojoType=\"dijit.form.Button\" onclick=\"Users.resetSelected()\">".
+ <button dojoType='dijit.form.Button' onclick='Users.resetSelected()'>".
__('Reset password')."</button dojoType=\"dijit.form.Button\">";
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION,
@@ -400,16 +380,15 @@ class Pref_Users extends Handler_Protected {
ORDER BY $sort");
$sth->execute([":search" => $user_search ? "%$user_search%" : ""]);
- print "<p><table width=\"100%\" cellspacing=\"0\"
- class=\"prefUserList\" id=\"prefUserList\">";
+ print "<p><table width='100%' cellspacing='0' class='prefUserList' id='prefUserList'>";
- print "<tr class=\"title\">
- <td align='center' width=\"5%\">&nbsp;</td>
- <td width='20%'><a href=\"#\" onclick=\"Users.reload('login')\">".__('Login')."</a></td>
- <td width='20%'><a href=\"#\" onclick=\"Users.reload('access_level')\">".__('Access Level')."</a></td>
- <td width='10%'><a href=\"#\" onclick=\"Users.reload('num_feeds')\">".__('Subscribed feeds')."</a></td>
- <td width='20%'><a href=\"#\" onclick=\"Users.reload('created')\">".__('Registered')."</a></td>
- <td width='20%'><a href=\"#\" onclick=\"Users.reload('last_login')\">".__('Last login')."</a></td></tr>";
+ print "<tr class='title'>
+ <td align='center' width='5%'>&nbsp;</td>
+ <td width='20%'><a href='#' onclick=\"Users.reload('login')\">".__('Login')."</a></td>
+ <td width='20%'><a href='#' onclick=\"Users.reload('access_level')\">".__('Access Level')."</a></td>
+ <td width='10%'><a href='#' onclick=\"Users.reload('num_feeds')\">".__('Subscribed feeds')."</a></td>
+ <td width='20%'><a href='#' onclick=\"Users.reload('created')\">".__('Registered')."</a></td>
+ <td width='20%'><a href='#' onclick=\"Users.reload('last_login')\">".__('Last login')."</a></td></tr>";
$lnum = 0;
@@ -417,14 +396,14 @@ class Pref_Users extends Handler_Protected {
$uid = $line["id"];
- print "<tr data-row-id=\"$uid\" onclick='Users.edit($uid)'>";
+ print "<tr data-row-id='$uid' onclick='Users.edit($uid)'>";
$line["login"] = htmlspecialchars($line["login"]);
$line["created"] = make_local_datetime($line["created"], false);
$line["last_login"] = make_local_datetime($line["last_login"], false);
print "<td align='center'><input onclick='Tables.onRowChecked(this); event.stopPropagation();'
- dojoType=\"dijit.form.CheckBox\" type=\"checkbox\"></td>";
+ dojoType='dijit.form.CheckBox' type='checkbox'></td>";
print "<td title='".__('Click to edit')."'><i class='material-icons'>person</i> " . $line["login"] . "</td>";
diff --git a/classes/rpc.php b/classes/rpc.php
index 7220e10ea..725ca9622 100755
--- a/classes/rpc.php
+++ b/classes/rpc.php
@@ -240,8 +240,8 @@ class RPC extends Handler_Protected {
$new_feed_id = (int)$row['id'] + 1;
$sth = $this->pdo->prepare("INSERT INTO ttrss_archived_feeds
- (id, owner_uid, title, feed_url, site_url)
- SELECT ?, owner_uid, title, feed_url, site_url from ttrss_feeds
+ (id, owner_uid, title, feed_url, site_url, created)
+ SELECT ?, owner_uid, title, feed_url, site_url, NOW() from ttrss_feeds
WHERE id = ?");
$sth->execute([$new_feed_id, $feed_id]);
@@ -287,7 +287,7 @@ class RPC extends Handler_Protected {
/* GET["cmode"] = 0 - mark as read, 1 - as unread, 2 - toggle */
function catchupSelected() {
$ids = explode(",", clean($_REQUEST["ids"]));
- $cmode = sprintf("%d", clean($_REQUEST["cmode"]));
+ $cmode = (int)clean($_REQUEST["cmode"]);
Article::catchupArticlesById($ids, $cmode);
@@ -347,20 +347,6 @@ class RPC extends Handler_Protected {
print "</ul>";
}
- function updateFeedBrowser() {
- if (defined('_DISABLE_FEED_BROWSER') && _DISABLE_FEED_BROWSER) return;
-
- $search = clean($_REQUEST["search"]);
- $limit = clean($_REQUEST["limit"]);
- $mode = (int) clean($_REQUEST["mode"]);
-
- require_once "feedbrowser.php";
-
- print json_encode(array("content" =>
- make_feed_browser($search, $limit, $mode),
- "mode" => $mode));
- }
-
// Silent
function massSubscribe() {
diff --git a/classes/rssutils.php b/classes/rssutils.php
index 6048c8310..68e0255ed 100755
--- a/classes/rssutils.php
+++ b/classes/rssutils.php
@@ -19,51 +19,9 @@ class RSSUtils {
return preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $str);
}
- static function update_feedbrowser_cache() {
-
+ static function cleanup_feed_browser() {
$pdo = Db::pdo();
-
- $sth = $pdo->query("SELECT feed_url, site_url, title, COUNT(id) AS subscribers
- FROM ttrss_feeds WHERE feed_url NOT IN (SELECT feed_url FROM ttrss_feeds
- WHERE private IS true OR auth_login != '' OR auth_pass != '' OR feed_url LIKE '%:%@%/%')
- GROUP BY feed_url, site_url, title ORDER BY subscribers DESC LIMIT 1000");
-
- $pdo->beginTransaction();
-
$pdo->query("DELETE FROM ttrss_feedbrowser_cache");
-
- $count = 0;
-
- while ($line = $sth->fetch()) {
-
- $subscribers = $line["subscribers"];
- $feed_url = $line["feed_url"];
- $title = $line["title"];
- $site_url = $line["site_url"];
-
- $tmph = $pdo->prepare("SELECT subscribers FROM
- ttrss_feedbrowser_cache WHERE feed_url = ?");
- $tmph->execute([$feed_url]);
-
- if (!$tmph->fetch()) {
-
- $tmph = $pdo->prepare("INSERT INTO ttrss_feedbrowser_cache
- (feed_url, site_url, title, subscribers)
- VALUES
- (?, ?, ?, ?)");
-
- $tmph->execute([$feed_url, $site_url, $title, $subscribers]);
-
- ++$count;
-
- }
-
- }
-
- $pdo->commit();
-
- return $count;
-
}
static function update_daemon_common($limit = DAEMON_FEED_LIMIT) {
@@ -305,7 +263,7 @@ class RSSUtils {
$pdo = Db::pdo();
- $sth = $pdo->prepare("SELECT title FROM ttrss_feeds WHERE id = ?");
+ $sth = $pdo->prepare("SELECT title, site_url FROM ttrss_feeds WHERE id = ?");
$sth->execute([$feed]);
if (!$row = $sth->fetch()) {
@@ -315,11 +273,12 @@ class RSSUtils {
}
$title = $row["title"];
+ $site_url = $row["site_url"];
// feed was batch-subscribed or something, we need to get basic info
// this is not optimal currently as it fetches stuff separately TODO: optimize
- if ($title == "[Unknown]") {
- Debug::log("setting basic feed info for $feed...");
+ if ($title == "[Unknown]" || !$title || !$site_url) {
+ Debug::log("setting basic feed info for $feed [$title, $site_url]...");
RSSUtils::set_basic_feed_info($feed);
}
@@ -349,7 +308,12 @@ class RSSUtils {
$fetch_url = $row["feed_url"];
$feed_language = mb_strtolower($row["feed_language"]);
- if (!$feed_language) $feed_language = 'english';
+
+ if (!$feed_language)
+ $feed_language = mb_strtolower(get_pref('DEFAULT_SEARCH_LANGUAGE', $owner_uid));
+
+ if (!$feed_language)
+ $feed_language = 'simple';
} else {
return false;
@@ -1206,10 +1170,12 @@ class RSSUtils {
$sth->execute([$error_msg, $feed]);
unset($rss);
+
+ Debug::log("update failed.", Debug::$LOG_VERBOSE);
return false;
}
- Debug::log("done", Debug::$LOG_VERBOSE);
+ Debug::log("update done.", Debug::$LOG_VERBOSE);
return true;
}
@@ -1241,12 +1207,8 @@ class RSSUtils {
static function cache_media($html, $site_url) {
libxml_use_internal_errors(true);
- $charset_hack = '<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
- </head>';
-
$doc = new DOMDocument();
- $doc->loadHTML($charset_hack . $html);
+ $doc->loadHTML('<?xml encoding="UTF-8">' . $html);
$xpath = new DOMXPath($doc);
$entries = $xpath->query('(//img[@src])|(//video/source[@src])|(//audio/source[@src])');
@@ -1288,6 +1250,20 @@ class RSSUtils {
}
}
+ static function expire_feed_archive() {
+ Debug::log("Removing old archived feeds...");
+
+ $pdo = Db::pdo();
+
+ if (DB_TYPE == "pgsql") {
+ $pdo->query("DELETE FROM ttrss_archived_feeds
+ WHERE created < NOW() - INTERVAL '1 month'");
+ } else {
+ $pdo->query("DELETE FROM ttrss_archived_feeds
+ WHERE created < DATE_SUB(NOW(), INTERVAL 1 MONTH)");
+ }
+ }
+
static function expire_lock_files() {
Debug::log("Removing old lock files...", Debug::$LOG_VERBOSE);
@@ -1526,9 +1502,8 @@ class RSSUtils {
RSSUtils::expire_cached_files();
RSSUtils::expire_lock_files();
RSSUtils::expire_error_log();
-
- $count = RSSUtils::update_feedbrowser_cache();
- Debug::log("Feedbrowser updated, $count feeds processed.");
+ RSSUtils::expire_feed_archive();
+ RSSUtils::cleanup_feed_browser();
Article::purge_orphans();
RSSUtils::cleanup_counters_cache();