Pref_Prefs::toggleAdvanced()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 2
rs 10
1
<?php
2
3
class Pref_Prefs extends Handler_Protected {
4
5
    private $pref_help = [];
6
    private $pref_item_map = [];
7
    private $pref_blacklist = [];
8
    private $profile_blacklist = [];
9
10
    public function csrf_ignore($method) {
11
        $csrf_ignored = array("index", "updateself", "customizecss", "editprefprofiles");
12
13
        return array_search($method, $csrf_ignored) !== false;
14
    }
15
16
    public function __construct($args) {
17
        parent::__construct($args);
18
19
        $this->pref_item_map = [
20
            __('General') => [
21
                'USER_LANGUAGE',
22
                'USER_TIMEZONE',
23
                'BLOCK_SEPARATOR',
24
                'USER_CSS_THEME',
25
                'BLOCK_SEPARATOR',
26
                'ENABLE_API_ACCESS',
27
            ],
28
            __('Feeds') => [
29
                'DEFAULT_UPDATE_INTERVAL',
30
                'FRESH_ARTICLE_MAX_AGE',
31
                'DEFAULT_SEARCH_LANGUAGE',
32
                'BLOCK_SEPARATOR',
33
                'ENABLE_FEED_CATS',
34
                'BLOCK_SEPARATOR',
35
                'CONFIRM_FEED_CATCHUP',
36
                'ON_CATCHUP_SHOW_NEXT_FEED',
37
                'BLOCK_SEPARATOR',
38
                'HIDE_READ_FEEDS',
39
                'HIDE_READ_SHOWS_SPECIAL',
40
            ],
41
            __('Articles') => [
42
                'PURGE_OLD_DAYS',
43
                'PURGE_UNREAD_ARTICLES',
44
                'BLOCK_SEPARATOR',
45
                'COMBINED_DISPLAY_MODE',
46
                'CDM_EXPANDED',
47
                'BLOCK_SEPARATOR',
48
                'CDM_AUTO_CATCHUP',
49
                'VFEED_GROUP_BY_FEED',
50
                'BLOCK_SEPARATOR',
51
                'SHOW_CONTENT_PREVIEW',
52
                'STRIP_IMAGES',
53
            ],
54
            __('Digest') => [
55
                'DIGEST_ENABLE',
56
                'DIGEST_CATCHUP',
57
                'DIGEST_PREFERRED_TIME',
58
            ],
59
            __('Advanced') => [
60
                'BLACKLISTED_TAGS',
61
                'BLOCK_SEPARATOR',
62
                'LONG_DATE_FORMAT',
63
                'SHORT_DATE_FORMAT',
64
                'BLOCK_SEPARATOR',
65
                'SSL_CERT_SERIAL',
66
            ]
67
        ];
68
69
        $this->pref_help = [
70
            "ALLOW_DUPLICATE_POSTS" => array(__("Allow duplicate articles"), ""),
71
            "BLACKLISTED_TAGS" => array(__("Blacklisted tags"), __("Never apply these tags automatically (comma-separated list).")),
72
            "DEFAULT_SEARCH_LANGUAGE" => array(__("Default language"), __("Used for full-text search")),
73
            "CDM_AUTO_CATCHUP" => array(__("Mark read on scroll"), __("Mark articles as read as you scroll past them")),
74
            "CDM_EXPANDED" => array(__("Always expand articles")),
75
            "COMBINED_DISPLAY_MODE" => array(__("Combined mode"), __("Show flat list of articles instead of separate panels")),
76
            "CONFIRM_FEED_CATCHUP" => array(__("Confirm marking feeds as read")),
77
            "DEFAULT_ARTICLE_LIMIT" => array(__("Amount of articles to display at once")),
78
            "DEFAULT_UPDATE_INTERVAL" => array(__("Default update interval")),
79
            "DIGEST_CATCHUP" => array(__("Mark sent articles as read")),
80
            "DIGEST_ENABLE" => array(__("Enable digest"), __("Send daily digest of new (and unread) headlines to your e-mail address")),
81
            "DIGEST_PREFERRED_TIME" => array(__("Try to send around this time"), __("Time in UTC")),
82
            "ENABLE_API_ACCESS" => array(__("Enable API"), __("Allows accessing this account through the API")),
83
            "ENABLE_FEED_CATS" => array(__("Enable categories")),
84
            "FEEDS_SORT_BY_UNREAD" => array(__("Sort feeds by unread articles count"), ""),
85
            "FRESH_ARTICLE_MAX_AGE" => array(__("Maximum age of fresh articles"), "<strong>".__("hours")."</strong>"),
86
            "HIDE_READ_FEEDS" => array(__("Hide read feeds")),
87
            "HIDE_READ_SHOWS_SPECIAL" => array(__("Always show special feeds"), __("While hiding read feeds")),
88
            "LONG_DATE_FORMAT" => array(__("Long date format"), __("Syntax is identical to PHP <a href='http://php.net/manual/function.date.php'>date()</a> function.")),
89
            "ON_CATCHUP_SHOW_NEXT_FEED" => array(__("Automatically show next feed"), __("After marking one as read")),
90
            "PURGE_OLD_DAYS" => array(__("Purge articles older than"), __("<strong>days</strong> (0 disables)")),
91
            "PURGE_UNREAD_ARTICLES" => array(__("Purge unread articles")),
92
            "REVERSE_HEADLINES" => array(__("Reverse headline order (oldest first)")),
93
            "SHORT_DATE_FORMAT" => array(__("Short date format")),
94
            "SHOW_CONTENT_PREVIEW" => array(__("Show content preview in headlines")),
95
            "SORT_HEADLINES_BY_FEED_DATE" => array(__("Sort headlines by feed date"), __("Use feed-specified date to sort headlines instead of local import date.")),
96
            "SSL_CERT_SERIAL" => array(__("SSL client certificate")),
97
            "STRIP_IMAGES" => array(__("Do not embed media")),
98
            "STRIP_UNSAFE_TAGS" => array(__("Strip unsafe tags from articles"), __("Strip all but most common HTML tags when reading articles.")),
99
            "USER_STYLESHEET" => array(__("Customize stylesheet")),
100
            "USER_TIMEZONE" => array(__("Time zone")),
101
            "VFEED_GROUP_BY_FEED" => array(__("Group by feed"), __("Group multiple-feed output by originating feed")),
102
            "USER_LANGUAGE" => array(__("Language")),
103
            "USER_CSS_THEME" => array(__("Theme"))
104
        ];
105
106
        $this->pref_blacklist = ["ALLOW_DUPLICATE_POSTS", "STRIP_UNSAFE_TAGS", "REVERSE_HEADLINES",
107
            "SORT_HEADLINES_BY_FEED_DATE", "DEFAULT_ARTICLE_LIMIT",
108
            "FEEDS_SORT_BY_UNREAD", "USER_STYLESHEET"];
109
110
        /* "FEEDS_SORT_BY_UNREAD", "HIDE_READ_FEEDS", "REVERSE_HEADLINES" */
111
112
        $this->profile_blacklist = ["ALLOW_DUPLICATE_POSTS", "PURGE_OLD_DAYS",
113
            "PURGE_UNREAD_ARTICLES", "DIGEST_ENABLE", "DIGEST_CATCHUP",
114
            "BLACKLISTED_TAGS", "ENABLE_API_ACCESS", "UPDATE_POST_ON_CHECKSUM_CHANGE",
115
            "DEFAULT_UPDATE_INTERVAL", "USER_TIMEZONE", "SORT_HEADLINES_BY_FEED_DATE",
116
            "SSL_CERT_SERIAL", "DIGEST_PREFERRED_TIME"];
117
    }
118
119
    public function changepassword() {
120
121
        if (defined('_TTRSS_DEMO_INSTANCE')) {
122
            print "ERROR: ".format_error("Disabled in demo version.");
0 ignored issues
show
Unused Code introduced by
The call to format_error() has too many arguments starting with 'Disabled in demo version.'. ( Ignorable by Annotation )

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

122
            print "ERROR: "./** @scrutinizer ignore-call */ format_error("Disabled in demo version.");

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 format_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

122
            print "ERROR: "./** @scrutinizer ignore-deprecated */ format_error("Disabled in demo version.");

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...
Bug introduced by
Are you sure the usage of format_error('Disabled in demo version.') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
123
            return;
124
        }
125
126
        $old_pw = clean($_POST["old_password"]);
127
        $new_pw = clean($_POST["new_password"]);
128
        $con_pw = clean($_POST["confirm_password"]);
129
130
        if ($old_pw == $new_pw) {
131
            print "ERROR: ".format_error("New password must be different from the old one.");
0 ignored issues
show
Bug introduced by
Are you sure the usage of format_error('New passwo...ent from the old one.') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Deprecated Code introduced by
The function format_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

131
            print "ERROR: "./** @scrutinizer ignore-deprecated */ format_error("New password must be different from the old one.");

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...
132
            return;
133
        }
134
135
        if ($old_pw == "") {
136
            print "ERROR: ".format_error("Old password cannot be blank.");
0 ignored issues
show
Deprecated Code introduced by
The function format_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

136
            print "ERROR: "./** @scrutinizer ignore-deprecated */ format_error("Old password cannot be blank.");

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...
Bug introduced by
Are you sure the usage of format_error('Old password cannot be blank.') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
137
            return;
138
        }
139
140
        if ($new_pw == "") {
141
            print "ERROR: ".format_error("New password cannot be blank.");
0 ignored issues
show
Deprecated Code introduced by
The function format_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

141
            print "ERROR: "./** @scrutinizer ignore-deprecated */ format_error("New password cannot be blank.");

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...
Bug introduced by
Are you sure the usage of format_error('New password cannot be blank.') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
142
            return;
143
        }
144
145
        if ($new_pw != $con_pw) {
146
            print "ERROR: ".format_error("Entered passwords do not match.");
0 ignored issues
show
Bug introduced by
Are you sure the usage of format_error('Entered passwords do not match.') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Deprecated Code introduced by
The function format_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

146
            print "ERROR: "./** @scrutinizer ignore-deprecated */ format_error("Entered passwords do not match.");

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...
147
            return;
148
        }
149
150
        $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]);
