Issues (1270)

plugins/af_readability/init.php (8 issues)

1
<?php
2
use andreskrey\Readability\Readability;
3
use andreskrey\Readability\Configuration;
4
5
class Af_Readability extends Plugin {
6
7
    /* @var PluginHost $host */
8
    private $host;
9
10
    public function about() {
11
        return array(1.0,
12
            "Try to inline article content using Readability",
13
            "fox");
14
    }
15
16
    public function flags() {
17
        return array("needs_curl" => true);
18
    }
19
20
    public function save() {
21
        $enable_share_anything = checkbox_to_sql_bool($_POST["enable_share_anything"]);
22
23
        $this->host->set($this, "enable_share_anything", $enable_share_anything);
24
25
        echo __("Data saved.");
26
    }
27
28
    public function init($host)
29
    {
30
        $this->host = $host;
31
32
        if (version_compare(PHP_VERSION, '7.0.0', '<')) {
33
            user_error("af_readability requires PHP 7.0", E_USER_WARNING);
34
            return;
35
        }
36
37
        $host->add_hook($host::HOOK_ARTICLE_FILTER, $this);
38
        $host->add_hook($host::HOOK_PREFS_TAB, $this);
39
        $host->add_hook($host::HOOK_PREFS_EDIT_FEED, $this);
40
        $host->add_hook($host::HOOK_PREFS_SAVE_FEED, $this);
41
42
        // Note: we have to install the hook even if disabled because init() is being run before plugin data has loaded
43
        // so we can't check for our storage-set options here
44
        $host->add_hook($host::HOOK_GET_FULL_TEXT, $this);
45
46
        $host->add_filter_action($this, "action_inline", __("Inline content"));
47
    }
48
49
    public function hook_prefs_tab($args) {
50
        if ($args != "prefFeeds") {
51
            return;
52
        }
53
54
        print "<div dojoType='dijit.layout.AccordionPane'
55
			title=\"<i class='material-icons'>extension</i> ".__('Readability settings (af_readability)')."\">";
56
57
        if (version_compare(PHP_VERSION, '7.0.0', '<')) {
58
            print_error("This plugin requires PHP 7.0.");
0 ignored issues
show
The call to print_error() has too many arguments starting with 'This plugin requires PHP 7.0.'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

58
            /** @scrutinizer ignore-call */ 
59
            print_error("This plugin requires PHP 7.0.");

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Deprecated Code introduced by
The function print_error() has been deprecated: Use twig function errorMessage ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

58
            /** @scrutinizer ignore-deprecated */ print_error("This plugin requires PHP 7.0.");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
59
        } else {
60
61
            print "<h2>".__("Global settings")."</h2>";
62
63
            print_notice("Enable for specific feeds in the feed editor.");
0 ignored issues
show
The call to print_notice() has too many arguments starting with 'Enable for specific feeds in the feed editor.'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

63
            /** @scrutinizer ignore-call */ 
64
            print_notice("Enable for specific feeds in the feed editor.");

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Deprecated Code introduced by
The function print_notice() has been deprecated: Use twig function noticeMessage ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

63
            /** @scrutinizer ignore-deprecated */ print_notice("Enable for specific feeds in the feed editor.");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
64
65
            print "<form dojoType='dijit.form.Form'>";
66
67
            print "<script type='dojo/method' event='onSubmit' args='evt'>
68
			evt.preventDefault();
69
			if (this.validate()) {
70
				console.log(dojo.objectToQuery(this.getValues()));
71
				new Ajax.Request('backend.php', {
72
					parameters: dojo.objectToQuery(this.getValues()),
73
					onComplete: function(transport) {
74
						Notify.info(transport.responseText);
75
					}
76
				});
77
				//this.reset();
78
			}
79
			</script>";
80
81
            print_hidden("op", "pluginhandler");
82
            print_hidden("method", "save");
83
            print_hidden("plugin", "af_readability");
84
85
            $enable_share_anything = $this->host->get($this, "enable_share_anything");
86
87
            print "<fieldset>";
88
            print "<label class='checkbox'> ";
89
            print_checkbox("enable_share_anything", $enable_share_anything);
90
            print " ".__("Provide full-text services to core code (bookmarklets) and other plugins");
91
            print "</label>";
92
            print "</fieldset>";
93
94
            print_button("submit", __("Save"), "class='alt-primary'");
95
            print "</form>";
96
97
            $enabled_feeds = $this->host->get($this, "enabled_feeds");
98
            if (!is_array($enabled_feeds)) {
99
                $enabled_feeds = array();
100
            }
101
102
            $enabled_feeds = $this->filter_unknown_feeds($enabled_feeds);
103
            $this->host->set($this, "enabled_feeds", $enabled_feeds);
104
105
            if (count($enabled_feeds) > 0) {
106
                print "<h3>".__("Currently enabled for (click to edit):")."</h3>";
107
108
                print "<ul class='panel panel-scrollable list list-unstyled'>";
109
                foreach ($enabled_feeds as $f) {
110
                    print "<li><i class='material-icons'>rss_feed</i> <a href='#'
111
						onclick='CommonDialogs.editFeed($f)'>".
112
                        Feeds::getFeedTitle($f)."</a></li>";
113
                }
114
                print "</ul>";
115
            }
116
117
        }
118
119
        print "</div>";
120
    }
121
122
    public function hook_prefs_edit_feed($feed_id) {
123
        print "<header>".__("Readability")."</header>";
124
        print "<section>";
125
126
        $enabled_feeds = $this->host->get($this, "enabled_feeds");
127
        if (!is_array($enabled_feeds)) {
128
            $enabled_feeds = array();
129
        }
130
131
        $key = array_search($feed_id, $enabled_feeds);
132
        $checked = $key !== false ? "checked" : "";
133
134
        print "<fieldset>";
135
136
        print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='af_readability_enabled'
137
			name='af_readability_enabled' $checked>&nbsp;".__('Inline article content')."</label>";
138
139
        print "</fieldset>";
140
141
        print "</section>";
142
    }
143
144
    public function hook_prefs_save_feed($feed_id) {
145
        $enabled_feeds = $this->host->get($this, "enabled_feeds");
146
        if (!is_array($enabled_feeds)) {
147
            $enabled_feeds = array();
148
        }
149
150
        $enable = checkbox_to_sql_bool($_POST["af_readability_enabled"]);
151
        $key = array_search($feed_id, $enabled_feeds);
152
153
        if ($enable) {
154
            if ($key === false) {
155
                array_push($enabled_feeds, $feed_id);
156
            }
157
        } else {
158
            if ($key !== false) {
159
                unset($enabled_feeds[$key]);
160
            }
161
        }
162
163
        $this->host->set($this, "enabled_feeds", $enabled_feeds);
164
    }
165
166
    /**
167
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
168
     */
169
    public function hook_article_filter_action($article, $action) {
0 ignored issues
show
The parameter $action is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

169
    public function hook_article_filter_action($article, /** @scrutinizer ignore-unused */ $action) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
170
        return $this->process_article($article);
171
    }
172
173
    public function extract_content($url) {
174
175
        global $fetch_effective_url;
176
177
        $tmp = fetch_file_contents([
178
            "url" => $url,
179
            "http_accept" => "text/*",
180
            "type" => "text/html"]);
181
182
        if ($tmp && mb_strlen($tmp) < 1024 * 500) {
0 ignored issues
show
It seems like $tmp can also be of type true; however, parameter $str of mb_strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

182
        if ($tmp && mb_strlen(/** @scrutinizer ignore-type */ $tmp) < 1024 * 500) {
Loading history...
183
            $tmpdoc = new DOMDocument("1.0", "UTF-8");
184
185
            if (!@$tmpdoc->loadHTML($tmp)) {
0 ignored issues
show
It seems like $tmp can also be of type true; however, parameter $source of DOMDocument::loadHTML() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

185
            if (!@$tmpdoc->loadHTML(/** @scrutinizer ignore-type */ $tmp)) {
Loading history...
186
                            return false;
187
            }
188
189
            // this is the worst hack yet :(
190
            if (strtolower($tmpdoc->encoding) != 'utf-8') {
191
                $tmp = preg_replace("/<meta.*?charset.*?\/?>/i", "", $tmp);
192
                if (empty($tmpdoc->encoding)) {
193
                    $tmp = mb_convert_encoding($tmp, 'utf-8');
194
                } else {
195
                    $tmp = mb_convert_encoding($tmp, 'utf-8', $tmpdoc->encoding);
196
                }
197
            }
198
199
            try {
200
                $r = new Readability(new Configuration());
201
202
                if ($r->parse($tmp)) {
203
204
                    $tmpxpath = new DOMXPath($r->getDOMDOcument());
205
                    $entries = $tmpxpath->query('(//a[@href]|//img[@src])');
206
207
                    foreach ($entries as $entry) {
208
                        if ($entry->hasAttribute("href")) {
209
                            $entry->setAttribute("href",
210
                                    rewrite_relative_url($fetch_effective_url, $entry->getAttribute("href")));
211
212
                        }
213
214
                        if ($entry->hasAttribute("src")) {
215
                            $entry->setAttribute("src",
216
                                    rewrite_relative_url($fetch_effective_url, $entry->getAttribute("src")));
217
218
                        }
219
                    }
220
221
                    return $r->getContent();
222
                }
223
224
            } catch (Exception $e) {
225
                return false;
226
            }
227
        }
228
229
        return false;
230
    }
231
232
    public function process_article($article) {
233
234
        $extracted_content = $this->extract_content($article["link"]);
235
236
        # let's see if there's anything of value in there
237
        $content_test = trim(strip_tags(sanitize($extracted_content)));
238
239
        if ($content_test) {
240
            $article["content"] = $extracted_content;
241
        }
242
243
        return $article;
244
    }
245
246
    public function hook_article_filter($article) {
247
248
        $enabled_feeds = $this->host->get($this, "enabled_feeds");
249
        if (!is_array($enabled_feeds)) {
250
            return $article;
251
        }
252
253
        $key = array_search($article["feed"]["id"], $enabled_feeds);
254
        if ($key === false) {
255
            return $article;
256
        }
257
258
        return $this->process_article($article);
259
260
    }
261
262
    public function hook_get_full_text($link)
263
    {
264
        $enable_share_anything = $this->host->get($this, "enable_share_anything");
265
266
        if ($enable_share_anything) {
267
            $extracted_content = $this->extract_content($link);
268
269
            # let's see if there's anything of value in there
270
            $content_test = trim(strip_tags(sanitize($extracted_content)));
271
272
            if ($content_test) {
273
                return $extracted_content;
274
            }
275
        }
276
277
        return false;
278
    }
279
280
    public function api_version() {
281
        return 2;
282
    }
283
284
    private function filter_unknown_feeds($enabled_feeds) {
285
        $tmp = array();
286
287
        foreach ($enabled_feeds as $feed) {
288
289
            $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? AND owner_uid = ?");
290
            $sth->execute([$feed, $_SESSION['uid']]);
291
292
            if ($row = $sth->fetch()) {
0 ignored issues
show
The assignment to $row is dead and can be removed.
Loading history...
293
                array_push($tmp, $feed);
294
            }
295
        }
296
297
        return $tmp;
298
    }
299
300
}
301