diff options
Diffstat (limited to 'classes')
-rwxr-xr-x | classes/article.php | 78 | ||||
-rw-r--r-- | classes/backend.php | 22 | ||||
-rw-r--r-- | classes/db/prefs.php | 4 | ||||
-rw-r--r-- | classes/dbupdater.php | 19 | ||||
-rw-r--r-- | classes/digest.php | 15 | ||||
-rw-r--r-- | classes/dlg.php | 52 | ||||
-rw-r--r-- | classes/feedparser.php | 58 | ||||
-rwxr-xr-x | classes/feeds.php | 234 | ||||
-rwxr-xr-x | classes/handler/public.php | 121 | ||||
-rwxr-xr-x | classes/logger/sql.php | 8 | ||||
-rw-r--r-- | classes/mailer.php | 3 | ||||
-rw-r--r-- | classes/opml.php | 72 | ||||
-rw-r--r-- | classes/plugin.php | 19 | ||||
-rwxr-xr-x | classes/pluginhost.php | 10 | ||||
-rwxr-xr-x | classes/pref/feeds.php | 392 | ||||
-rwxr-xr-x | classes/pref/filters.php | 178 | ||||
-rw-r--r-- | classes/pref/labels.php | 54 | ||||
-rw-r--r-- | classes/pref/prefs.php | 192 | ||||
-rw-r--r-- | classes/pref/users.php | 147 | ||||
-rwxr-xr-x | classes/rpc.php | 20 | ||||
-rwxr-xr-x | classes/rssutils.php | 87 |
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 .= " "; - $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\"> ".__('Hide from Popular feeds')."</label>"; - - print " "; $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\"> ".__('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'> ".__('Include in e-mail digest')."</label>"; print " "; $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\"> ".__('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'> ".__('Always display image attachments')."</label>"; print " "; $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\"> ". __('Do not embed media')."</label>"; + print "<label class='checkbox'><input disabled='1' type='checkbox' id='hide_images' + name='hide_images' dojoType='dijit.form.CheckBox'> ". __('Do not embed media')."</label>"; print " "; $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\"> ".__('Cache media')."</label>"; + print "<label class='checkbox'><input disabled='1' type='checkbox' id='cache_images' + name='cache_images' dojoType='dijit.form.CheckBox'> ".__('Cache media')."</label>"; print " "; $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\"> ".__('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'> ".__('Mark updated articles as unread')."</label>"; print " "; $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 " "; // 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%\"> </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 " — <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%\"> </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 " — <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%\"> </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%'> </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(); |