151
152
        if (method_exists($authenticator, "change_password")) {
153
            print format_notice($authenticator->change_password($_SESSION["uid"], $old_pw, $new_pw));
0 ignored issues
show
Unused Code introduced by
The call to format_notice() has too many arguments starting with $authenticator->change_p...id'], $old_pw, $new_pw). ( Ignorable by Annotation )

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

153
            print /** @scrutinizer ignore-call */ format_notice($authenticator->change_password($_SESSION["uid"], $old_pw, $new_pw));

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 format_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

153
            print /** @scrutinizer ignore-deprecated */ format_notice($authenticator->change_password($_SESSION["uid"], $old_pw, $new_pw));

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...
Bug introduced by
Are you sure the usage of format_notice($authentic...d'], $old_pw, $new_pw)) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
154
        } else {
155
            print "ERROR: ".format_error("Function not supported by authentication module.");
0 ignored issues
show
Deprecated Code introduced by
The function format_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

155
            print "ERROR: "./** @scrutinizer ignore-deprecated */ format_error("Function not supported by authentication module.");

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...
Bug introduced by
Are you sure the usage of format_error('Function n...uthentication module.') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
156
        }
157
    }
158
159
    public function saveconfig() {
160
        $boolean_prefs = explode(",", clean($_POST["boolean_prefs"]));
0 ignored issues
show
Bug introduced by
It seems like clean($_POST['boolean_prefs']) can also be of type array; however, parameter $string of explode() 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

160
        $boolean_prefs = explode(",", /** @scrutinizer ignore-type */ clean($_POST["boolean_prefs"]));
Loading history...
161
162
        foreach ($boolean_prefs as $pref) {
163
            if (!isset($_POST[$pref])) {
164
                $_POST[$pref] = 'false';
165
            }
166
        }
167
168
        $need_reload = false;
169
170
        foreach (array_keys($_POST) as $pref_name) {
171
172
            $value = $_POST[$pref_name];
173
174
            switch ($pref_name) {
175
            case 'DIGEST_PREFERRED_TIME':
176
                if (get_pref('DIGEST_PREFERRED_TIME') != $value) {
177
178
                    $sth = $this->pdo->prepare("UPDATE ttrss_users SET
179
						last_digest_sent = NULL WHERE id = ?");
180
                    $sth->execute([$_SESSION['uid']]);
181
182
                }
183
                break;
184
            case 'USER_LANGUAGE':
185
                if (!$need_reload) {
186
                    $need_reload = $_SESSION["language"] != $value;
187
                }
188
                break;
189
190
            case 'USER_CSS_THEME':
191
                if (!$need_reload) {
192
                    $need_reload = get_pref($pref_name) != $value;
193
                }
194
                break;
195
            }
196
197
            set_pref($pref_name, $value);
198
        }
199
200
        if ($need_reload) {
0 ignored issues
show
introduced by
The condition $need_reload is always false.
Loading history...
201
            print "PREFS_NEED_RELOAD";
202
        } else {
203
            print __("The configuration was saved.");
204
        }
205
    }
206
207
    public function changeemail() {
208
209
        $email = clean($_POST["email"]);
210
        $full_name = clean($_POST["full_name"]);
211
        $active_uid = $_SESSION["uid"];
212
213
        $sth = $this->pdo->prepare("SELECT email, login, full_name FROM ttrss_users WHERE id = ?");
214
        $sth->execute([$active_uid]);
215
216
        if ($row = $sth->fetch()) {
217
            $old_email = $row["email"];
218
219
            if ($old_email != $email) {
220
                $mailer = new Mailer();
221
222
                require_once "lib/MiniTemplator.class.php";
223
224
                $tpl = new MiniTemplator;
225
226
                $tpl->readTemplateFromFile("templates/mail_change_template.txt");
227
228
                $tpl->setVariable('LOGIN', $row["login"]);
229
                $tpl->setVariable('NEWMAIL', $email);
0 ignored issues
show
Bug introduced by
It seems like $email can also be of type array; however, parameter $variableValue of MiniTemplator::setVariable() 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

229
                $tpl->setVariable('NEWMAIL', /** @scrutinizer ignore-type */ $email);
Loading history...
230
                $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH);
0 ignored issues
show
Bug introduced by
The constant SELF_URL_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
231
232
                $tpl->addBlock('message');
233
234
                $tpl->generateOutputToString($message);
235
236
                $mailer->mail(["to_name" => $row["login"],
237
                    "to_address" => $row["email"],
238
                    "subject" => "[tt-rss] Mail address change notification",
239
                    "message" => $message]);
240
241
            }
242
        }
243
244
        $sth = $this->pdo->prepare("UPDATE ttrss_users SET email = ?,
245
			full_name = ? WHERE id = ?");
246
        $sth->execute([$email, $full_name, $active_uid]);
247
248
        print __("Your personal data has been saved.");
249
250
        return;
251
    }
252
253
    public function resetconfig() {
254
255
        $_SESSION["prefs_op_result"] = "reset-to-defaults";
256
257
        $sth = $this->pdo->prepare("DELETE FROM ttrss_user_prefs
258
			WHERE (profile = :profile OR (:profile IS NULL AND profile IS NULL))
259
				AND owner_uid = :uid");
260
        $sth->execute([":profile" => $_SESSION['profile'], ":uid" => $_SESSION['uid']]);
261
262
        initialize_user_prefs($_SESSION["uid"], $_SESSION["profile"]);
263
264
        echo __("Your preferences are now set to default values.");
265
    }
266
267
    public function index() {
268
269
        global $access_level_names;
270
271
        $_SESSION["prefs_op_result"] = "";
272
273
        print "<div dojoType='dijit.layout.AccordionContainer' region='center'>";
274
        print "<div dojoType='dijit.layout.AccordionPane'
275
			title=\"<i class='material-icons'>person</i> ".__('Personal data / Authentication')."\">";
276
277
        print "<div dojoType='dijit.layout.TabContainer'>";
278
        print "<div dojoType='dijit.layout.ContentPane' title=\"".__('Personal data')."\">";
279
280
        print "<form dojoType='dijit.form.Form' id='changeUserdataForm'>";
281
282
        print "<script type='dojo/method' event='onSubmit' args='evt'>
283
		evt.preventDefault();
284
		if (this.validate()) {
285
			Notify.progress('Saving data...', true);
286
287
			new Ajax.Request('backend.php', {
288
				parameters: dojo.objectToQuery(this.getValues()),
289
				onComplete: function(transport) {
290
					notify_callback2(transport);
291
			} });
292
293
		}
294
		</script>";
295
296
        $sth = $this->pdo->prepare("SELECT email,full_name,otp_enabled,
297
			access_level FROM ttrss_users
298
			WHERE id = ?");
299
        $sth->execute([$_SESSION["uid"]]);
300
        $row = $sth->fetch();
301
302
        $email = htmlspecialchars($row["email"]);
303
        $full_name = htmlspecialchars($row["full_name"]);
304
        $otp_enabled = sql_bool_to_bool($row["otp_enabled"]);
305
306
        print "<fieldset>";
307
        print "<label>".__('Full name:')."</label>";
308
        print "<input dojoType='dijit.form.ValidationTextBox' name='full_name' required='1' value='$full_name'>";
309
        print "</fieldset>";
310
311
        print "<fieldset>";
312
        print "<label>".__('E-mail:')."</label>";
313
        print "<input dojoType='dijit.form.ValidationTextBox' name='email' required='1' value='$email'>";
314
        print "</fieldset>";
315
316
        if (!SINGLE_USER_MODE && !$_SESSION["hide_hello"]) {
0 ignored issues
show
Bug introduced by
The constant SINGLE_USER_MODE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
317
318
            $access_level = $row["access_level"];
319
            print "<fieldset>";
320
            print "<label>".__('Access level:')."</label>";
321
            print $access_level_names[$access_level];
322
            print "</fieldset>";
323
        }
324
325
        print_hidden("op", "pref-prefs");
326
        print_hidden("method", "changeemail");
327
328
        print "<hr/>";
329
330
        print "<button dojoType='dijit.form.Button' type='submit' class='alt-primary'>".
331
            __("Save data")."</button>";
332
333
        print "</form>";
334
335
        print "</div>"; # content pane
336
        print "<div dojoType='dijit.layout.ContentPane' title=\"".__('Password')."\">";
337
338
        if ($_SESSION["auth_module"]) {
339
            $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]);
340
        } else {
341
            $authenticator = false;
342
        }
343
344
        if ($authenticator && method_exists($authenticator, "change_password")) {
345
346
            print "<div style='display : none' id='pwd_change_infobox'></div>";
347
348
            print "<form dojoType='dijit.form.Form'>";
349
350
            print "<script type='dojo/method' event='onSubmit' args='evt'>
351
			evt.preventDefault();
352
			if (this.validate()) {
353
				Notify.progress('Changing password...', true);
354
355
				new Ajax.Request('backend.php', {
356
					parameters: dojo.objectToQuery(this.getValues()),
357
					onComplete: function(transport) {
358
						Notify.close();
359
						if (transport.responseText.indexOf('ERROR: ') == 0) {
360
361
							$('pwd_change_infobox').innerHTML =
362
								transport.responseText.replace('ERROR: ', '');
363
364
						} else {
365
							$('pwd_change_infobox').innerHTML =
366
								transport.responseText.replace('ERROR: ', '');
367
368
							var warn = $('default_pass_warning');
369
							if (warn) Element.hide(warn);
370
						}
371
372
						new Effect.Appear('pwd_change_infobox');
373
374
				}});
375
				this.reset();
376
			}
377
			</script>";
378
379
            if ($otp_enabled) {
380
                print_notice(__("Changing your current password will disable OTP."));
0 ignored issues
show
Unused Code introduced by
The call to print_notice() has too many arguments starting with __('Changing your curren...ord will disable OTP.'). ( Ignorable by Annotation )

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

380
                /** @scrutinizer ignore-call */ 
381
                print_notice(__("Changing your current password will disable OTP."));

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

380
                /** @scrutinizer ignore-deprecated */ print_notice(__("Changing your current password will disable OTP."));

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...
381
            }
382
383
            print "<fieldset>";
384
            print "<label>".__("Old password:")."</label>";
385
            print "<input dojoType='dijit.form.ValidationTextBox' type='password' required='1' name='old_password'>";
386
            print "</fieldset>";
387
388
            print "<fieldset>";
389
            print "<label>".__("New password:")."</label>";
390
            print "<input dojoType='dijit.form.ValidationTextBox' type='password' required='1' name='new_password'>";
391
            print "</fieldset>";
392
393
            print "<fieldset>";
394
            print "<label>".__("Confirm password:")."</label>";
395
            print "<input dojoType='dijit.form.ValidationTextBox' type='password' required='1' name='confirm_password'>";
396
            print "</fieldset>";
397
398
            print_hidden("op", "pref-prefs");
399
            print_hidden("method", "changepassword");
400
401
            print "<hr/>";
402
403
            print "<button dojoType='dijit.form.Button' type='submit' class='alt-primary'>".
404
                __("Change password")."</button>";
405
406
            print "</form>";
407
408
            print "</div>"; # content pane
409
410
            if ($_SESSION["auth_module"] == "auth_internal") {
411
412
                print "<div dojoType='dijit.layout.ContentPane' title=\"".__('App passwords')."\">";
413
414
                print_notice("You can create separate passwords for API clients. Using one is required if you enable OTP.");
0 ignored issues
show
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

414
                /** @scrutinizer ignore-deprecated */ print_notice("You can create separate passwords for API clients. Using one is required if you enable OTP.");

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...
415
416
                print "<div id='app_passwords_holder'>";
417
                $this->appPasswordList();
418
                print "</div>";
419
420
                print "<hr>";
421
422
                print "<button style='float : left' class='alt-primary' dojoType='dijit.form.Button'
423
					onclick=\"Helpers.AppPasswords.generate()\">" .
424
                    __('Generate new password')."</button> ";
425
426
                print "<button style='float : left' class='alt-danger' dojoType='dijit.form.Button'
427
					onclick=\"Helpers.AppPasswords.removeSelected()\">" .
428
                    __('Remove selected passwords')."</button>";
429
430
                print "</div>"; # content pane
431
            }
432
433
            print "<div dojoType='dijit.layout.ContentPane' title=\"".__('One time passwords / Authenticator')."\">";
434
435
            if ($_SESSION["auth_module"] == "auth_internal") {
436
437
                if ($otp_enabled) {
438
439
                    print_warning("One time passwords are currently enabled. Enter your current password below to disable.");
0 ignored issues
show
Unused Code introduced by
The call to print_warning() has too many arguments starting with 'One time passwords are ...word below to disable.'. ( Ignorable by Annotation )

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

439
                    /** @scrutinizer ignore-call */ 
440
                    print_warning("One time passwords are currently enabled. Enter your current password below to disable.");

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_warning() has been deprecated: Use twig function warningMessage ( Ignorable by Annotation )

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

439
                    /** @scrutinizer ignore-deprecated */ print_warning("One time passwords are currently enabled. Enter your current password below to disable.");

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...
440
441
                    print "<form dojoType='dijit.form.Form'>";
442
443
                    print "<script type='dojo/method' event='onSubmit' args='evt'>
444
					evt.preventDefault();
445
					if (this.validate()) {
446
						Notify.progress('Disabling OTP', true);
447
448
						new Ajax.Request('backend.php', {
449
							parameters: dojo.objectToQuery(this.getValues()),
450
							onComplete: function(transport) {
451
								Notify.close();
452
								if (transport.responseText.indexOf('ERROR: ') == 0) {
453
									Notify.error(transport.responseText.replace('ERROR: ', ''));
454
								} else {
455
									window.location.reload();
456
								}
457
						}});
458
						this.reset();
459
					}
460
					</script>";
461
462
                    print "<fieldset>";
463
                    print "<label>".__("Your password:")."</label>";
464
                    print "<input dojoType='dijit.form.ValidationTextBox' type='password' required='1' name='password'>";
465
                    print "</fieldset>";
466
467
                    print_hidden("op", "pref-prefs");
468
                    print_hidden("method", "otpdisable");
469
470
                    print "<hr/>";
471
472
                    print "<button dojoType='dijit.form.Button' type='submit'>".
473
                        __("Disable OTP")."</button>";
474
475
                    print "</form>";
476
477
                } else {
478
479
                    print_warning("You will need a compatible Authenticator to use this. Changing your password would automatically disable OTP.");
0 ignored issues
show
Deprecated Code introduced by
The function print_warning() has been deprecated: Use twig function warningMessage ( Ignorable by Annotation )

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

479
                    /** @scrutinizer ignore-deprecated */ print_warning("You will need a compatible Authenticator to use this. Changing your password would automatically disable OTP.");

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...
480
                    print_notice("You will need to generate app passwords for the API clients if you enable OTP.");
0 ignored issues
show
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

480
                    /** @scrutinizer ignore-deprecated */ print_notice("You will need to generate app passwords for the API clients if you enable OTP.");

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...
481
482
                    if (function_exists("imagecreatefromstring")) {
483
                        print "<h3>".__("Scan the following code by the Authenticator application or copy the key manually")."</h3>";
484
485
                        $csrf_token = $_SESSION["csrf_token"];
486
                        print "<img alt='otp qr-code' src='backend.php?op=pref-prefs&method=otpqrcode&csrf_token=$csrf_token'>";
487
                    } else {
488
                        print_error("PHP GD functions are required to generate QR codes.");
0 ignored issues
show
Unused Code introduced by
The call to print_error() has too many arguments starting with 'PHP GD functions are re... to generate QR codes.'. ( Ignorable by Annotation )

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

488
                        /** @scrutinizer ignore-call */ 
489
                        print_error("PHP GD functions are required to generate QR codes.");

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

488
                        /** @scrutinizer ignore-deprecated */ print_error("PHP GD functions are required to generate QR codes.");

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...
489
                        print "<h3>".__("Use the following OTP key with a compatible Authenticator application")."</h3>";
490
                    }
491
492
                    print "<form dojoType='dijit.form.Form' id='changeOtpForm'>";
493
494
                    $otp_secret = $this->otpsecret();
495
496
                    print "<fieldset>";
497
                    print "<label>".__("OTP Key:")."</label>";
498
                    print "<input dojoType='dijit.form.ValidationTextBox' disabled='disabled' value='$otp_secret' size='32'>";
499
                    print "</fieldset>";
500
501
                    print_hidden("op", "pref-prefs");
502
                    print_hidden("method", "otpenable");
503
504
                    print "<script type='dojo/method' event='onSubmit' args='evt'>
505
					evt.preventDefault();
506
					if (this.validate()) {
507
						Notify.progress('Saving data...', true);
508
509
						new Ajax.Request('backend.php', {
510
							parameters: dojo.objectToQuery(this.getValues()),
511
							onComplete: function(transport) {
512
								Notify.close();
513
								if (transport.responseText.indexOf('ERROR:') == 0) {
514
									Notify.error(transport.responseText.replace('ERROR:', ''));
515
								} else {
516
									window.location.reload();
517
								}
518
						} });
519
520
					}
521
					</script>";
522
523
                    print "<fieldset>";
524
                    print "<label>".__("Your password:")."</label>";
525
                    print "<input dojoType='dijit.form.ValidationTextBox' type='password' required='1'
526
						name='password'>";
527
                    print "</fieldset>";
528
529
                    print "<fieldset>";
530
                    print "<label>".__("One time password:")."</label>";
531
                    print "<input dojoType='dijit.form.ValidationTextBox' autocomplete='off'
532
						required='1' name='otp'>";
533
                    print "</fieldset>";
534
535
                    print "<hr/>";
536
                    print "<button dojoType='dijit.form.Button' type='submit' class='alt-primary'>".
537
                        __("Enable OTP")."</button>";
538
539
                    print "</form>";
540
541
                }
542
            }
543
544
            print "</div>"; # content pane
545
            print "</div>"; # tab container
546
547
        }
548
549
        PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION,
550
            "hook_prefs_tab_section", "prefPrefsAuth");
551
552
        print "</div>"; #pane
553
554
        print "<div dojoType='dijit.layout.AccordionPane' selected='true'
555
			title=\"<i class='material-icons'>settings</i> ".__('Preferences')."\">";
556
557
        print "<form dojoType='dijit.form.Form' id='changeSettingsForm'>";
558
559
        print "<script type='dojo/method' event='onSubmit' args='evt, quit'>
560
		if (evt) evt.preventDefault();
561
		if (this.validate()) {
562
			console.log(dojo.objectToQuery(this.getValues()));
563
564
			new Ajax.Request('backend.php', {
565
				parameters: dojo.objectToQuery(this.getValues()),
566
				onComplete: function(transport) {
567
					var msg = transport.responseText;
568
					if (quit) {
569
						document.location.href = 'index.php';
570
					} else {
571
						if (msg == 'PREFS_NEED_RELOAD') {
572
							window.location.reload();
573
						} else {
574
							Notify.info(msg);
575
						}
576
					}
577
			} });
578
		}
579
		</script>";
580
581
        print '<div dojoType="dijit.layout.BorderContainer" gutters="false">';
582
583
        print '<div dojoType="dijit.layout.ContentPane" region="center" style="overflow-y : auto">';
584
585
        $profile = $_SESSION["profile"];
586
587
        if ($profile) {
588
            print_notice(__("Some preferences are only available in default profile."));
0 ignored issues
show
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

588
            /** @scrutinizer ignore-deprecated */ print_notice(__("Some preferences are only available in default profile."));

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...
589
590
            initialize_user_prefs($_SESSION["uid"], $profile);
591
        } else {
592
            initialize_user_prefs($_SESSION["uid"]);
593
        }
594
595
        $prefs_available = [];
596
597
        $sth = $this->pdo->prepare("SELECT DISTINCT
598
			ttrss_user_prefs.pref_name,value,type_name,
599
			ttrss_prefs_sections.order_id,
600
			def_value,section_id
601
			FROM ttrss_prefs,ttrss_prefs_types,ttrss_prefs_sections,ttrss_user_prefs
602
			WHERE type_id = ttrss_prefs_types.id AND
603
				(profile = :profile OR (:profile IS NULL AND profile IS NULL)) AND
604
				section_id = ttrss_prefs_sections.id AND
605
				ttrss_user_prefs.pref_name = ttrss_prefs.pref_name AND
606
				owner_uid = :uid
607
			ORDER BY ttrss_prefs_sections.order_id,pref_name");
608
        $sth->execute([":uid" => $_SESSION['uid'], ":profile" => $profile]);
609
610
        $listed_boolean_prefs = [];
611
612
        while ($line = $sth->fetch()) {
613
614
            if (in_array($line["pref_name"], $this->pref_blacklist)) {
615
                continue;
616
            }
617
618
            if ($profile && in_array($line["pref_name"], $this->profile_blacklist)) {
619
                continue;
620
            }
621
622
            $pref_name = $line["pref_name"];
623
            $short_desc = $this->getShortDesc($pref_name);
624
625
            if (!$short_desc) {
626
                            continue;
627
            }
628
629
            $prefs_available[$pref_name] = [
630
                'type_name' => $line["type_name"],
631
                'value' => $line['value'],
632
                'help_text' => $this->getHelpText($pref_name),
633
                'short_desc' => $short_desc
634
            ];
635
        }
636
637
        foreach (array_keys($this->pref_item_map) as $section) {
638
639
            print "<h2>$section</h2>";
640
641
            foreach ($this->pref_item_map[$section] as $pref_name) {
642
643
                if ($pref_name == 'BLOCK_SEPARATOR' && !$profile) {
644
                    print "<hr/>";
645
                    continue;
646
                }
647
648
                if ($pref_name == "DEFAULT_SEARCH_LANGUAGE" && DB_TYPE != "pgsql") {
0 ignored issues
show
Bug introduced by
The constant DB_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
649
                    continue;
650
                }
651
652
                if ($item = $prefs_available[$pref_name]) {
653
654
                    print "<fieldset class='prefs'>";
655
656
                    print "<label for='CB_$pref_name'>";
657
                    print $item['short_desc'].":";
658
                    print "</label>";
659
660
                    $value = $item['value'];
661
                    $type_name = $item['type_name'];
662
663
                    if ($pref_name == "USER_LANGUAGE") {
664
                        print_select_hash($pref_name, $value, get_translations(),
665
                            "style='width : 220px; margin : 0px' dojoType='fox.form.Select'");
666
667
                    } else if ($pref_name == "USER_TIMEZONE") {
668
669
                        $timezones = explode("\n", file_get_contents("lib/timezones.txt"));
670
671
                        print_select($pref_name, $value, $timezones, 'dojoType="dijit.form.FilteringSelect"');
672
                    } else if ($pref_name == "USER_CSS_THEME") {
673
674
                        $themes = array_merge(glob("themes/*.php"), glob("themes/*.css"), glob("themes.local/*.css"));
0 ignored issues
show
Bug introduced by
It seems like glob('themes.local/*.css') can also be of type false; however, parameter $_ of array_merge() does only seem to accept array, 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

674
                        $themes = array_merge(glob("themes/*.php"), glob("themes/*.css"), /** @scrutinizer ignore-type */ glob("themes.local/*.css"));
Loading history...
Bug introduced by
It seems like glob('themes/*.php') can also be of type false; however, parameter $array1 of array_merge() does only seem to accept array, 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

674
                        $themes = array_merge(/** @scrutinizer ignore-type */ glob("themes/*.php"), glob("themes/*.css"), glob("themes.local/*.css"));
Loading history...
Bug introduced by
It seems like glob('themes/*.css') can also be of type false; however, parameter $array2 of array_merge() does only seem to accept array|null, 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

674
                        $themes = array_merge(glob("themes/*.php"), /** @scrutinizer ignore-type */ glob("themes/*.css"), glob("themes.local/*.css"));
Loading history...
675
                        $themes = array_map("basename", $themes);
676
                        $themes = array_filter($themes, "theme_exists");
677
                        asort($themes);
678
679
                        if (!theme_exists($value)) {
680
                            $value = "default.php";
681
                        }
682
683
                        print "<select name='$pref_name' id='$pref_name' dojoType='fox.form.Select'>";
684
685
                        $issel = $value == "default.php" ? "selected='selected'" : "";
686
                        print "<option $issel value='default.php'>".__("default")."</option>";
687
688
                        foreach ($themes as $theme) {
689
                            $issel = $value == $theme ? "selected='selected'" : "";
690
                            print "<option $issel value='$theme'>$theme</option>";
691
                        }
692
693
                        print "</select>";
694
695
                        print " <button dojoType=\"dijit.form.Button\" class='alt-info'
696
							onclick=\"Helpers.customizeCSS()\">" . __('Customize')."</button>";
697
698
                        print " <button dojoType='dijit.form.Button' onclick='window.open(\"https://tt-rss.org/wiki/Themes\")'>
699
							<i class='material-icons'>open_in_new</i> ".__("More themes...")."</button>";
700
701
                    } else if ($pref_name == "DEFAULT_UPDATE_INTERVAL") {
702
703
                        global $update_intervals_nodefault;
704
705
                        print_select_hash($pref_name, $value, $update_intervals_nodefault,
706
                            'dojoType="fox.form.Select"');
707
                    } else if ($pref_name == "DEFAULT_SEARCH_LANGUAGE") {
708
709
                        print_select($pref_name, $value, Pref_Feeds::get_ts_languages(),
710
                            'dojoType="fox.form.Select"');
711
712
                    } else if ($type_name == "bool") {
713
714
                        array_push($listed_boolean_prefs, $pref_name);
715
716
                        $checked = ($value == "true") ? "checked=\"checked\"" : "";
717
718
                        if ($pref_name == "PURGE_UNREAD_ARTICLES" && FORCE_ARTICLE_PURGE != 0) {
0 ignored issues
show
Bug introduced by
The constant FORCE_ARTICLE_PURGE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
719
                            $disabled = "disabled=\"1\"";
720
                            $checked = "checked=\"checked\"";
721
                        } else {
722
                            $disabled = "";
723
                        }
724
725
                        print "<input type='checkbox' name='$pref_name' $checked $disabled
726
							dojoType='dijit.form.CheckBox' id='CB_$pref_name' value='1'>";
727
728
                    } else if (array_search($pref_name, array('FRESH_ARTICLE_MAX_AGE',
729
                            'PURGE_OLD_DAYS', 'LONG_DATE_FORMAT', 'SHORT_DATE_FORMAT')) !== false) {
730
731
                        $regexp = ($type_name == 'integer') ? 'regexp="^\d*$"' : '';
732
733
                        if ($pref_name == "PURGE_OLD_DAYS" && FORCE_ARTICLE_PURGE != 0) {
734
                            $disabled = "disabled='1'";
735
                            $value = FORCE_ARTICLE_PURGE;
736
                        } else {
737
                            $disabled = "";
738
                        }
739
740
                        if ($type_name == 'integer') {
741
                                                    print "<input dojoType=\"dijit.form.NumberSpinner\"
742
								required='1' $disabled
743
								name=\"$pref_name\" value=\"$value\">";
744
                        } else {
745
                                                    print "<input dojoType=\"dijit.form.TextBox\"
746
								required='1' $regexp $disabled
747
								name=\"$pref_name\" value=\"$value\">";
748
                        }
749
750
                    } else if ($pref_name == "SSL_CERT_SERIAL") {
751
752
                        print "<input dojoType='dijit.form.ValidationTextBox'
753
							id='SSL_CERT_SERIAL' readonly='1'
754
							name=\"$pref_name\" value=\"$value\">";
755
756
                        $cert_serial = htmlspecialchars(get_ssl_certificate_id());
757
                        $has_serial = ($cert_serial) ? "false" : "true";
758
759
                        print "<button dojoType='dijit.form.Button' disabled='$has_serial'
760
							onclick=\"dijit.byId('SSL_CERT_SERIAL').attr('value', '$cert_serial')\">".
761
                            __('Register')."</button>";
762
763
                        print "<button dojoType='dijit.form.Button' class='alt-danger'
764
							onclick=\"dijit.byId('SSL_CERT_SERIAL').attr('value', '')\">" .
765
                            __('Clear')."</button>";
766
767
                        print "<button dojoType='dijit.form.Button' class='alt-info'
768
							onclick='window.open(\"https://tt-rss.org/wiki/SSL%20Certificate%20Authentication\")'>
769
							<i class='material-icons'>help</i> ".__("More info...")."</button>";
770
771
                    } else if ($pref_name == 'DIGEST_PREFERRED_TIME') {
772
                        print "<input dojoType=\"dijit.form.ValidationTextBox\"
773
							id=\"$pref_name\" regexp=\"[012]?\d:\d\d\" placeHolder=\"12:00\"
774
							name=\"$pref_name\" value=\"$value\">";
775
776
                        $item['help_text'] .= ". ".T_sprintf("Current server time: %s", date("H:i"));
777
                    } else {
778
                        $regexp = ($type_name == 'integer') ? 'regexp="^\d*$"' : '';
779
780
                        print "<input dojoType=\"dijit.form.ValidationTextBox\" $regexp name=\"$pref_name\" value=\"$value\">";
781
                    }
782
783
                    if ($item['help_text']) {
784
                                            print "<div class='help-text text-muted'><label for='CB_$pref_name'>".$item['help_text']."</label></div>";
785
                    }
786
787
                    print "</fieldset>";
788
                }
789
            }
790
        }
791
792
        $listed_boolean_prefs = htmlspecialchars(join(",", $listed_boolean_prefs));
793
794
        print_hidden("boolean_prefs", "$listed_boolean_prefs");
795
796
        PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION,
797
            "hook_prefs_tab_section", "prefPrefsPrefsInside");
798
799
        print '</div>'; # inside pane
800
        print '<div dojoType="dijit.layout.ContentPane" region="bottom">';
801
802
        print_hidden("op", "pref-prefs");
803
        print_hidden("method", "saveconfig");
804
805
        print "<div dojoType=\"fox.form.ComboButton\" type=\"submit\" class=\"alt-primary\">
806
			<span>".__('Save configuration')."</span>
807
			<div dojoType=\"dijit.DropDownMenu\">
808
				<div dojoType=\"dijit.MenuItem\"
809
					onclick=\"dijit.byId('changeSettingsForm').onSubmit(null, true)\">".
810
                __("Save and exit preferences")."</div>
811
			</div>
812
			</div>";
813
814
        print "<button dojoType=\"dijit.form.Button\" onclick=\"return Helpers.editProfiles()\">".
815
            __('Manage profiles')."</button> ";
816
817
        print "<button dojoType=\"dijit.form.Button\" class=\"alt-danger\" onclick=\"return Helpers.confirmReset()\">".
818
            __('Reset to defaults')."</button>";
819
820
        print "&nbsp;";
821
822
        PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION,
823
            "hook_prefs_tab_section", "prefPrefsPrefsOutside");
