init.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <?php
  2. use andreskrey\Readability\Readability;
  3. use andreskrey\Readability\Configuration;
  4. class Af_Readability extends Plugin {
  5. /* @var PluginHost $host */
  6. private $host;
  7. function about() {
  8. return array(1.0,
  9. "Try to inline article content using Readability",
  10. "fox");
  11. }
  12. function flags() {
  13. return array("needs_curl" => true);
  14. }
  15. function save() {
  16. $enable_share_anything = checkbox_to_sql_bool($_POST["enable_share_anything"]);
  17. $this->host->set($this, "enable_share_anything", $enable_share_anything);
  18. echo __("Data saved.");
  19. }
  20. function init($host)
  21. {
  22. $this->host = $host;
  23. if (version_compare(PHP_VERSION, '5.6.0', '<')) {
  24. return;
  25. }
  26. $host->add_hook($host::HOOK_ARTICLE_FILTER, $this);
  27. $host->add_hook($host::HOOK_PREFS_TAB, $this);
  28. $host->add_hook($host::HOOK_PREFS_EDIT_FEED, $this);
  29. $host->add_hook($host::HOOK_PREFS_SAVE_FEED, $this);
  30. $host->add_filter_action($this, "action_inline", __("Inline content"));
  31. }
  32. function hook_prefs_tab($args) {
  33. if ($args != "prefFeeds") return;
  34. print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Readability settings (af_readability)')."\">";
  35. if (version_compare(PHP_VERSION, '5.6.0', '<')) {
  36. print_error("This plugin requires PHP version 5.6.");
  37. }
  38. print_notice("Enable the plugin for specific feeds in the feed editor.");
  39. print "<form dojoType=\"dijit.form.Form\">";
  40. print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\">
  41. evt.preventDefault();
  42. if (this.validate()) {
  43. console.log(dojo.objectToQuery(this.getValues()));
  44. new Ajax.Request('backend.php', {
  45. parameters: dojo.objectToQuery(this.getValues()),
  46. onComplete: function(transport) {
  47. notify_info(transport.responseText);
  48. }
  49. });
  50. //this.reset();
  51. }
  52. </script>";
  53. print_hidden("op", "pluginhandler");
  54. print_hidden("method", "save");
  55. print_hidden("plugin", "af_readability");
  56. $enable_share_anything = $this->host->get($this, "enable_share_anything");
  57. print_checkbox("enable_share_anything", $enable_share_anything);
  58. print "&nbsp;<label for=\"enable_share_anything\">" . __("Use Readability for pages shared via bookmarklet.") . "</label>";
  59. print "<p>"; print_button("submit", __("Save"));
  60. print "</form>";
  61. $enabled_feeds = $this->host->get($this, "enabled_feeds");
  62. if (!is_array($enabled_feeds)) $enabled_feeds = array();
  63. $enabled_feeds = $this->filter_unknown_feeds($enabled_feeds);
  64. $this->host->set($this, "enabled_feeds", $enabled_feeds);
  65. if (count($enabled_feeds) > 0) {
  66. print "<h3>" . __("Currently enabled for (click to edit):") . "</h3>";
  67. print "<ul class=\"browseFeedList\" style=\"border-width : 1px\">";
  68. foreach ($enabled_feeds as $f) {
  69. print "<li>" .
  70. "<img src='images/pub_set.png'
  71. style='vertical-align : middle'> <a href='#'
  72. onclick='editFeed($f)'>".
  73. Feeds::getFeedTitle($f) . "</a></li>";
  74. }
  75. print "</ul>";
  76. }
  77. print "</div>";
  78. }
  79. function hook_prefs_edit_feed($feed_id) {
  80. print "<div class=\"dlgSec\">".__("Readability")."</div>";
  81. print "<div class=\"dlgSecCont\">";
  82. $enabled_feeds = $this->host->get($this, "enabled_feeds");
  83. if (!is_array($enabled_feeds)) $enabled_feeds = array();
  84. $key = array_search($feed_id, $enabled_feeds);
  85. $checked = $key !== FALSE ? "checked" : "";
  86. print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"af_readability_enabled\"
  87. name=\"af_readability_enabled\"
  88. $checked>&nbsp;<label for=\"af_readability_enabled\">".__('Inline article content')."</label>";
  89. print "</div>";
  90. }
  91. function hook_prefs_save_feed($feed_id) {
  92. $enabled_feeds = $this->host->get($this, "enabled_feeds");
  93. if (!is_array($enabled_feeds)) $enabled_feeds = array();
  94. $enable = checkbox_to_sql_bool($_POST["af_readability_enabled"]);
  95. $key = array_search($feed_id, $enabled_feeds);
  96. if ($enable) {
  97. if ($key === FALSE) {
  98. array_push($enabled_feeds, $feed_id);
  99. }
  100. } else {
  101. if ($key !== FALSE) {
  102. unset($enabled_feeds[$key]);
  103. }
  104. }
  105. $this->host->set($this, "enabled_feeds", $enabled_feeds);
  106. }
  107. /**
  108. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  109. */
  110. function hook_article_filter_action($article, $action) {
  111. return $this->process_article($article);
  112. }
  113. public function extract_content($url) {
  114. global $fetch_effective_url;
  115. $tmp = fetch_file_contents([
  116. "url" => $url,
  117. "http_accept" => "text/*",
  118. "type" => "text/html"]);
  119. if ($tmp && mb_strlen($tmp) < 1024 * 500) {
  120. $tmpdoc = new DOMDocument("1.0", "UTF-8");
  121. if (!$tmpdoc->loadHTML('<?xml encoding="utf-8" ?>\n' . $tmp))
  122. return false;
  123. if (strtolower($tmpdoc->encoding) != 'utf-8') {
  124. $tmpxpath = new DOMXPath($tmpdoc);
  125. foreach ($tmpxpath->query("//meta") as $elem) {
  126. $elem->parentNode->removeChild($elem);
  127. }
  128. $tmp = $tmpdoc->saveHTML();
  129. }
  130. $r = new Readability(new Configuration());
  131. try {
  132. if ($r->parse($tmp)) {
  133. $tmpxpath = new DOMXPath($r->getDOMDOcument());
  134. $entries = $tmpxpath->query('(//a[@href]|//img[@src])');
  135. foreach ($entries as $entry) {
  136. if ($entry->hasAttribute("href")) {
  137. $entry->setAttribute("href",
  138. rewrite_relative_url($fetch_effective_url, $entry->getAttribute("href")));
  139. }
  140. if ($entry->hasAttribute("src")) {
  141. $entry->setAttribute("src",
  142. rewrite_relative_url($fetch_effective_url, $entry->getAttribute("src")));
  143. }
  144. }
  145. return $r->getContent();
  146. }
  147. } catch (Exception $e) {
  148. return false;
  149. }
  150. }
  151. return false;
  152. }
  153. function process_article($article) {
  154. $extracted_content = $this->extract_content($article["link"]);
  155. # let's see if there's anything of value in there
  156. $content_test = trim(strip_tags(sanitize($extracted_content)));
  157. if ($content_test) {
  158. $article["content"] = $extracted_content;
  159. }
  160. return $article;
  161. }
  162. function hook_article_filter($article) {
  163. $enabled_feeds = $this->host->get($this, "enabled_feeds");
  164. if (!is_array($enabled_feeds)) return $article;
  165. $key = array_search($article["feed"]["id"], $enabled_feeds);
  166. if ($key === FALSE) return $article;
  167. return $this->process_article($article);
  168. }
  169. function api_version() {
  170. return 2;
  171. }
  172. private function filter_unknown_feeds($enabled_feeds) {
  173. $tmp = array();
  174. foreach ($enabled_feeds as $feed) {
  175. $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? AND owner_uid = ?");
  176. $sth->execute([$feed, $_SESSION['uid']]);
  177. if ($row = $sth->fetch()) {
  178. array_push($tmp, $feed);
  179. }
  180. }
  181. return $tmp;
  182. }
  183. }