]>
git.wh0rd.org - tt-rss.git/blob - classes/pref/prefs.php
3 class Pref_Prefs
extends Handler_Protected
{
5 private $pref_help = array ();
6 private $pref_sections = array ();
8 function csrf_ignore ( $method ) {
9 $csrf_ignored = array ( "index" , "updateself" , "customizecss" , "editprefprofiles" );
11 return array_search ( $method , $csrf_ignored ) !== false ;
14 function __construct ( $args ) {
15 parent
:: __construct ( $args );
17 $this -> pref_sections
= array (
24 $this -> pref_help
= array (
25 "ALLOW_DUPLICATE_POSTS" => array ( __ ( "Allow duplicate articles" ), "" ),
26 "AUTO_ASSIGN_LABELS" => array ( __ ( "Assign articles to labels automatically" ), "" ),
27 "BLACKLISTED_TAGS" => array ( __ ( "Blacklisted tags" ), __ ( "When auto-detecting tags in articles these tags will not be applied (comma-separated list)." )),
28 "CDM_AUTO_CATCHUP" => array ( __ ( "Automatically mark articles as read" ), __ ( "This option enables marking articles as read automatically while you scroll article list." )),
29 "CDM_EXPANDED" => array ( __ ( "Automatically expand articles in combined mode" ), "" ),
30 "COMBINED_DISPLAY_MODE" => array ( __ ( "Combined feed display" ), __ ( "Display expanded list of feed articles, instead of separate displays for headlines and article content" )),
31 "CONFIRM_FEED_CATCHUP" => array ( __ ( "Confirm marking feed as read" ), "" ),
32 "DEFAULT_ARTICLE_LIMIT" => array ( __ ( "Amount of articles to display at once" ), "" ),
33 "DEFAULT_UPDATE_INTERVAL" => array ( __ ( "Default feed update interval" ), __ ( "Shortest interval at which a feed will be checked for updates regardless of update method" )),
34 "DIGEST_CATCHUP" => array ( __ ( "Mark articles in e-mail digest as read" ), "" ),
35 "DIGEST_ENABLE" => array ( __ ( "Enable e-mail digest" ), __ ( "This option enables sending daily digest of new (and unread) headlines on your configured e-mail address" )),
36 "DIGEST_PREFERRED_TIME" => array ( __ ( "Try to send digests around specified time" ), __ ( "Uses UTC timezone" )),
37 "ENABLE_API_ACCESS" => array ( __ ( "Enable API access" ), __ ( "Allows external clients to access this account through the API" )),
38 "ENABLE_FEED_CATS" => array ( __ ( "Enable feed categories" ), "" ),
39 "FEEDS_SORT_BY_UNREAD" => array ( __ ( "Sort feeds by unread articles count" ), "" ),
40 "FRESH_ARTICLE_MAX_AGE" => array ( __ ( "Maximum age of fresh articles (in hours)" ), "" ),
41 "HIDE_READ_FEEDS" => array ( __ ( "Hide feeds with no unread articles" ), "" ),
42 "HIDE_READ_SHOWS_SPECIAL" => array ( __ ( "Show special feeds when hiding read feeds" ), "" ),
43 "LONG_DATE_FORMAT" => array ( __ ( "Long date format" ), "" ),
44 "ON_CATCHUP_SHOW_NEXT_FEED" => array ( __ ( "On catchup show next feed" ), __ ( "Automatically open next feed with unread articles after marking one as read" )),
45 "PURGE_OLD_DAYS" => array ( __ ( "Purge articles after this number of days (0 - disables)" ), "" ),
46 "PURGE_UNREAD_ARTICLES" => array ( __ ( "Purge unread articles" ), "" ),
47 "REVERSE_HEADLINES" => array ( __ ( "Reverse headline order (oldest first)" ), "" ),
48 "SHORT_DATE_FORMAT" => array ( __ ( "Short date format" ), "" ),
49 "SHOW_CONTENT_PREVIEW" => array ( __ ( "Show content preview in headlines list" ), "" ),
50 "SORT_HEADLINES_BY_FEED_DATE" => array ( __ ( "Sort headlines by feed date" ), __ ( "Use feed-specified date to sort headlines instead of local import date." )),
51 "SSL_CERT_SERIAL" => array ( __ ( "Login with an SSL certificate" ), __ ( "Click to register your SSL client certificate with tt-rss" )),
52 "STRIP_IMAGES" => array ( __ ( "Do not embed images in articles" ), "" ),
53 "STRIP_UNSAFE_TAGS" => array ( __ ( "Strip unsafe tags from articles" ), __ ( "Strip all but most common HTML tags when reading articles." )),
54 "USER_STYLESHEET" => array ( __ ( "Customize stylesheet" ), __ ( "Customize CSS stylesheet to your liking" )),
55 "USER_TIMEZONE" => array ( __ ( "User timezone" ), "" ),
56 "VFEED_GROUP_BY_FEED" => array ( __ ( "Group headlines in virtual feeds" ), __ ( "Special feeds, labels, and categories are grouped by originating feeds" )),
57 "USER_CSS_THEME" => array ( __ ( "Select theme" ), __ ( "Select one of the available CSS themes" ))
61 function changepassword () {
63 $old_pw = $_POST [ "old_password" ];
64 $new_pw = $_POST [ "new_password" ];
65 $con_pw = $_POST [ "confirm_password" ];
68 print "ERROR: " . __ ( "Old password cannot be blank." );
73 print "ERROR: " . __ ( "New password cannot be blank." );
77 if ( $new_pw != $con_pw ) {
78 print "ERROR: " . __ ( "Entered passwords do not match." );
82 $authenticator = PluginHost
:: getInstance ()-> get_plugin ( $_SESSION [ "auth_module" ]);
84 if ( method_exists ( $authenticator , "change_password" )) {
85 print $authenticator -> change_password ( $_SESSION [ "uid" ], $old_pw , $new_pw );
87 print "ERROR: " . __ ( "Function not supported by authentication module." );
91 function saveconfig () {
92 $boolean_prefs = explode ( "," , $_POST [ "boolean_prefs" ]);
94 foreach ( $boolean_prefs as $pref ) {
95 if (! isset ( $_POST [ $pref ])) $_POST [ $pref ] = 'false' ;
100 foreach ( array_keys ( $_POST ) as $pref_name ) {
102 $pref_name = $this -> dbh
-> escape_string ( $pref_name );
103 $value = $this -> dbh
-> escape_string ( $_POST [ $pref_name ]);
105 if ( $pref_name == 'DIGEST_PREFERRED_TIME' ) {
106 if ( get_pref ( 'DIGEST_PREFERRED_TIME' ) != $value ) {
108 $this -> dbh
-> query ( "UPDATE ttrss_users SET
109 last_digest_sent = NULL WHERE id = " . $_SESSION [ 'uid' ]);
114 if ( $pref_name == "language" ) {
115 if ( $_SESSION [ "language" ] != $value ) {
116 setcookie ( "ttrss_lang" , $value ,
117 time () + COOKIE_LIFETIME_LONG
);
118 $_SESSION [ "language" ] = $value ;
123 set_pref ( $pref_name , $value );
129 print "PREFS_NEED_RELOAD" ;
131 print __ ( "The configuration was saved." );
137 $pref_name = $this -> dbh
-> escape_string ( $_REQUEST [ "pn" ]);
139 $result = $this -> dbh
-> query ( "SELECT help_text FROM ttrss_prefs
140 WHERE pref_name = ' $pref_name '" );
142 if ( $this -> dbh
-> num_rows ( $result ) > 0 ) {
143 $help_text = $this -> dbh
-> fetch_result ( $result , 0 , "help_text" );
146 printf ( __ ( "Unknown option: %s " ), $pref_name );
150 function changeemail () {
152 $email = $this -> dbh
-> escape_string ( $_POST [ "email" ]);
153 $full_name = $this -> dbh
-> escape_string ( $_POST [ "full_name" ]);
155 $active_uid = $_SESSION [ "uid" ];
157 $this -> dbh
-> query ( "UPDATE ttrss_users SET email = ' $email ',
158 full_name = ' $full_name ' WHERE id = ' $active_uid '" );
160 print __ ( "Your personal data has been saved." );
165 function resetconfig () {
167 $_SESSION [ "prefs_op_result" ] = "reset-to-defaults" ;
169 if ( $_SESSION [ "profile" ]) {
170 $profile_qpart = "profile = '" . $_SESSION [ "profile" ] . "'" ;
172 $profile_qpart = "profile IS NULL" ;
175 $this -> dbh
-> query ( "DELETE FROM ttrss_user_prefs
176 WHERE $profile_qpart AND owner_uid = " . $_SESSION [ "uid" ]);
178 initialize_user_prefs ( $_SESSION [ "uid" ], $_SESSION [ "profile" ]);
180 echo __ ( "Your preferences are now set to default values." );
185 global $access_level_names ;
187 $prefs_blacklist = array ( "STRIP_UNSAFE_TAGS" , "REVERSE_HEADLINES" ,
188 "SORT_HEADLINES_BY_FEED_DATE" , "DEFAULT_ARTICLE_LIMIT" );
190 /* "FEEDS_SORT_BY_UNREAD", "HIDE_READ_FEEDS", "REVERSE_HEADLINES" */
192 $profile_blacklist = array ( "ALLOW_DUPLICATE_POSTS" , "PURGE_OLD_DAYS" ,
193 "PURGE_UNREAD_ARTICLES" , "DIGEST_ENABLE" , "DIGEST_CATCHUP" ,
194 "BLACKLISTED_TAGS" , "ENABLE_API_ACCESS" , "UPDATE_POST_ON_CHECKSUM_CHANGE" ,
195 "DEFAULT_UPDATE_INTERVAL" , "USER_TIMEZONE" , "SORT_HEADLINES_BY_FEED_DATE" ,
196 "SSL_CERT_SERIAL" , "DIGEST_PREFERRED_TIME" );
199 $_SESSION [ "prefs_op_result" ] = "" ;
201 print "<div dojoType= \" dijit.layout.AccordionContainer \" region= \" center \" >" ;
202 print "<div dojoType= \" dijit.layout.AccordionPane \" title= \" " . __ ( 'Personal data / Authentication' ). " \" >" ;
204 print "<form dojoType= \" dijit.form.Form \" id= \" changeUserdataForm \" >" ;
206 print "<script type= \" dojo/method \" event= \" onSubmit \" args= \" evt \" >
207 evt.preventDefault();
208 if (this.validate()) {
209 notify_progress('Saving data...', true);
211 new Ajax.Request('backend.php', {
212 parameters: dojo.objectToQuery(this.getValues()),
213 onComplete: function(transport) {
214 notify_callback2(transport);
220 print "<table width= \" 100% \" class= \" prefPrefsList \" >" ;
222 print "<h2>" . __ ( "Personal data" ) . "</h2>" ;
224 $result = $this -> dbh
-> query ( "SELECT email,full_name,otp_enabled,
225 access_level FROM ttrss_users
226 WHERE id = " . $_SESSION [ "uid" ]);
228 $email = htmlspecialchars ( $this -> dbh
-> fetch_result ( $result , 0 , "email" ));
229 $full_name = htmlspecialchars ( $this -> dbh
-> fetch_result ( $result , 0 , "full_name" ));
230 $otp_enabled = sql_bool_to_bool ( $this -> dbh
-> fetch_result ( $result , 0 , "otp_enabled" ));
232 print "<tr><td width= \" 40% \" >" . __ ( 'Full name' ). "</td>" ;
233 print "<td class= \" prefValue \" ><input dojoType= \" dijit.form.ValidationTextBox \" name= \" full_name \" required= \" 1 \"
234 value= \" $full_name\" ></td></tr>" ;
236 print "<tr><td width= \" 40% \" >" . __ ( 'E-mail' ). "</td>" ;
237 print "<td class= \" prefValue \" ><input dojoType= \" dijit.form.ValidationTextBox \" name= \" email \" required= \" 1 \" value= \" $email\" ></td></tr>" ;
239 if (! SINGLE_USER_MODE
&& ! $_SESSION [ "hide_hello" ]) {
241 $access_level = $this -> dbh
-> fetch_result ( $result , 0 , "access_level" );
242 print "<tr><td width= \" 40% \" >" . __ ( 'Access level' ). "</td>" ;
243 print "<td>" . $access_level_names [ $access_level ] . "</td></tr>" ;
248 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" op \" value= \" pref-prefs \" >" ;
249 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" method \" value= \" changeemail \" >" ;
251 print "<p><button dojoType= \" dijit.form.Button \" type= \" submit \" >" .
252 __ ( "Save data" ). "</button>" ;
256 if ( $_SESSION [ "auth_module" ]) {
257 $authenticator = PluginHost
:: getInstance ()-> get_plugin ( $_SESSION [ "auth_module" ]);
259 $authenticator = false ;
262 if ( $authenticator && method_exists ( $authenticator , "change_password" )) {
264 print "<h2>" . __ ( "Password" ) . "</h2>" ;
266 $result = $this -> dbh
-> query ( "SELECT id FROM ttrss_users
267 WHERE id = " . $_SESSION [ "uid" ]. " AND pwd_hash
268 = 'SHA1:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8'" );
270 if ( $this -> dbh
-> num_rows ( $result ) != 0 ) {
271 print format_warning ( __ ( "Your password is at default value, please change it." ), "default_pass_warning" );
274 print "<form dojoType= \" dijit.form.Form \" >" ;
276 print "<script type= \" dojo/method \" event= \" onSubmit \" args= \" evt \" >
277 evt.preventDefault();
278 if (this.validate()) {
279 notify_progress('Changing password...', true);
281 new Ajax.Request('backend.php', {
282 parameters: dojo.objectToQuery(this.getValues()),
283 onComplete: function(transport) {
285 if (transport.responseText.indexOf('ERROR: ') == 0) {
286 notify_error(transport.responseText.replace('ERROR: ', ''));
288 notify_info(transport.responseText);
289 var warn = $('default_pass_warning');
290 if (warn) Element.hide(warn);
298 print_notice ( __ ( "Changing your current password will disable OTP." ));
301 print "<table width= \" 100% \" class= \" prefPrefsList \" >" ;
303 print "<tr><td width= \" 40% \" >" . __ ( "Old password" ). "</td>" ;
304 print "<td class= \" prefValue \" ><input dojoType= \" dijit.form.ValidationTextBox \" type= \" password \" required= \" 1 \" name= \" old_password \" ></td></tr>" ;
306 print "<tr><td width= \" 40% \" >" . __ ( "New password" ). "</td>" ;
308 print "<td class= \" prefValue \" ><input dojoType= \" dijit.form.ValidationTextBox \" type= \" password \" required= \" 1 \"
309 name= \" new_password \" ></td></tr>" ;
311 print "<tr><td width= \" 40% \" >" . __ ( "Confirm password" ). "</td>" ;
313 print "<td class= \" prefValue \" ><input dojoType= \" dijit.form.ValidationTextBox \" type= \" password \" required= \" 1 \" name= \" confirm_password \" ></td></tr>" ;
317 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" op \" value= \" pref-prefs \" >" ;
318 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" method \" value= \" changepassword \" >" ;
320 print "<p><button dojoType= \" dijit.form.Button \" type= \" submit \" >" .
321 __ ( "Change password" ). "</button>" ;
325 if ( $_SESSION [ "auth_module" ] == "auth_internal" ) {
327 print "<h2>" . __ ( "One time passwords / Authenticator" ) . "</h2>" ;
331 print_notice ( __ ( "One time passwords are currently enabled. Enter your current password below to disable." ));
333 print "<form dojoType= \" dijit.form.Form \" >" ;
335 print "<script type= \" dojo/method \" event= \" onSubmit \" args= \" evt \" >
336 evt.preventDefault();
337 if (this.validate()) {
338 notify_progress('Disabling OTP', true);
340 new Ajax.Request('backend.php', {
341 parameters: dojo.objectToQuery(this.getValues()),
342 onComplete: function(transport) {
344 if (transport.responseText.indexOf('ERROR: ') == 0) {
345 notify_error(transport.responseText.replace('ERROR: ', ''));
347 window.location.reload();
354 print "<table width= \" 100% \" class= \" prefPrefsList \" >" ;
356 print "<tr><td width= \" 40% \" >" . __ ( "Enter your password" ). "</td>" ;
358 print "<td class= \" prefValue \" ><input dojoType= \" dijit.form.ValidationTextBox \" type= \" password \" required= \" 1 \"
359 name= \" password \" ></td></tr>" ;
363 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" op \" value= \" pref-prefs \" >" ;
364 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" method \" value= \" otpdisable \" >" ;
366 print "<p><button dojoType= \" dijit.form.Button \" type= \" submit \" >" .
367 __ ( "Disable OTP" ). "</button>" ;
371 } else if ( function_exists ( "imagecreatefromstring" )) {
373 print "<p>" . __ ( "You will need a compatible Authenticator to use this. Changing your password would automatically disable OTP." ) . "</p>" ;
375 print "<p>" . __ ( "Scan the following code by the Authenticator application:" ). "</p>" ;
377 $csrf_token = $_SESSION [ "csrf_token" ];
379 print "<img src= \" backend.php?op=pref-prefs&method=otpqrcode&csrf_token= $csrf_token\" >" ;
381 print "<form dojoType= \" dijit.form.Form \" id= \" changeOtpForm \" >" ;
383 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" op \" value= \" pref-prefs \" >" ;
384 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" method \" value= \" otpenable \" >" ;
386 print "<script type= \" dojo/method \" event= \" onSubmit \" args= \" evt \" >
387 evt.preventDefault();
388 if (this.validate()) {
389 notify_progress('Saving data...', true);
391 new Ajax.Request('backend.php', {
392 parameters: dojo.objectToQuery(this.getValues()),
393 onComplete: function(transport) {
395 if (transport.responseText.indexOf('ERROR:') == 0) {
396 notify_error(transport.responseText.replace('ERROR:', ''));
398 window.location.reload();
405 print "<table width= \" 100% \" class= \" prefPrefsList \" >" ;
407 print "<tr><td width= \" 40% \" >" . __ ( "Enter your password" ). "</td>" ;
409 print "<td class= \" prefValue \" ><input dojoType= \" dijit.form.ValidationTextBox \" type= \" password \" required= \" 1 \"
410 name= \" password \" ></td></tr>" ;
412 print "<tr><td width= \" 40% \" >" . __ ( "Enter the generated one time password" ). "</td>" ;
414 print "<td class= \" prefValue \" ><input dojoType= \" dijit.form.ValidationTextBox \" autocomplete= \" off \"
416 name= \" otp \" ></td></tr>" ;
418 print "<tr><td colspan= \" 2 \" >" ;
420 print "</td></tr><tr><td colspan= \" 2 \" >" ;
425 print "<p><button dojoType= \" dijit.form.Button \" type= \" submit \" >" .
426 __ ( "Enable OTP" ). "</button>" ;
432 print_notice ( __ ( "PHP GD functions are required for OTP support." ));
439 PluginHost
:: getInstance ()-> run_hooks ( PluginHost
:: HOOK_PREFS_TAB_SECTION
,
440 "hook_prefs_tab_section" , "prefPrefsAuth" );
442 print "</div>" ; #pane
444 print "<div dojoType= \" dijit.layout.AccordionPane \" selected= \" true \" title= \" " . __ ( 'Preferences' ). " \" >" ;
446 print "<form dojoType= \" dijit.form.Form \" id= \" changeSettingsForm \" >" ;
448 print "<script type= \" dojo/method \" event= \" onSubmit \" args= \" evt, quit \" >
449 if (evt) evt.preventDefault();
450 if (this.validate()) {
451 console.log(dojo.objectToQuery(this.getValues()));
453 new Ajax.Request('backend.php', {
454 parameters: dojo.objectToQuery(this.getValues()),
455 onComplete: function(transport) {
456 var msg = transport.responseText;
460 if (msg == 'PREFS_NEED_RELOAD') {
461 window.location.reload();
470 print '<div dojoType="dijit.layout.BorderContainer" gutters="false">' ;
472 print '<div dojoType="dijit.layout.ContentPane" region="center" style="overflow-y : auto">' ;
474 if ( $_SESSION [ "profile" ]) {
475 print_notice ( __ ( "Some preferences are only available in default profile." ));
478 if ( $_SESSION [ "profile" ]) {
479 initialize_user_prefs ( $_SESSION [ "uid" ], $_SESSION [ "profile" ]);
480 $profile_qpart = "profile = '" . $_SESSION [ "profile" ] . "'" ;
482 initialize_user_prefs ( $_SESSION [ "uid" ]);
483 $profile_qpart = "profile IS NULL" ;
486 /* if ($_SESSION["prefs_show_advanced"])
487 $access_query = "true";
489 $access_query = "(access_level = 0 AND section_id != 3)"; */
491 $access_query = 'true' ;
493 $result = $this -> dbh
-> query ( "SELECT DISTINCT
494 ttrss_user_prefs.pref_name,value,type_name,
495 ttrss_prefs_sections.order_id,
497 FROM ttrss_prefs,ttrss_prefs_types,ttrss_prefs_sections,ttrss_user_prefs
498 WHERE type_id = ttrss_prefs_types.id AND
500 section_id = ttrss_prefs_sections.id AND
501 ttrss_user_prefs.pref_name = ttrss_prefs.pref_name AND
503 owner_uid = " . $_SESSION [ "uid" ]. "
504 ORDER BY ttrss_prefs_sections.order_id,pref_name" );
508 $active_section = "" ;
510 $listed_boolean_prefs = array ();
512 while ( $line = $this -> dbh
-> fetch_assoc ( $result )) {
514 if ( in_array ( $line [ "pref_name" ], $prefs_blacklist )) {
518 $type_name = $line [ "type_name" ];
519 $pref_name = $line [ "pref_name" ];
520 $section_name = $this -> getSectionName ( $line [ "section_id" ]);
521 $value = $line [ "value" ];
523 $short_desc = $this -> getShortDesc ( $pref_name );
524 $help_text = $this -> getHelpText ( $pref_name );
526 if (! $short_desc ) continue ;
528 if ( $_SESSION [ "profile" ] && in_array ( $line [ "pref_name" ],
529 $profile_blacklist )) {
533 if ( $active_section != $line [ "section_id" ]) {
535 if ( $active_section != "" ) {
539 print "<table width= \" 100% \" class= \" prefPrefsList \" >" ;
541 $active_section = $line [ "section_id" ];
543 print "<tr><td colspan= \" 3 \" ><h3>" . $section_name . "</h3></td></tr>" ;
547 if ( $active_section == 2 ) {
550 print "<td width= \" 40% \" class= \" prefName \" >" ;
552 print __ ( "Language:" );
556 print_select_hash ( "language" , $_COOKIE [ "ttrss_lang" ], get_translations (),
557 "style='width : 220px; margin : 0px' dojoType='dijit.form.Select'" );
566 print "<td width= \" 40% \" class= \" prefName \" id= \" $pref_name\" >" ;
567 print "<label for='CB_ $pref_name '>" ;
571 if ( $help_text ) print "<div class= \" prefHelp \" >" . __ ( $help_text ). "</div>" ;
575 print "<td class= \" prefValue \" >" ;
577 if ( $pref_name == "USER_TIMEZONE" ) {
579 $timezones = explode ( " \n " , file_get_contents ( "lib/timezones.txt" ));
581 print_select ( $pref_name , $value , $timezones , 'dojoType="dijit.form.FilteringSelect"' );
582 } else if ( $pref_name == "USER_STYLESHEET" ) {
584 print "<button dojoType= \" dijit.form.Button \"
585 onclick= \" customizeCSS() \" >" . __ ( 'Customize' ) . "</button>" ;
587 } else if ( $pref_name == "USER_CSS_THEME" ) {
589 $themes = array_map ( "basename" , glob ( "themes/*.css" ));
591 print_select ( $pref_name , $value , $themes ,
592 'dojoType="dijit.form.Select"' );
595 } else if ( $pref_name == "DEFAULT_UPDATE_INTERVAL" ) {
597 global $update_intervals_nodefault ;
599 print_select_hash ( $pref_name , $value , $update_intervals_nodefault ,
600 'dojoType="dijit.form.Select"' );
602 } else if ( $type_name == "bool" ) {
604 array_push ( $listed_boolean_prefs , $pref_name );
606 $checked = ( $value == "true" ) ?
"checked= \" checked \" " : "" ;
608 if ( $pref_name == "PURGE_UNREAD_ARTICLES" && FORCE_ARTICLE_PURGE
!= 0 ) {
609 $disabled = "disabled= \" 1 \" " ;
610 $checked = "checked= \" checked \" " ;
615 print "<input type='checkbox' name=' $pref_name ' $checked $disabled
616 dojoType='dijit.form.CheckBox' id='CB_ $pref_name ' value='1'>" ;
618 } else if ( array_search ( $pref_name , array ( 'FRESH_ARTICLE_MAX_AGE' ,
619 'PURGE_OLD_DAYS' , 'LONG_DATE_FORMAT' , 'SHORT_DATE_FORMAT' )) !== false ) {
621 $regexp = ( $type_name == 'integer' ) ?
'regexp="^\d*$"' : '' ;
623 if ( $pref_name == "PURGE_OLD_DAYS" && FORCE_ARTICLE_PURGE
!= 0 ) {
624 $disabled = "disabled= \" 1 \" " ;
625 $value = FORCE_ARTICLE_PURGE
;
630 print "<input dojoType= \" dijit.form.ValidationTextBox \"
631 required= \" 1 \" $regexp $disabled
632 name= \" $pref_name\" value= \" $value\" >" ;
634 } else if ( $pref_name == "SSL_CERT_SERIAL" ) {
636 print "<input dojoType= \" dijit.form.ValidationTextBox \"
637 id= \" SSL_CERT_SERIAL \" readonly= \" 1 \"
638 name= \" $pref_name\" value= \" $value\" >" ;
640 $cert_serial = htmlspecialchars ( get_ssl_certificate_id ());
641 $has_serial = ( $cert_serial ) ?
"false" : "true" ;
643 print " <button dojoType= \" dijit.form.Button \" disabled= \" $has_serial\"
644 onclick= \" insertSSLserial(' $cert_serial ') \" >" .
645 __ ( 'Register' ) . "</button>" ;
647 print " <button dojoType= \" dijit.form.Button \"
648 onclick= \" insertSSLserial('') \" >" .
649 __ ( 'Clear' ) . "</button>" ;
651 } else if ( $pref_name == 'DIGEST_PREFERRED_TIME' ) {
652 print "<input dojoType= \" dijit.form.ValidationTextBox \"
653 id= \" $pref_name\" regexp= \" [012]?\d:\d\d \" placeHolder= \" 12:00 \"
654 name= \" $pref_name\" value= \" $value\" ><div class= \" insensitive \" >" .
655 T_sprintf ( "Current server time: %s (UTC)" , date ( "H:i" )) . "</div>" ;
657 $regexp = ( $type_name == 'integer' ) ?
'regexp="^\d*$"' : '' ;
659 print "<input dojoType= \" dijit.form.ValidationTextBox \"
661 name= \" $pref_name\" value= \" $value\" >" ;
673 $listed_boolean_prefs = htmlspecialchars ( join ( "," , $listed_boolean_prefs ));
675 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" boolean_prefs \" value= \" $listed_boolean_prefs\" >" ;
677 PluginHost
:: getInstance ()-> run_hooks ( PluginHost
:: HOOK_PREFS_TAB_SECTION
,
678 "hook_prefs_tab_section" , "prefPrefsPrefsInside" );
680 print '</div>' ; # inside pane
681 print '<div dojoType="dijit.layout.ContentPane" region="bottom">' ;
683 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" op \" value= \" pref-prefs \" >" ;
684 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" method \" value= \" saveconfig \" >" ;
686 print "<div dojoType= \" dijit.form.ComboButton \" type= \" submit \" >
687 <span>" . __ ( 'Save configuration' ). "</span>
688 <div dojoType= \" dijit.DropDownMenu \" >
689 <div dojoType= \" dijit.MenuItem \"
690 onclick= \" dijit.byId('changeSettingsForm').onSubmit(null, true) \" >" .
691 __ ( "Save and exit preferences" ). "</div>
695 print "<button dojoType= \" dijit.form.Button \" onclick= \" return editProfiles() \" >" .
696 __ ( 'Manage profiles' ). "</button> " ;
698 print "<button dojoType= \" dijit.form.Button \" onclick= \" return validatePrefsReset() \" >" .
699 __ ( 'Reset to defaults' ). "</button>" ;
703 /* $checked = $_SESSION["prefs_show_advanced"] ? "checked='1'" : "";
705 print "<input onclick='toggleAdvancedPrefs()'
706 id='prefs_show_advanced'
707 dojoType=\"dijit.form.CheckBox\"
709 type=\"checkbox\"></input>
710 <label for='prefs_show_advanced'>" .
711 __("Show additional preferences") . "</label>"; */
713 PluginHost
:: getInstance ()-> run_hooks ( PluginHost
:: HOOK_PREFS_TAB_SECTION
,
714 "hook_prefs_tab_section" , "prefPrefsPrefsOutside" );
717 print '</div>' ; # inner pane
718 print '</div>' ; # border container
720 print "</div>" ; #pane
722 print "<div dojoType= \" dijit.layout.AccordionPane \" title= \" " . __ ( 'Plugins' ). " \" >" ;
724 print "<h2>" . __ ( "Plugins" ). "</h2>" ;
726 print "<p>" . __ ( "You will need to reload Tiny Tiny RSS for plugin changes to take effect." ) . "</p>" ;
728 print_notice ( __ ( "Download more plugins at tt-rss.org <a class= \" visibleLink \" target= \" _blank \" href= \" http://tt-rss.org/forum/viewforum.php?f=22 \" >forums</a> or <a target= \" _blank \" class= \" visibleLink \" href= \" http://tt-rss.org/wiki/Plugins \" >wiki</a>." ));
730 print "<form dojoType= \" dijit.form.Form \" id= \" changePluginsForm \" >" ;
732 print "<script type= \" dojo/method \" event= \" onSubmit \" args= \" evt \" >
733 evt.preventDefault();
734 if (this.validate()) {
735 notify_progress('Saving data...', true);
737 new Ajax.Request('backend.php', {
738 parameters: dojo.objectToQuery(this.getValues()),
739 onComplete: function(transport) {
741 if (confirm(__('Selected plugins have been enabled. Reload?'))) {
742 window.location.reload();
749 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" op \" value= \" pref-prefs \" >" ;
750 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" method \" value= \" setplugins \" >" ;
752 print "<table width='100%' class='prefPluginsList'>" ;
754 print "<tr><td colspan='4'><h3>" . __ ( "System plugins" ). "</h3></td></tr>" ;
756 print "<tr class= \" title \" >
757 <td width= \" 5% \" > </td>
758 <td width='10%'>" . __ ( 'Plugin' ). "</td>
759 <td width=''>" . __ ( 'Description' ). "</td>
760 <td width='5%'>" . __ ( 'Version' ). "</td>
761 <td width='10%'>" . __ ( 'Author' ). "</td></tr>" ;
763 $system_enabled = array_map ( "trim" , explode ( "," , PLUGINS
));
764 $user_enabled = array_map ( "trim" , explode ( "," , get_pref ( "_ENABLED_PLUGINS" )));
766 $tmppluginhost = new PluginHost ( Db
:: get ());
767 $tmppluginhost -> load_all ( $tmppluginhost :: KIND_ALL
, $_SESSION [ "uid" ]);
768 $tmppluginhost -> load_data ( true );
770 foreach ( $tmppluginhost -> get_plugins () as $name => $plugin ) {
771 $about = $plugin -> about ();
773 if ( $about [ 3 ] && strpos ( $name , "example" ) === FALSE ) {
774 if ( in_array ( $name , $system_enabled )) {
775 $checked = "checked='1'" ;
782 print "<td align='center'><input disabled='1'
783 dojoType= \" dijit.form.CheckBox \" $checked
784 type= \" checkbox \" ></td>" ;
786 print "<td> $name </td>" ;
787 print "<td>" . htmlspecialchars ( $about [ 1 ]);
789 print " — <a target= \" _blank \" class= \" visibleLink \"
790 href= \" " . htmlspecialchars ( $about [ 4 ]). " \" >" . __ ( "more info" ). "</a>" ;
793 print "<td>" . htmlspecialchars ( sprintf ( "%.2f" , $about [ 0 ])) . "</td>" ;
794 print "<td>" . htmlspecialchars ( $about [ 2 ]) . "</td>" ;
796 if ( count ( $tmppluginhost -> get_all ( $plugin )) > 0 ) {
797 if ( in_array ( $name , $system_enabled )) {
798 print "<td><a href='#' onclick= \" clearPluginData(' $name ') \"
799 class='visibleLink'>" . __ ( "Clear data" ). "</a></td>" ;
808 print "<tr><td colspan='4'><h3>" . __ ( "User plugins" ). "</h3></td></tr>" ;
810 print "<tr class= \" title \" >
811 <td width= \" 5% \" > </td>
812 <td width='10%'>" . __ ( 'Plugin' ). "</td>
813 <td width=''>" . __ ( 'Description' ). "</td>
814 <td width='5%'>" . __ ( 'Version' ). "</td>
815 <td width='10%'>" . __ ( 'Author' ). "</td></tr>" ;
818 foreach ( $tmppluginhost -> get_plugins () as $name => $plugin ) {
819 $about = $plugin -> about ();
821 if (! $about [ 3 ] && strpos ( $name , "example" ) === FALSE ) {
823 if ( in_array ( $name , $system_enabled )) {
824 $checked = "checked='1'" ;
825 $disabled = "disabled='1'" ;
827 } else if ( in_array ( $name , $user_enabled )) {
828 $checked = "checked='1'" ;
830 $rowclass = "Selected" ;
837 print "<tr class=' $rowclass '>" ;
839 print "<td align='center'><input id='FPCHK- $name ' name='plugins[]' value=' $name ' onclick='toggleSelectRow2(this);'
840 dojoType= \" dijit.form.CheckBox \" $checked $disabled
841 type= \" checkbox \" ></td>" ;
843 print "<td><label for='FPCHK- $name '> $name </label></td>" ;
844 print "<td><label for='FPCHK- $name '>" . htmlspecialchars ( $about [ 1 ]) . "</label>" ;
846 print " — <a target= \" _blank \" class= \" visibleLink \"
847 href= \" " . htmlspecialchars ( $about [ 4 ]). " \" >" . __ ( "more info" ). "</a>" ;
851 print "<td>" . htmlspecialchars ( sprintf ( "%.2f" , $about [ 0 ])) . "</td>" ;
852 print "<td>" . htmlspecialchars ( $about [ 2 ]) . "</td>" ;
854 if ( count ( $tmppluginhost -> get_all ( $plugin )) > 0 ) {
855 if ( in_array ( $name , $system_enabled ) ||
in_array ( $name , $user_enabled )) {
856 print "<td><a href='#' onclick= \" clearPluginData(' $name ') \" class='visibleLink'>" . __ ( "Clear data" ). "</a></td>" ;
870 print "<p><button dojoType= \" dijit.form.Button \" type= \" submit \" >" .
871 __ ( "Enable selected plugins" ). "</button></p>" ;
875 print "</div>" ; #pane
877 PluginHost
:: getInstance ()-> run_hooks ( PluginHost
:: HOOK_PREFS_TAB
,
878 "hook_prefs_tab" , "prefPrefs" );
880 print "</div>" ; #container
883 function toggleAdvanced () {
884 $_SESSION [ "prefs_show_advanced" ] = ! $_SESSION [ "prefs_show_advanced" ];
887 function otpqrcode () {
888 require_once "lib/otphp/vendor/base32.php" ;
889 require_once "lib/otphp/lib/otp.php" ;
890 require_once "lib/otphp/lib/totp.php" ;
891 require_once "lib/phpqrcode/phpqrcode.php" ;
893 $result = $this -> dbh
-> query ( "SELECT login,salt,otp_enabled
895 WHERE id = " . $_SESSION [ "uid" ]);
897 $base32 = new Base32 ();
899 $login = $this -> dbh
-> fetch_result ( $result , 0 , "login" );
900 $otp_enabled = sql_bool_to_bool ( $this -> dbh
-> fetch_result ( $result , 0 , "otp_enabled" ));
903 $secret = $base32 -> encode ( sha1 ( $this -> dbh
-> fetch_result ( $result , 0 , "salt" )));
904 $topt = new \OTPHP\
TOTP ( $secret );
905 print QRcode
:: png ( $topt -> provisioning_uri ( $login ));
909 function otpenable () {
910 require_once "lib/otphp/vendor/base32.php" ;
911 require_once "lib/otphp/lib/otp.php" ;
912 require_once "lib/otphp/lib/totp.php" ;
914 $password = $_REQUEST [ "password" ];
915 $otp = $_REQUEST [ "otp" ];
917 $authenticator = PluginHost
:: getInstance ()-> get_plugin ( $_SESSION [ "auth_module" ]);
919 if ( $authenticator -> check_password ( $_SESSION [ "uid" ], $password )) {
921 $result = $this -> dbh
-> query ( "SELECT salt
923 WHERE id = " . $_SESSION [ "uid" ]);
925 $base32 = new Base32 ();
927 $secret = $base32 -> encode ( sha1 ( $this -> dbh
-> fetch_result ( $result , 0 , "salt" )));
928 $topt = new \OTPHP\
TOTP ( $secret );
930 $otp_check = $topt -> now ();
932 if ( $otp == $otp_check ) {
933 $this -> dbh
-> query ( "UPDATE ttrss_users SET otp_enabled = true WHERE
934 id = " . $_SESSION [ "uid" ]);
938 print "ERROR:" . __ ( "Incorrect one time password" );
941 print "ERROR:" . __ ( "Incorrect password" );
946 function otpdisable () {
947 $password = $this -> dbh
-> escape_string ( $_REQUEST [ "password" ]);
949 $authenticator = PluginHost
:: getInstance ()-> get_plugin ( $_SESSION [ "auth_module" ]);
951 if ( $authenticator -> check_password ( $_SESSION [ "uid" ], $password )) {
953 $this -> dbh
-> query ( "UPDATE ttrss_users SET otp_enabled = false WHERE
954 id = " . $_SESSION [ "uid" ]);
958 print "ERROR: " . __ ( "Incorrect password" );
963 function setplugins () {
964 if ( is_array ( $_REQUEST [ "plugins" ]))
965 $plugins = join ( "," , $_REQUEST [ "plugins" ]);
969 set_pref ( "_ENABLED_PLUGINS" , $plugins );
972 function clearplugindata () {
973 $name = $this -> dbh
-> escape_string ( $_REQUEST [ "name" ]);
975 PluginHost
:: getInstance ()-> clear_data ( PluginHost
:: getInstance ()-> get_plugin ( $name ));
978 function customizeCSS () {
979 $value = get_pref ( "USER_STYLESHEET" );
981 $value = str_replace ( "<br/>" , " \n " , $value );
983 print_notice ( T_sprintf ( "You can override colors, fonts and layout of your currently selected theme with custom CSS declarations here. <a target= \" _blank \" class= \" visibleLink \" href= \" %s\" >This file</a> can be used as a baseline." , "tt-rss.css" ));
985 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" op \" value= \" rpc \" >" ;
986 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" method \" value= \" setpref \" >" ;
987 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" key \" value= \" USER_STYLESHEET \" >" ;
989 print "<table width='100%'><tr><td>" ;
990 print "<textarea dojoType= \" dijit.form.SimpleTextarea \"
991 style='font-size : 12px; width : 100%; height: 200px;'
992 placeHolder='body#ttrssMain { font-size : 14px; };'
993 name='value'> $value </textarea>" ;
994 print "</td></tr></table>" ;
996 print "<div class='dlgButtons'>" ;
997 print "<button dojoType= \" dijit.form.Button \"
998 onclick= \" dijit.byId('cssEditDlg').execute() \" >" . __ ( 'Save' ). "</button> " ;
999 print "<button dojoType= \" dijit.form.Button \"
1000 onclick= \" dijit.byId('cssEditDlg').hide() \" >" . __ ( 'Cancel' ). "</button>" ;
1005 function editPrefProfiles () {
1006 print "<div dojoType= \" dijit.Toolbar \" >" ;
1008 print "<div dojoType= \" dijit.form.DropDownButton \" >" .
1009 "<span>" . __ ( 'Select' ). "</span>" ;
1010 print "<div dojoType= \" dijit.Menu \" style= \" display: none; \" >" ;
1011 print "<div onclick= \" selectTableRows('prefFeedProfileList', 'all') \"
1012 dojoType= \" dijit.MenuItem \" >" . __ ( 'All' ). "</div>" ;
1013 print "<div onclick= \" selectTableRows('prefFeedProfileList', 'none') \"
1014 dojoType= \" dijit.MenuItem \" >" . __ ( 'None' ). "</div>" ;
1015 print "</div></div>" ;
1017 print "<div style= \" float : right \" >" ;
1019 print "<input name= \" newprofile \" dojoType= \" dijit.form.ValidationTextBox \"
1021 <button dojoType= \" dijit.form.Button \"
1022 onclick= \" dijit.byId('profileEditDlg').addProfile() \" >" .
1023 __ ( 'Create profile' ). "</button></div>" ;
1027 $result = $this -> dbh
-> query ( "SELECT title,id FROM ttrss_settings_profiles
1028 WHERE owner_uid = " . $_SESSION [ "uid" ]. " ORDER BY title" );
1030 print "<div class= \" prefProfileHolder \" >" ;
1032 print "<form id= \" profile_edit_form \" onsubmit= \" return false \" >" ;
1034 print "<table width= \" 100% \" class= \" prefFeedProfileList \"
1035 cellspacing= \" 0 \" id= \" prefFeedProfileList \" >" ;
1037 print "<tr class= \" placeholder \" id= \" FCATR-0 \" >" ; #odd
1039 print "<td width='5%' align='center'><input
1041 onclick='toggleSelectRow2(this);'
1042 dojoType= \" dijit.form.CheckBox \"
1043 type= \" checkbox \" ></td>" ;
1045 if (! $_SESSION [ "profile" ]) {
1046 $is_active = __ ( "(active)" );
1051 print "<td><span>" .
1052 __ ( "Default profile" ) . " $is_active </span></td>" ;
1058 while ( $line = $this -> dbh
-> fetch_assoc ( $result )) {
1060 $class = ( $lnum %
2 ) ?
"even" : "odd" ;
1062 $profile_id = $line [ "id" ];
1063 $this_row_id = "id= \" FCATR- $profile_id\" " ;
1065 print "<tr class= \" placeholder \" $this_row_id >" ;
1067 $edit_title = htmlspecialchars ( $line [ "title" ]);
1069 print "<td width='5%' align='center'><input
1070 onclick='toggleSelectRow2(this);'
1071 id='FCATC- $profile_id '
1072 dojoType= \" dijit.form.CheckBox \"
1073 type= \" checkbox \" ></td>" ;
1075 if ( $_SESSION [ "profile" ] == $line [ "id" ]) {
1076 $is_active = __ ( "(active)" );
1081 print "<td><span dojoType= \" dijit.InlineEditBox \"
1082 width= \" 300px \" autoSave= \" false \"
1083 profile-id= \" $profile_id\" >" . $edit_title .
1084 "<script type= \" dojo/method \" event= \" onChange \" args= \" item \" >
1088 content: {op: 'rpc', method: 'saveprofile',
1090 id: this.srcNodeRef.getAttribute('profile-id')},
1091 load: function(response) {
1092 elem.attr('value', response);
1096 </span> $is_active </td>" ;
1107 print "<div class='dlgButtons'>
1108 <div style='float : left'>
1109 <button dojoType= \" dijit.form.Button \" onclick= \" dijit.byId('profileEditDlg').removeSelected() \" >" .
1110 __ ( 'Remove selected profiles' ). "</button>
1111 <button dojoType= \" dijit.form.Button \" onclick= \" dijit.byId('profileEditDlg').activateProfile() \" >" .
1112 __ ( 'Activate profile' ). "</button>
1115 print "<button dojoType= \" dijit.form.Button \" onclick= \" dijit.byId('profileEditDlg').hide() \" >" .
1116 __ ( 'Close this window' ). "</button>" ;
1121 private function getShortDesc ( $pref_name ) {
1122 if ( isset ( $this -> pref_help
[ $pref_name ])) {
1123 return $this -> pref_help
[ $pref_name ][ 0 ];
1128 private function getHelpText ( $pref_name ) {
1129 if ( isset ( $this -> pref_help
[ $pref_name ])) {
1130 return $this -> pref_help
[ $pref_name ][ 1 ];
1135 private function getSectionName ( $id ) {
1136 if ( isset ( $this -> pref_sections
[ $id ])) {
1137 return $this -> pref_sections
[ $id ];