824
825
        print "</form>";
826
        print '</div>'; # inner pane
827
        print '</div>'; # border container
828
829
        print "</div>"; #pane
830
831
        print "<div dojoType=\"dijit.layout.AccordionPane\"
832
			title=\"<i class='material-icons'>extension</i> ".__('Plugins')."\">";
833
834
        print "<form dojoType=\"dijit.form.Form\" id=\"changePluginsForm\">";
835
836
        print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\">
837
		evt.preventDefault();
838
		if (this.validate()) {
839
			Notify.progress('Saving data...', true);
840
841
			new Ajax.Request('backend.php', {
842
				parameters: dojo.objectToQuery(this.getValues()),
843
				onComplete: function(transport) {
844
					Notify.close();
845
					if (confirm(__('Selected plugins have been enabled. Reload?'))) {
846
						window.location.reload();
847
					}
848
			} });
849
850
		}
851
		</script>";
852
853
        print_hidden("op", "pref-prefs");
854
        print_hidden("method", "setplugins");
855
856
        print '<div dojoType="dijit.layout.BorderContainer" gutters="false">';
857
        print '<div dojoType="dijit.layout.ContentPane" region="center" style="overflow-y : auto">';
858
859
        if (ini_get("open_basedir") && function_exists("curl_init") && !defined("NO_CURL")) {
860
            print_warning("Your PHP configuration has open_basedir restrictions enabled. Some plugins relying on CURL for functionality may not work correctly.");
0 ignored issues
show
Deprecated Code introduced by
The function print_warning() has been deprecated: Use twig function warningMessage ( Ignorable by Annotation )

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

860
            /** @scrutinizer ignore-deprecated */ print_warning("Your PHP configuration has open_basedir restrictions enabled. Some plugins relying on CURL for functionality may not work correctly.");

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...
861
        }
