<?php
/**
* This file implements the GeneralSettings class, which handles Name/Value pairs.
*
* This file is part of the evoCore framework - {@link http://evocore.net/}
* See also {@link https://github.com/b2evolution/b2evolution}.
*
* @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
*
* @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
* Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
*
* @package evocore
*/
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
load_class( 'settings/model/_abstractsettings.class.php', 'AbstractSettings' );
/**
* Class to handle the global settings.
*
* @package evocore
*/
class GeneralSettings extends AbstractSettings
{
/**
* The default settings to use, when a setting is not given
* in the database.
*
* @todo Allow overriding from /conf/_config_TEST.php?
* @access protected
* @var array
*/
var $_defaults = array(
'admin_skin' => 'bootstrap',
'antispam_last_update' => '2000-01-01 00:00:00',
'antispam_threshold_publish' => '-90',
'antispam_threshold_delete' => '100', // do not delete by default!
'antispam_block_contact_form' => '1', // Block spam words on contact form by antispam blacklist
'antispam_block_spam_referers' => '0', // By default, let spam referers go in silently (just don't log them). This is in case the blacklist is too paranoid (social media, etc.)
'antispam_report_to_central' => '1',
'evonet_last_update' => '1196000000', // just around the time we implemented this ;)
'evonet_last_attempt' => '1196000000', // just around the time we implemented this ;)
'log_public_hits' => '1',
'log_admin_hits' => '0',
'log_spam_hits' => '0',
'auto_prune_stats_mode' => 'page', // 'page' is the safest mode for average installs (may be "off", "page" or "cron")
'auto_prune_stats' => '15', // days (T_hitlog and T_sessions)
'auto_empty_trash' => '15', // days (How many days to keep recycled comments)
'cleanup_jobs_threshold' => 30, // days (Cleanup successfully finished scheduled jobs threshold)
'cleanup_jobs_threshold_failed' => 90, // days (Cleanup failed scheduled jobs threshold)
'cleanup_email_logs_threshold' => 59616000, // seconds (Cleanup email logs threshold)
'activate_account_reminder_threshold' => 86400, // seconds (Account activation reminder threshold)
'activate_account_reminder_config' => '86400,129600,345600,604800,0,0', // seconds (Account activation reminder settings), Defaults: one day, 1.5 days, 4 days, 7 days, "Don't send", "Don't delete"
'inactive_account_reminder_threshold' => 31536000, // seconds (Inactive account reminder threshold)
'comment_moderation_reminder_threshold' => 86400, // seconds (Comment moderation reminder threshold)
'post_moderation_reminder_threshold' => 86400, // seconds (Post moderation reminder threshold)
'unread_message_reminder_threshold' => 86400, // seconds (Unread private messages reminder threshold)
'unread_message_reminder_delay' => '10:3,30:6,90:15,180:30,365:60,730:120',// Unread message reminder is sent in every y days in case when a user last logged in date is below x days.
/* The default values are in x:y format:
less than 10 days -> 3 days spacing
10 to 30 days -> 6 days spacing
30 to 90 days -> 15 days spacing
90 to 180 days -> 30 days spacing
180 to 365 days -> 60 days spacing
365 to 730 days -> 120 days spacing
more => "The user has not logged in for x days, so we will not send him notifications any more"*/
// Manage email address statuses:
'manage_email_statuses_min_delay' => 259200, // seconds, Minimum 3 days delay since last error
'manage_email_statuses_min_sends' => 3, // Number of minimum sends since last error
'email_service' => 'mail', // Preferred email service: 'mail', 'smtp'
'force_email_sending' => '0', // Force email sending
'sendmail_params' => 'return', // Sendmail additional params: 'return', 'from', 'custom'
'outbound_notifications_mode' => 'immediate', // 'immediate' is the safest mode for average installs (may be "off", "immediate" or "cron")
'notification_sender_email' => '', // notification emails will be sent from this email. The real default value is set in the constructor.
'notification_return_path' => '', // erroneous emails will be sent to this email address. The real default value is set in the constructor.
'notification_sender_name' => 'b2evo mailer', // notification emails will be sent with this sender name
'notification_short_name' => 'This site', // notification emails will use this as short site name
'notification_long_name' => '', // notification emails will use this as long site name
'notification_logo_file_ID' => '', // notification emails will use this as url to site logo
'social_media_image_file_ID' => '', // social media tags will use this as the default as image
'email_campaign_send_mode' => 'immediate', // Sending mode for campaign
'email_campaign_chunk_size' => 50, // Chunk size of emails to send a campaign at a time
'email_campaign_cron_repeat' => 300, // 5 minutes: Delay between chunks on scheduled campaign job runs
'email_campaign_cron_limited' => 21600, // 6 hours: Delay between chunks on scheduled campaign job runs in case all remaining recipients have reached max # of emails for the current day
'email_campaign_max_domain' => 20, // Max emails to same domain
'fm_enable_create_dir' => '1',
'fm_enable_create_file' => '1',
'fm_enable_roots_blog' => '1',
'fm_enable_roots_user' => '1',
'fm_enable_roots_shared' => '1',
'fm_enable_roots_skins' => '1',
'fm_enable_roots_plugins' => '1',
'fm_showtypes' => '0',
'fm_showfsperms' => '0',
'fm_default_chmod_file' => '664',
'fm_default_chmod_dir' => '775',
// Image options
'exif_orientation' => '1',
'fm_resize_enable' => '0',
'fm_resize_width' => '2880',
'fm_resize_height' => '1800',
'fm_resize_quality' => '95',
'newusers_canregister' => 'no',
'registration_is_public' => '1',
'quick_registration' => '0',
'newusers_mustvalidate' => '1',
'newusers_revalidate_emailchg' => '1',
'validation_process' => 'easy',
'activate_requests_limit' => '300', // Only one activation email can be sent to the same email address in the given interval ( value is in seconds )
'newusers_findcomments' => '1',
'after_email_validation' => 'return_to_original', // where to redirect after account activation. Values: return_to_original, or the previously set specific url
'after_registration' => 'return_to_original', // where to redirect after new user registration. Values: 'return_to_original' redirect_to url, or 'slug', or return to the previously set specific url
'after_registration_slug' => '', // Slug value for after_registration == 'slug'
'newusers_level' => '1',
'registration_master_template' => 'registration_master_standard',
'registration_after_quick' => 'regform',
'registration_no_username' => 'firstname',
'pass_after_quick_reg' => '1',
// Default user settings
'def_enable_PM' => '1',
'def_enable_email' => '0',
'def_notify_messages' => '1',
'def_notify_unread_messages' => '1',
'def_notify_comment_mentioned' => '1',
'def_notify_published_comments' => '1',
'def_notify_comment_moderation' => '1',
'def_notify_edit_cmt_moderation' => '1',
'def_notify_spam_cmt_moderation' => '1',
'def_notify_meta_comment_mentioned' => '1',
'def_notify_meta_comments' => '1',
'def_notify_post_mentioned' => '1',
'def_notify_post_moderation' => '1',
'def_notify_edit_pst_moderation' => '1',
'def_notify_post_proposed' => '1',
'def_notify_post_assignment' => '1',
'def_newsletters' => '1',
'def_notification_email_limit' => '3',
'def_newsletter_limit' => '3',
'allow_avatars' => 1,
'min_picture_size' => 160, // minimum profile picture dimensions in pixels (width and height)
'allow_html_message' => 0, // Allow HTML in messages
// Users list settings:
'userlist_default_filters' => 'name_email,country',
// Welcome private message
'welcomepm_enabled' => 0,
'welcomepm_notag' => 0,
'welcomepm_from' => 'admin', // User login
'welcomepm_title' => 'Welcome to our community!',
'welcomepm_message' => '',
// Info message to reporters after account deletion
'reportpm_enabled' => 0,
'reportpm_from' => 'admin', // User login
'reportpm_title' => 'The user account $reportedlogin$ that you have reported has just been deleted.',
'reportpm_message' => "You have reported the user account \$reportedlogin\$.\n\nThis is to inform you that we have just deleted this account.\n\nThank you for your help in keeping this site a friendly place!",
'regexp_filename' => '^[a-zA-Z0-9\-_. ]+$', // TODO: accept (spaces and) special chars / do full testing on this
'regexp_dirname' => '^[a-zA-Z0-9\-_]+$', // TODO: accept spaces and special chars / do full testing on this
'reloadpage_timeout' => '300',
'time_difference' => '0',
'timeout_sessions' => '604800', // seconds (604800 == 7 days)
'timeout_online' => '1200', // seconds (1200 == 20 minutes)
'upload_enabled' => '1',
'upload_maxkb' => '32000', // 32 MB
'evocache_foldername' => '.evocache',
'blogs_order_by' => 'order', // blogs order in backoffice menu and other places
'blogs_order_dir' => 'ASC', // blogs order direction in backoffice menu and other places
'user_minpwdlen' => '5',
'js_passwd_hashing' => '1', // Use JS password hashing by default
'passwd_special' => '0', // Do not require a special character in password by default
'strict_logins' => 1, // Allow only plain ACSII characters in user login
'allow_moving_chapters' => '0', // Do not allow moving chapters by default
'cross_posting' => 0, // Allow additional categories from other blogs
'cross_posting_blog' => 0, // Allow to choose main category from another blog
'redirect_moved_posts' => 1, // Allow to redirect moved posts link to the correct blog
'always_match_slug' => 1, // Allow to redirect to correct Collection if an Item Slug was found in any URL
'redirect_tinyurl' => 1, // Allow 301 redirect from Tiny URLs to canonical URL
'subscribe_new_blogs' => 'public', // Subscribing to new blogs: 'page', 'public', 'all'
// Site settings
'system_lock' => 0,
'site_code' => 'b2evo',
'site_color' => '#ff8c0f',
'site_skins_enabled' => 1, // Enables a sitewide header and footer
'info_blog_ID' => 0, // Blog for info pages
'general_cache_enabled' => 0,
'newblog_cache_enabled' => 0,
'newblog_cache_enabled_widget' => 0,
// Default Skins for New Collections:
'def_normal_skin_ID' => '1', // Default normal skin ID
'def_mobile_skin_ID' => NULL, // NULL means same as normal skin
'def_tablet_skin_ID' => NULL, // NULL means same as normal skin
'def_alt_skin_ID' => NULL, // NULL means same as normal skin
// Default URL for New Collections:
'coll_access_type' => 'extrapath',
// Post by Email
'eblog_enabled' => 0,
'eblog_method' => 'imap',
'eblog_encrypt' => 'none',
'eblog_server_port' => 143,
'eblog_default_category' => 1,
'eblog_add_imgtag' => 0,
'eblog_body_terminator' => '___',
'eblog_subject_prefix' => 'blog:',
'eblog_html_tag_limit' => 1,
'eblog_delete_emails' => 1,
'eblog_renderers' => array('default'),
// Returned Emails
'repath_enabled' => 0,
'repath_method' => 'imap',
'repath_encrypt' => 'none',
'repath_server_port' => 143,
'repath_imap_folder' => 'INBOX',
'repath_subject' => 'Returned mail:
Mail delivery failed:
Undelivered Mail Returned to Sender
Delivery Status Notification (Failure)
delayed 24 hours
delayed 48 hours
delayed 72 hours
failure notice
Undeliverable:
Quarantine
Карантин',
'repath_body_terminator' => '---------- Forwarded message ----------
------ This is a copy of the message, including all the headers. ------
----- Transcript of session follows -----
Received: from
--- Below this line is a copy of the message.',
'repath_errtype' => 'C ^101
C ^111
T ^421
T ^431
T ^432
T ^441
T ^450
T ^452
C ^500
C ^501
C ^502
C ^503
C ^504
T ^512
T ^523
P ^510
P ^511
S ^541
S ^550[ \-]5\.4\.1
P ^550([ \-]5\.1\.1)?
C ^551
S DNSBL
S Blacklist
S reputation
S client host .* blocked
S verification failed
S verify failed
S policy reasons
S not authorized
S refused
S spammer
S DYN:T1
S (CON:B1)
T currently suspended
T temporarily disabled
T unrouteable address
T Mailbox is inactive
T over quota
T account has been disabled
T timeout exceeded
T quota exceeded
T Delivery attempts will continue
T quarantine
T карантин
P is unavailable
P not available
P user doesn\'t have a .+ account
P invalid recipient
P does not like recipient
P not our customer
P no such user
P addressee unknown
P address rejected
P permanent delivery error
P permanent error
P mailbox unavailable
P no mailbox here by that name
P mailbox has been blocked
C relay denied
C relaying denied
C message size exceeds',
'general_xmlrpc' => 1,
'xmlrpc_default_title' => '', // default title for posts created throgh blogger api
'nickname_editing' => 'edited-user', // "never" - Never allow; "default-no" - Let users decide, default to "no" for new users; "default-yes" - Let users decide, default to "yes" for new users; "always" - Always allow
'firstname_editing' => 'edited-user', // "edited-user" - Can be edited by user; "edited-admin" - Can be edited by admins only, "hidden" - Hidden for all
'lastname_editing' => 'edited-user', // "edited-user" - Can be edited by user; "edited-admin" - Can be edited by admins only, "hidden" - Hidden for all
'location_country' => 'optional', // Editing mode of country for user: "optional" | "required" | "hidden"
'location_region' => 'optional', // Editing mode of region for user: "optional" | "required" | "hidden"
'location_subregion' => 'optional', // Editing mode of subregion for user: "optional" | "required" | "hidden"
'location_city' => 'optional', // Editing mode of city for user: "optional" | "required" | "hidden"
'birthday_year' => 'optional', // Editing mode of birthday year for user: "optional" | "required" | "hidden"
'birthday_month' => 'optional', // Editing mode of birthday month for user: "optional" | "required" | "hidden"
'birthday_day' => 'optional', // Editing mode of birthday day for user: "optional" | "required" | "hidden"
'self_selected_age_group' => 'hidden', // Editing mode of self-selected age group for user: "optional" | "required" | "hidden"
'minimum_age' => '13', // Minimum age for user forms
'multiple_sessions' => 'userset_default_no', // multiple sessions settings -- overriden for demo mode in contructor
'emails_msgform' => 'adminset', // Receiving emails through a message form is allowed: "never" | "adminset" | "userset"
// Display options:
'use_gravatar' => 1, // Use gravatar if a user has not uploaded a profile picture
'default_gravatar' => 'b2evo', // Gravatar type: 'b2evo', '', 'identicon', 'monsterid', 'wavatar', 'retro'
'username_display' => 'login', // What to display as user name: 'login' - Usernames/Logins or 'name' - 'Friendly names'
'bubbletip' => 1, // Display bubletips in the Back-office
'bubbletip_size_admin' => 'fit-160x160', // Avatar size in the bubbletip in the Back-office
'bubbletip_size_front' => 'fit-160x160', // Avatar size in the bubbletip in the Front-office
'bubbletip_anonymous' => 1, // Display bubbletips in Front-office for anonymous users
'bubbletip_size_anonymous' => 'fit-160x160-blur-18', // Avatar size in the bubbletip in the Front-office for anonymous users
'bubbletip_overlay' => "Log in to\r\nsee this\r\nimage",// Overlay text on the profile image for anonymous users
'allow_anonymous_user_list' => 1, // Allow anonymous users to see user list (disp=users)
'allow_anonymous_user_profiles' => 0, // Allow anonymous users to see the user display ( disp=user )
'allow_anonymous_user_level_min' => 0, // Min value of user group level to display for anonymous users
'allow_anonymous_user_level_max' => 10, // Max value of user group level to display for anonymous users
'user_url_loggedin' => 'page', // Link an user url to 'page' or 'url' for logged-in users
'user_url_anonymous' => 'page', // Link an user url to 'page' or 'url' for anonymous users
// Account closing options:
'account_close_enabled' => 1, // Allow users to close their account themselves
'account_close_intro' => "We are sorry to see you leave.\n\nWe value your feedback. Please be so kind and tell us in a few words why you are leaving us. This will help us to improve the site for the future.",
'account_close_reasons' => "I don't need this account any more.\nI do not like this site.\nI am getting spam from this site.", // Reasons to close an account, separated by new line
'account_close_byemsg' => 'Your account has now been closed. If you ever want to log in again, you will need to create a new account.',
// Back-end settings, these can't be modified by the users:
'last_invalidation_timestamp' => 0,
// Cron job settings:
// Max emails to send:
'cjob_maxemail_send-non-activated-account-reminders' => 50,
'cjob_maxemail_execute-automations' => 50,
'cjob_maxemail_send-inactive-account-reminders' => 50,
'cjob_maxemail_send-unread-messages-reminders' => 50,
// Do not notify IMAP errors before X consecutive errors:
'cjob_imap_error_process-return-path-inbox' => 3,
'cjob_imap_error_create-post-by-email' => 3,
);
/**
* Constructor.
*
* This loads the general settings and checks db_version.
*
* It will also turn off error-reporting/halting of the {@link $DB DB object}
* temporarily to present a more decent error message if tables do not exist yet.
*
* Because the {@link $DB DB object} itself creates a connection when it gets
* created "Error selecting database" occurs before we can check for it here.
* @param boolean TRUE to check current DB version
*/
function __construct( $check_version = true )
{
global $new_db_version, $DB, $demo_mode, $instance_name, $basehost;
$save_DB_show_errors = $DB->show_errors;
$save_DB_halt_on_error = $DB->halt_on_error;
$DB->halt_on_error = false;
$DB->show_errors = false;
// Init through the abstract constructor. This should be the first DB connection.
parent::__construct( 'T_settings', array( 'set_name' ), 'set_value', 0 );
// check DB version:
if( $check_version && $this->get( 'db_version' ) != $new_db_version )
{ // Database is not up to date:
if( $DB->last_error )
{
$error_title = 'The database is not installed yet!';
$error_message = '<p>MySQL error:</p>'.$DB->last_error;
}
else
{
$error_title = 'Database schema is not up to date!';
$error_message = '<p>Database schema is not up to date!</p>'
.'<p>You have schema version «'.(integer)$this->get( 'db_version' ).'», '
.'but we would need «'.(integer)$new_db_version.'».</p>';
}
global $adminskins_path;
require $adminskins_path.'conf_error.main.php'; // error & exit
}
$DB->halt_on_error = $save_DB_halt_on_error;
$DB->show_errors = $save_DB_show_errors;
if( $demo_mode )
{ // Demo mode requires to allow multiple concurrent sessions:
$this->_defaults['multiple_sessions'] = 'always';
}
// set those defaults which needs some global variables
$this->_defaults['notification_sender_email'] = $instance_name.'-noreply@'.$basehost;
$this->_defaults['notification_return_path'] = $instance_name.'-return@'.$basehost;
}
/**
* Get a 32-byte string that can be used as salt for public keys.
*
* @return string
*/
function get_public_key_salt()
{
$public_key_salt = $this->get( 'public_key_salt' );
if( empty($public_key_salt) )
{
$public_key_salt = generate_random_key(32);
$this->set( 'public_key_salt', $public_key_salt );
$this->dbupdate();
}
return $public_key_salt;
}
/**
* Get a member param by its name
*
* @param string Name of parameter
* @param boolean true to return param's real value
* @return mixed Value of parameter
*/
function get( $parname, $real_value = false )
{
if( $real_value )
{
return parent::getx( $parname );
}
switch($parname)
{
case 'normal_skin_ID':
$result = parent::getx( $parname );
if( $result === NULL )
{ // Try to get default from the global settings:
$result = $this->get( 'def_'.$parname );
}
return $result;
case 'mobile_skin_ID':
case 'tablet_skin_ID':
case 'alt_skin_ID':
$result = parent::getx( $parname );
if( $result === NULL )
{ // Try to get default from the global settings:
$result = $this->get( 'def_'.$parname );
}
if( ( $result === '0' ) && ! $real_value )
{ // 0 value means that use the same as normal case:
$result = $this->get( 'normal_skin_ID' );
}
return $result;
case 'allow_avatars':
return ( parent::getx( $parname ) && isset($GLOBALS['files_Module']) );
break;
case 'upload_enabled':
return ( parent::getx( $parname ) && isset($GLOBALS['files_Module']) );
break;
case 'activate_account_reminder_config':
$value = parent::getx( $parname );
if( ! is_array( $value ) )
{ // Convert the setting value to array because it is used as array but stored as values separated by comma:
$value = explode( ',', $value );
}
return $value;
case 'unread_message_reminder_delay':
$value = parent::getx( $parname );
if( ! is_array( $value ) )
{ // Convert the setting value to array because it is used as array but stored as values separated by comma and colon:
$values = array();
if( preg_match_all( '/(\d+):(\d+)(,|$)/', $value, $matches ) )
{
foreach( $matches[1] as $m => $v )
{
$values[ intval( $v ) ] = intval( $matches[2][ $m ] );
}
}
$value = $values;
}
return $value;
case 'email_service':
global $email_send_allow_php_mail;
// Force to use SMTP gateway when php mail sending is disabled by config:
return $email_send_allow_php_mail ? parent::getx( $parname ) : 'smtp';
case 'force_email_sending':
global $email_send_allow_php_mail;
// Don't force to use secondary email service when php mail sending is disabled by config:
return $email_send_allow_php_mail ? parent::getx( $parname ) : 0;
default:
$value = parent::getx( $parname );
if( $value === NULL && strpos( $parname, 'cjob_timeout_' ) === 0 )
{ // Set default 10 minutes for max execution time of each cron job type:
$value = 600;
}
return $value;
}
}
/**
* Temporarily sets a setting ({@link dbupdate()} writes it to DB).
*
* @param string Name of parameter
* @param mixed Value
* @return boolean true, if the value has been set, false if it has not changed.
*/
function set( $setting, $value )
{
// Limit value with max possible length:
$value = utf8_substr( $value, 0, 10000 );
return parent::setx( $setting, $value );
}
}
?>