862
863
        $feed_handler_whitelist = ["Af_Comics"];
864
865
        $feed_handlers = array_merge(
866
            PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FEED_FETCHED),
867
            PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FEED_PARSED),
868
            PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FETCH_FEED));
869
870
        $feed_handlers = array_filter($feed_handlers, function($plugin) use ($feed_handler_whitelist) {
871
            return in_array(get_class($plugin), $feed_handler_whitelist) === false; });
872
873
        if (count($feed_handlers) > 0) {
874
            print_error(
0 ignored issues
show
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

874
            /** @scrutinizer ignore-deprecated */ print_error(

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...
875
                T_sprintf("The following plugins use per-feed content hooks. This may cause excessive data usage and origin server load resulting in a ban of your instance: <b>%s</b>",
876
                    implode(", ", array_map(function($plugin) { return get_class($plugin); }, $feed_handlers))
877
                )." (<a href='https://tt-rss.org/wiki/FeedHandlerPlugins' target='_blank'>".__("More info...")."</a>)"
878
            );
879
        }
880
881
        print "<h2>".__("System plugins")."</h2>";
882
        print_notice("System plugins are enabled in <strong>config.php</strong> for all users.");
0 ignored issues
show
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

882
        /** @scrutinizer ignore-deprecated */ print_notice("System plugins are enabled in <strong>config.php</strong> for all users.");

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...
883
884
        $system_enabled = array_map("trim", explode(",", PLUGINS));
0 ignored issues
show
Bug introduced by
The constant PLUGINS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
885
        $user_enabled = array_map("trim", explode(",", get_pref("_ENABLED_PLUGINS")));
886
887
        $tmppluginhost = new PluginHost();
888
        $tmppluginhost->load_all($tmppluginhost::KIND_ALL, $_SESSION["uid"], true);
889
        $tmppluginhost->load_data();
890
891
        foreach ($tmppluginhost->get_plugins() as $name => $plugin) {
892
            $about = $plugin->about();
893
894
            if ($about[3]) {
895
                if (in_array($name, $system_enabled)) {
896
                    $checked = "checked='1'";
897
                } else {
898
                    $checked = "";
899
                }
900
901
                print "<fieldset class='prefs plugin'>
902
					<label>$name:</label>
903
					<label class='checkbox description text-muted' id='PLABEL-$name'>
904
						<input disabled='1'
905
							dojoType='dijit.form.CheckBox' $checked type='checkbox'>
906
						".htmlspecialchars($about[1])."</label>";
907
908
                    if (@$about[4]) {
909
                        print "<button dojoType='dijit.form.Button' class='alt-info'
910
							onclick='window.open(\"".htmlspecialchars($about[4])."\")'>
911
								<i class='material-icons'>open_in_new</i> ".__("More info...")."</button>";
912
                    }
913
914
                    print "<div dojoType='dijit.Tooltip' connectId='PLABEL-$name' position='after'>".
915
                        htmlspecialchars(T_sprintf("v%.2f, by %s", $about[0], $about[2])).
916
                        "</div>";
917
918
                print "</fieldset>";
919
920
            }
921
        }
922
923
        print "<h2>".__("User plugins")."</h2>";
924
925
        foreach ($tmppluginhost->get_plugins() as $name => $plugin) {
926
            $about = $plugin->about();
927
928
            if (!$about[3]) {
929
930
                $checked = "";
931
                $disabled = "";
932
933
                if (in_array($name, $system_enabled)) {
934
                    $checked = "checked='1'";
935
                    $disabled = "disabled='1'";
936
                } else if (in_array($name, $user_enabled)) {
937
                    $checked = "checked='1'";
938
                }
939
940
                print "<fieldset class='prefs plugin'>
941
					<label>$name:</label>
942
					<label class='checkbox description text-muted' id='PLABEL-$name'>
943
						<input name='plugins[]' value='$name' dojoType='dijit.form.CheckBox' $checked $disabled type='checkbox'>
944
						".htmlspecialchars($about[1])."</label>";
945
946
                if (count($tmppluginhost->get_all($plugin)) > 0) {
947
                    if (in_array($name, $system_enabled) || in_array($name, $user_enabled)) {
948
                        print " <button dojoType='dijit.form.Button'
949
							onclick=\"Helpers.clearPluginData('$name')\">
950
								<i class='material-icons'>clear</i> ".__("Clear data")."</button>";
951
                    }
952
                }
953
954
                if (@$about[4]) {
955
                    print " <button dojoType='dijit.form.Button' class='alt-info'
956
							onclick='window.open(\"".htmlspecialchars($about[4])."\")'>
957
								<i class='material-icons'>open_in_new</i> ".__("More info...")."</button>";
958
                }
959
960
                print "<div dojoType='dijit.Tooltip' connectId='PLABEL-$name' position='after'>".
961
                    htmlspecialchars(T_sprintf("v%.2f, by %s", $about[0], $about[2])).
962
                    "</div>";
963
964
                print "</fieldset>";
965
            }
966
        }
967
968
        print "</div>"; #content-pane
969
        print '<div dojoType="dijit.layout.ContentPane" region="bottom">';
970
971
        print "<button dojoType='dijit.form.Button' style='float : left' class='alt-info' onclick='window.open(\"https://tt-rss.org/wiki/Plugins\")'>
972
			<i class='material-icons'>help</i> ".__("More info...")."</button>";
973
974
        print "<button dojoType='dijit.form.Button' class='alt-primary' type='submit'>".
975
            __("Enable selected plugins")."</button>";
976
        print "</div>"; #pane
977
978
        print "</div>"; #pane
979
        print "</div>"; #border-container
980
981
        print "</form>";
982
983
        PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB,
984
            "hook_prefs_tab", "prefPrefs");
985
986
        print "</div>"; #container
987
988
    }
989
990
    public function toggleAdvanced() {
991
        $_SESSION["prefs_show_advanced"] = !$_SESSION["prefs_show_advanced"];
992
    }
993
994
    public function otpsecret() {
995
        $sth = $this->pdo->prepare("SELECT salt, otp_enabled
996
			FROM ttrss_users
997
			WHERE id = ?");
998
        $sth->execute([$_SESSION['uid']]);
999
1000
        if ($row = $sth->fetch()) {
1001
            $otp_enabled = sql_bool_to_bool($row["otp_enabled"]);
1002
1003
            if (!$otp_enabled) {
1004
                $base32 = new \OTPHP\Base32();
0 ignored issues
show
Bug introduced by
The type OTPHP\Base32 was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1005
                $secret = $base32->encode(mb_substr(sha1($row["salt"]), 0, 12), false);
1006
1007
                return $secret;
1008
            }
1009
        }
1010
1011
        return false;
1012
    }
1013
1014
    public function otpqrcode() {
1015
        require_once "lib/phpqrcode/phpqrcode.php";
1016
1017
        $sth = $this->pdo->prepare("SELECT login
1018
			FROM ttrss_users
1019
			WHERE id = ?");
1020
        $sth->execute([$_SESSION['uid']]);
1021
1022
        if ($row = $sth->fetch()) {
1023
            $secret = $this->otpsecret();
1024
            $login = $row['login'];
1025
1026
            if ($secret) {
1027
                QRcode::png("otpauth://totp/".urlencode($login).
1028
                    "?secret=$secret&issuer=".urlencode("Tiny Tiny RSS"));
1029
            }
1030
        }
1031
    }
1032
1033
    public function otpenable() {
1034
1035
        $password = clean($_REQUEST["password"]);
1036
        $otp = clean($_REQUEST["otp"]);
1037
1038
        $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]);
1039
1040
        if ($authenticator->check_password($_SESSION["uid"], $password)) {
1041
1042
            $secret = $this->otpsecret();
1043
1044
            if ($secret) {
1045
                $topt = new \OTPHP\TOTP($secret);
0 ignored issues
show
Bug introduced by
The call to OTPHP\TOTP::__construct() has too few arguments starting with period. ( Ignorable by Annotation )

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

1045
                $topt = /** @scrutinizer ignore-call */ new \OTPHP\TOTP($secret);

This check compares calls to functions or methods with their respective definitions. If the call has less 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...
1046
                $otp_check = $topt->now();
1047
1048
                if ($otp == $otp_check) {
1049
                    $sth = $this->pdo->prepare("UPDATE ttrss_users SET otp_enabled = true WHERE id = ?");
1050
1051
                    $sth->execute([$_SESSION['uid']]);
1052
1053
                    print "OK";
1054
                } else {
1055
                    print "ERROR:".__("Incorrect one time password");
1056
                }
1057
            }
1058
1059
        } else {
1060
            print "ERROR:".__("Incorrect password");
1061
        }
1062
1063
    }
1064
1065
    public static function isdefaultpassword() {
1066
        $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]);
1067
1068
        if ($authenticator &&
1069
                method_exists($authenticator, "check_password") &&
1070
                $authenticator->check_password($_SESSION["uid"], "password")) {
1071
1072
            return true;
1073
        }
1074
1075
        return false;
1076
    }
1077
1078
    public function otpdisable() {
1079
        $password = clean($_REQUEST["password"]);
1080
1081
        $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]);
1082
1083
        if ($authenticator->check_password($_SESSION["uid"], $password)) {
1084
1085
            $sth = $this->pdo->prepare("SELECT email, login FROM ttrss_users WHERE id = ?");
1086
            $sth->execute([$_SESSION['uid']]);
1087
1088
            if ($row = $sth->fetch()) {
1089
                $mailer = new Mailer();
1090
1091
                require_once "lib/MiniTemplator.class.php";
1092
1093
                $tpl = new MiniTemplator;
1094
1095
                $tpl->readTemplateFromFile("templates/otp_disabled_template.txt");
1096
1097
                $tpl->setVariable('LOGIN', $row["login"]);
1098
                $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH);
0 ignored issues
show
Bug introduced by
The constant SELF_URL_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1099
1100
                $tpl->addBlock('message');
1101
1102
                $tpl->generateOutputToString($message);
1103
1104
                $mailer->mail(["to_name" => $row["login"],
1105
                    "to_address" => $row["email"],
1106
                    "subject" => "[tt-rss] OTP change notification",
1107
                    "message" => $message]);
1108
            }
1109
1110
            $sth = $this->pdo->prepare("UPDATE ttrss_users SET otp_enabled = false WHERE
1111
				id = ?");
1112
            $sth->execute([$_SESSION['uid']]);
1113
1114
            print "OK";
1115
        } else {
1116
            print "ERROR: ".__("Incorrect password");
1117
        }
1118
1119
    }
1120
1121
    public function setplugins() {
1122
        if (is_array(clean($_REQUEST["plugins"]))) {
1123
                    $plugins = join(",", clean($_REQUEST["plugins"]));
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['plugins']) can also be of type string; however, parameter $pieces of join() does only seem to accept array, 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

1123
                    $plugins = join(",", /** @scrutinizer ignore-type */ clean($_REQUEST["plugins"]));
Loading history...
1124
        } else {
1125
                    $plugins = "";
1126
        }
1127
1128
        set_pref("_ENABLED_PLUGINS", $plugins);
1129
    }
1130
1131
    public function clearplugindata() {
1132
        $name = clean($_REQUEST["name"]);
1133
1134
        PluginHost::getInstance()->clear_data(PluginHost::getInstance()->get_plugin($name));
1135
    }
1136
1137
    public function customizeCSS() {
1138
        $value = get_pref("USER_STYLESHEET");
1139
        $value = str_replace("<br/>", "\n", $value);
1140
1141
        print_notice(__("You can override colors, fonts and layout of your currently selected theme with custom CSS declarations here."));
0 ignored issues
show
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

1141
        /** @scrutinizer ignore-deprecated */ print_notice(__("You can override colors, fonts and layout of your currently selected theme with custom CSS declarations here."));

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...
Unused Code introduced by
The call to print_notice() has too many arguments starting with __('You can override col...SS declarations here.'). ( Ignorable by Annotation )

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

1141
        /** @scrutinizer ignore-call */ 
1142
        print_notice(__("You can override colors, fonts and layout of your currently selected theme with custom CSS declarations here."));

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...
1142
1143
        print_hidden("op", "rpc");
1144
        print_hidden("method", "setpref");
1145
        print_hidden("key", "USER_STYLESHEET");
1146
1147
        print "<div id='css_edit_apply_msg' style='display : none'>";
1148
        print_warning(__("User CSS has been applied, you might need to reload the page to see all changes."));
0 ignored issues
show
Unused Code introduced by
The call to print_warning() has too many arguments starting with __('User CSS has been ap...e to see all changes.'). ( Ignorable by Annotation )

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

1148
        /** @scrutinizer ignore-call */ 
1149
        print_warning(__("User CSS has been applied, you might need to reload the page to see all changes."));

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_warning() has been deprecated: Use twig function warningMessage ( Ignorable by Annotation )

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

1148
        /** @scrutinizer ignore-deprecated */ print_warning(__("User CSS has been applied, you might need to reload the page to see all changes."));

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...
1149
        print "</div>";
1150
1151
        print "<textarea class='panel user-css-editor' dojoType='dijit.form.SimpleTextarea'
1152
			style='font-size : 12px;' name='value'>$value</textarea>";
1153
1154
        print "<footer>";
1155
        print "<button dojoType='dijit.form.Button' class='alt-success'
1156
			onclick=\"dijit.byId('cssEditDlg').apply()\">".__('Apply')."</button> ";
1157
        print "<button dojoType='dijit.form.Button' class='alt-primary'
1158
			onclick=\"dijit.byId('cssEditDlg').execute()\">".__('Save and reload')."</button> ";
1159
        print "<button dojoType='dijit.form.Button'
1160
			onclick=\"dijit.byId('cssEditDlg').hide()\">".__('Cancel')."</button>";
1161
        print "</footer>";
1162
1163
    }
1164
1165
    public function editPrefProfiles() {
1166
        print "<div dojoType='fox.Toolbar'>";
1167
1168
        print "<div dojoType='fox.form.DropDownButton'>".
1169
                "<span>".__('Select')."</span>";
1170
        print "<div dojoType='dijit.Menu' style='display: none'>";
1171
        print "<div onclick=\"Tables.select('pref-profiles-list', true)\"
1172
			dojoType='dijit.MenuItem'>".__('All')."</div>";
1173
        print "<div onclick=\"Tables.select('pref-profiles-list', false)\"
1174
			dojoType='dijit.MenuItem'>".__('None')."</div>";
1175
        print "</div></div>";
1176
1177
        print "<div style='float : right'>";
1178
1179
        print "<input name='newprofile' dojoType='dijit.form.ValidationTextBox'
1180
				required='1'>
1181
			<button dojoType='dijit.form.Button'
1182
			onclick=\"dijit.byId('profileEditDlg').addProfile()\">".
1183
                __('Create profile')."</button></div>";
1184
1185
        print "</div>";
1186
1187
        $sth = $this->pdo->prepare("SELECT title,id FROM ttrss_settings_profiles
1188
			WHERE owner_uid = ? ORDER BY title");
1189
        $sth->execute([$_SESSION['uid']]);
1190
1191
        print "<div class='panel panel-scrollable'>";
1192
1193
        print "<form id='profile_edit_form' onsubmit='return false'>";
1194
1195
        print "<table width='100%' id='pref-profiles-list'>";
1196
1197
        print "<tr>"; # data-row-id='0' <-- no point, shouldn't be removed
1198
1199
        print "<td><input onclick='Tables.onRowChecked(this);' dojoType='dijit.form.CheckBox' type='checkbox'></td>";
1200
1201
        if (!$_SESSION["profile"]) {
1202
            $is_active = __("(active)");
1203
        } else {
1204
            $is_active = "";
1205
        }
1206
1207
        print "<td width='100%'><span>".__("Default profile")." $is_active</span></td>";
1208
1209
        print "</tr>";
1210
1211
        while ($line = $sth->fetch()) {
1212
1213
            $profile_id = $line["id"];
1214
1215
            print "<tr data-row-id='$profile_id'>";
1216
1217
            $edit_title = htmlspecialchars($line["title"]);
1218
1219
            print "<td><input onclick='Tables.onRowChecked(this);' dojoType='dijit.form.CheckBox' type='checkbox'></td>";
1220
1221
            if ($_SESSION["profile"] == $line["id"]) {
1222
                $is_active = __("(active)");
1223
            } else {
1224
                $is_active = "";
1225
            }
1226
1227
            print "<td><span dojoType='dijit.InlineEditBox'
1228
				width='300px' autoSave='false'
1229
				profile-id='$profile_id'>".$edit_title.
1230
                "<script type='dojo/method' event='onChange' args='item'>
1231
					var elem = this;
1232
					dojo.xhrPost({
1233
						url: 'backend.php',
1234
						content: {op: 'rpc', method: 'saveprofile',
1235
							value: this.value,
1236
							id: this.srcNodeRef.getAttribute('profile-id')},
1237
							load: function(response) {
1238
								elem.attr('value', response);
1239
						}
1240
					});
1241
				</script>
1242
			</span> $is_active</td>";
1243
1244
            print "</tr>";
1245
        }
1246
1247
        print "</table>";
1248
        print "</form>";
1249
        print "</div>";
1250
1251
        print "<footer>
1252
			<button style='float : left' class='alt-danger' dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('profileEditDlg').removeSelected()\">".
1253
            __('Remove selected profiles')."</button>
1254
			<button dojoType='dijit.form.Button' class='alt-primary' type='submit' onclick=\"dijit.byId('profileEditDlg').activateProfile()\">".
1255
            __('Activate profile')."</button>
1256
			<button dojoType='dijit.form.Button' onclick=\"dijit.byId('profileEditDlg').hide()\">".
1257
            __('Cancel')."</button>";
1258
        print "</footer>";
1259
1260
    }
1261
1262
    private function getShortDesc($pref_name) {
1263
        if (isset($this->pref_help[$pref_name])) {
1264
            return $this->pref_help[$pref_name][0];
1265
        }
1266
        return "";
1267
    }
1268
1269
    private function getHelpText($pref_name) {
1270
        if (isset($this->pref_help[$pref_name])) {
1271
            return $this->pref_help[$pref_name][1];
1272
        }
1273
        return "";
1274
    }
1275
1276
    private function appPasswordList() {
1277
        print "<div dojoType='fox.Toolbar'>";
1278
        print "<div dojoType='fox.form.DropDownButton'>".
1279
            "<span>".__('Select')."</span>";
1280
        print "<div dojoType='dijit.Menu' style='display: none'>";
1281
        print "<div onclick=\"Tables.select('app-password-list', true)\"
1282
				dojoType=\"dijit.MenuItem\">" . __('All')."</div>";
1283
        print "<div onclick=\"Tables.select('app-password-list', false)\"
1284
				dojoType=\"dijit.MenuItem\">" . __('None')."</div>";
1285
        print "</div></div>";
1286
        print "</div>"; #toolbar
1287
1288
        print "<div class='panel panel-scrollable'>";
1289
        print "<table width='100%' id='app-password-list'>";
1290
        print "<tr>";
1291
        print "<th width='2%'></th>";
1292
        print "<th align='left'>".__("Description")."</th>";
1293
        print "<th align='right'>".__("Created")."</th>";
1294
        print "<th align='right'>".__("Last used")."</th>";
1295
        print "</tr>";
1296
1297
        $sth = $this->pdo->prepare("SELECT id, title, created, last_used
1298
			FROM ttrss_app_passwords WHERE owner_uid = ?");
1299
        $sth->execute([$_SESSION['uid']]);
1300
1301
        while ($row = $sth->fetch()) {
1302
1303
            $row_id = $row["id"];
1304
1305
            print "<tr data-row-id='$row_id'>";
1306
1307
            print "<td align='center'>
1308
						<input onclick='Tables.onRowChecked(this)' dojoType='dijit.form.CheckBox' type='checkbox'></td>";
1309
            print "<td>".htmlspecialchars($row["title"])."</td>";
1310
1311
            print "<td align='right' class='text-muted'>";
1312
            print make_local_datetime($row['created'], false);
1313
            print "</td>";
1314
1315
            print "<td align='right' class='text-muted'>";
1316
            print make_local_datetime($row['last_used'], false);
1317
            print "</td>";
1318
1319
            print "</tr>";
1320
        }
1321
1322
        print "</table>";
1323
        print "</div>";
1324
    }
1325
1326
    private function encryptAppPassword($password) {
1327
        $salt = substr(bin2hex(get_random_bytes(24)), 0, 24);
1328
1329
        return "SSHA-512:".hash('sha512', $salt.$password).":$salt";
1330
    }
1331
1332
    public function deleteAppPassword() {
1333
        $ids = explode(",", clean($_REQUEST['ids']));
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['ids']) can also be of type array; however, parameter $string of explode() 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

1333
        $ids = explode(",", /** @scrutinizer ignore-type */ clean($_REQUEST['ids']));
Loading history...
1334
        $ids_qmarks = arr_qmarks($ids);
1335
1336
        $sth = $this->pdo->prepare("DELETE FROM ttrss_app_passwords WHERE id IN ($ids_qmarks) AND owner_uid = ?");
1337
        $sth->execute(array_merge($ids, [$_SESSION['uid']]));
1338
1339
        $this->appPasswordList();
1340
    }
1341
1342
    public function generateAppPassword() {
1343
        $title = clean($_REQUEST['title']);
1344
        $new_password = make_password(16);
1345
        $new_password_hash = $this->encryptAppPassword($new_password);
1346
1347
        print_warning(T_sprintf("Generated password <strong>%s</strong> for %s. Please remember it for future reference.", $new_password, $title));
0 ignored issues
show
Unused Code introduced by
The call to print_warning() has too many arguments starting with T_sprintf('Generated pas... $new_password, $title). ( Ignorable by Annotation )

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

1347
        /** @scrutinizer ignore-call */ 
1348
        print_warning(T_sprintf("Generated password <strong>%s</strong> for %s. Please remember it for future reference.", $new_password, $title));

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_warning() has been deprecated: Use twig function warningMessage ( Ignorable by Annotation )

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

1347
        /** @scrutinizer ignore-deprecated */ print_warning(T_sprintf("Generated password <strong>%s</strong> for %s. Please remember it for future reference.", $new_password, $title));

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...
1348
1349
        $sth = $this->pdo->prepare("INSERT INTO ttrss_app_passwords
1350
    			(title, pwd_hash, service, created, owner_uid)
1351
    		 VALUES
1352
    		    (?, ?, ?, NOW(), ?)");
1353
1354
        $sth->execute([$title, $new_password_hash, Auth_Base::AUTH_SERVICE_API, $_SESSION['uid']]);
1355
1356
        $this->appPasswordList();
1357
    }
1358
}
1359