.
*/
/**
* Preferences panel user interface and interaction.
*
* @package Admin\Prefs
*/
if (!defined('txpinterface')) {
die('txpinterface is undefined.');
}
if ($event == 'prefs') {
require_privs('prefs');
bouncer($step, array(
'prefs_save' => true,
'prefs_list' => false,
));
switch (strtolower($step)) {
case '':
case 'prefs_list':
prefs_list();
break;
case 'prefs_save':
prefs_save();
break;
}
}
/**
* Commits prefs to the database.
*/
function prefs_save()
{
global $prefs, $gmtoffset, $is_dst, $auto_dst, $timezone_key, $txp_user;
// Update custom fields count from database schema and cache it as a hidden pref.
// TODO: move this when custom fields are refactored.
$max_custom_fields = count(preg_grep('/^custom_\d+/', getThings("DESCRIBE ".safe_pfx('textpattern'))));
set_pref('max_custom_fields', $max_custom_fields, 'publish', 2);
$sql = array();
$sql[] = "prefs_id = 1 AND event != '' AND type IN (".PREF_CORE.", ".PREF_PLUGIN.", ".PREF_HIDDEN.")";
$sql[] = "(user_name = '' OR (user_name = '".doSlash($txp_user)."' AND name NOT IN (
SELECT name FROM ".safe_pfx('txp_prefs')." WHERE user_name = ''
)))";
if (!get_pref('use_comments', 1, 1)) {
$sql[] = "event != 'comments'";
}
$prefnames = safe_rows_start(
"name, event, user_name, val",
'txp_prefs',
join(" AND ", $sql)
);
$post = stripPost();
if (isset($post['tempdir']) && empty($post['tempdir'])) {
$post['tempdir'] = find_temp_dir();
}
if (!empty($post['file_max_upload_size'])) {
$post['file_max_upload_size'] = real_max_upload_size($post['file_max_upload_size']);
}
if (isset($post['auto_dst'])) {
$prefs['auto_dst'] = $auto_dst = $post['auto_dst'];
if (isset($post['is_dst']) && !$post['auto_dst']) {
$is_dst = $post['is_dst'];
}
}
// Forge $gmtoffset and $is_dst from $timezone_key if present.
if (!empty($post['timezone_key'])) {
$key = $post['timezone_key'];
$tzd = Txp::get('\Textpattern\Date\Timezone')->getTimeZones();
if (isset($tzd[$key])) {
$prefs['timezone_key'] = $timezone_key = $key;
if ($auto_dst) {
$post['gmtoffset'] = $prefs['gmtoffset'] = $gmtoffset = $tzd[$key]['offset'];
$post['is_dst'] = $prefs['is_dst'] = $is_dst = (int)Txp::get('\Textpattern\Date\Timezone')->isDst(null, $key);
}
}
}
if (isset($post['siteurl'])) {
$post['siteurl'] = preg_replace('#^https?://#', '', rtrim($post['siteurl'], '/ '));
}
while ($a = nextRow($prefnames)) {
extract($a);
if (!isset($post[$name]) || !has_privs('prefs.'.$event)) {
continue;
}
if (is_array($post[$name])) {
$post[$name] = implode(',', array_diff($post[$name], array('')));
}
if ($name === 'logging' && $post[$name] === 'none' && $post[$name] !== $val) {
safe_truncate('txp_log');
}
if ($name === 'expire_logs_after' && (int) $post[$name] !== (int) $val) {
safe_delete('txp_log', "time < DATE_SUB(NOW(), INTERVAL ".intval($post[$name])." DAY)");
}
update_pref($name, (string) $post[$name], null, null, null, null, (string) $user_name);
}
update_lastmod('preferences_saved');
prefs_list(gTxt('preferences_saved'));
}
/**
* Renders the list of preferences.
*
* Plugins may add their own prefs, for example by using plugin lifecycle events
* or raising a (pre) callback on event=admin / step=prefs_list so they are
* installed or updated when accessing the Preferences panel. Access to the
* prefs can be controlled by using add_privs() on 'prefs.your-prefs-event-name'.
*
* @param string $message The feedback / error string to display
*/
function prefs_list($message = '')
{
global $prefs, $txp_user;
extract($prefs);
pagetop(gTxt('tab_preferences'), $message);
$locale = setlocale(LC_ALL, $locale);
echo n.'
'.
script_js('var selectedTab = "' . $tabActive . '";');
}
/**
* Calls a core or custom function to render a preference input widget.
*
* @param string $func Callable in a string presentation
* @param string $name HTML name/id of the input control
* @param string $val Initial (or current) value of the input control
* @param int $size Size of the input control (width or depth, dependent on control)
* @return string HTML
*/
function pref_func($func, $name, $val, $size = '')
{
if ($func != 'func' && is_callable('pref_'.$func)) {
$func = 'pref_'.$func;
} else {
$string = new \Textpattern\Type\StringType($func);
$func = $string->toCallback();
if (!is_callable($func)) {
$func = 'text_input';
}
}
return call_user_func($func, $name, $val, $size);
}
/**
* Renders a HTML <input> element.
*
* @param string $name HTML name and id of the text box
* @param string $val Initial (or current) content of the text box
* @param int $size Width of the textbox. Options are INPUT_MEDIUM | INPUT_SMALL | INPUT_XSMALL
* @return string HTML
*/
function text_input($name, $val, $size = 0)
{
$class = '';
switch ($size) {
case INPUT_MEDIUM:
$class = 'input-medium';
break;
case INPUT_SMALL:
$class = 'input-small';
break;
case INPUT_XSMALL:
$class = 'input-xsmall';
break;
}
return fInput('text', $name, $val, $class, '', '', $size, '', $name);
}
/**
* Renders a HTML <textarea> element.
*
* @param string $name HTML name of the textarea
* @param string $val Initial (or current) content of the textarea
* @param int $size Number of rows the textarea has
* @return string HTML
*/
function pref_longtext_input($name, $val, $size = '')
{
return text_area($name, '', '', $val, '', $size);
}
/**
* Renders a HTML <select> list of cities for timezone selection.
*
* Can be altered by plugins via the 'prefs_ui > gmtoffset'
* pluggable UI callback event.
*
* @param string $name HTML name of the list
* @param string $val Initial (or current) selected option
* @return string HTML
*/
function gmtoffset_select($name, $val)
{
// Fetch *hidden* pref
$key = get_pref('timezone_key', '', true);
if ($key === '') {
$key = (string) Txp::get('\Textpattern\Date\Timezone')->getTimezone();
}
$tz = new timezone;
$ui = $tz->selectInput('timezone_key', $key, false, '', 'gmtoffset');
return pluggable_ui('prefs_ui', 'gmtoffset', $ui, $name, $val);
}
/**
* Renders a HTML choice for whether Daylight Savings Time is in effect.
*
* Can be altered by plugins via the 'prefs_ui > is_dst'
* pluggable UI callback event.
*
* @param string $name HTML name of the widget
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function is_dst($name, $val)
{
global $timezone_key, $auto_dst;
if ($auto_dst) {
$val = (int)Txp::get('\Textpattern\Date\Timezone')->isDst(null, $timezone_key);
}
$ui = yesnoRadio($name, $val).
script_js(<< gTxt('all_hits'),
'refer' => gTxt('referrers_only'),
'none' => gTxt('none'),
);
return selectInput($name, $vals, $val, '', '', $name);
}
/**
* Renders a HTML <select> list of supported permanent link URL formats.
*
* @param string $name HTML name and id of the list
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function permlinkmodes($name, $val)
{
$vals = array(
'messy' => gTxt('messy'),
'id_title' => gTxt('id_title'),
'section_id_title' => gTxt('section_id_title'),
'year_month_day_title' => gTxt('year_month_day_title'),
'section_title' => gTxt('section_title'),
'title_only' => gTxt('title_only'),
//'category_subcategory' => gTxt('category_subcategory'),
);
return selectInput($name, $vals, $val, '', '', $name);
}
/**
* Renders a HTML choice of comment popup modes.
*
* @param string $name HTML name and id of the widget
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function commentmode($name, $val)
{
$vals = array(
'0' => gTxt('nopopup'),
'1' => gTxt('popup'),
);
return selectInput($name, $vals, $val, '', '', $name);
}
/**
* Renders a HTML <select> list of comment popup modes.
*
* Can be altered by plugins via the 'prefs_ui > weeks'
* pluggable UI callback event.
*
* @param string $name HTML name and id of the widget
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function weeks($name, $val)
{
$weeks = gTxt('weeks');
$vals = array(
'0' => gTxt('never'),
7 => '1 '.gTxt('week'),
14 => '2 '.$weeks,
21 => '3 '.$weeks,
28 => '4 '.$weeks,
35 => '5 '.$weeks,
42 => '6 '.$weeks,
);
return pluggable_ui('prefs_ui', 'weeks', selectInput($name, $vals, $val, '', '', $name), $name, $val);
}
/**
* Renders a HTML <select> list of available ways to display the date.
*
* Can be altered by plugins via the 'prefs_ui > dateformats'
* pluggable UI callback event.
*
* @param string $name HTML name and id of the widget
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function dateformats($name, $val)
{
$dayname = '%A';
$dayshort = '%a';
$daynum = is_numeric(@strftime('%e')) ? '%e' : '%d';
$daynumlead = '%d';
$daynumord = is_numeric(substr(trim(@strftime('%Oe')), 0, 1)) ? '%Oe' : $daynum;
$monthname = '%B';
$monthshort = '%b';
$monthnum = '%m';
$year = '%Y';
$yearshort = '%y';
$time24 = '%H:%M';
$time12 = @strftime('%p') ? '%I:%M %p' : $time24;
$date = @strftime('%x') ? '%x' : '%Y-%m-%d';
$formats = array(
"$monthshort $daynumord, $time12",
"$daynum.$monthnum.$yearshort",
"$daynumord $monthname, $time12",
"$yearshort.$monthnum.$daynumlead, $time12",
"$dayshort $monthshort $daynumord, $time12",
"$dayname $monthname $daynumord, $year",
"$monthshort $daynumord",
"$daynumord $monthname $yearshort",
"$daynumord $monthnum $year - $time24",
"$daynumord $monthname $year",
"$daynumord $monthname $year, $time24",
"$daynumord. $monthname $year",
"$daynumord. $monthname $year, $time24",
"$year-$monthnum-$daynumlead",
"$year-$daynumlead-$monthnum",
"$date $time12",
"$date",
"$time24",
"$time12",
"$year-$monthnum-$daynumlead $time24",
);
$ts = time();
$vals = array();
foreach ($formats as $f) {
if ($d = safe_strftime($f, $ts)) {
$vals[$f] = $d;
}
}
$vals['since'] = gTxt('hours_days_ago');
return pluggable_ui('prefs_ui', 'dateformats', selectInput($name, array_unique($vals), $val, '', '', $name), compact('vals', 'name', 'val', 'ts'));
}
/**
* Renders a HTML <select> list of content permlink options.
*
* @param string $name HTML name and id of the widget
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function permlink_format($name, $val)
{
$vals = array(
'0' => gTxt('permlink_intercapped'),
'1' => gTxt('permlink_hyphenated'),
);
return selectInput($name, $vals, $val, '', '', $name);
}
/**
* Renders a HTML <select> list of site production status.
*
* @param string $name HTML name and id of the widget
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function prod_levels($name, $val)
{
$vals = array(
'debug' => gTxt('production_debug'),
'testing' => gTxt('production_test'),
'live' => gTxt('production_live'),
);
return selectInput($name, $vals, $val, '', '', $name);
}
/**
* Renders a HTML <select> list of available panels to show immediately
* after login.
*
* @param string $name HTML name of the widget
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function default_event($name, $val)
{
$vals = areas();
$out = array();
foreach ($vals as $a => $b) {
if (count($b) > 0) {
$out[] = n.'';
}
}
return n.'';
}
/**
* Renders a HTML <select> list of sendmail options.
*
* @param string $name HTML name and id of the widget
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function commentsendmail($name, $val)
{
$vals = array(
'1' => gTxt('all'),
'0' => gTxt('none'),
'2' => gTxt('ham'),
);
return selectInput($name, $vals, $val, '', '', $name);
}
/**
* Renders a HTML custom field.
*
* Can be altered by plugins via the 'prefs_ui > custom_set'
* pluggable UI callback event.
*
* @param string $name HTML name of the widget
* @param string $val Initial (or current) content
* @return string HTML
* @todo deprecate or move this when CFs are migrated to the meta store
*/
function custom_set($name, $val)
{
return pluggable_ui('prefs_ui', 'custom_set', text_input($name, $val, INPUT_REGULAR), $name, $val);
}
/**
* Renders a HTML <select> list of installed admin-side themes.
*
* Can be altered by plugins via the 'prefs_ui > theme_name'
* pluggable UI callback event.
*
* @param string $name HTML name and id of the widget
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function themename($name, $val)
{
$themes = \Textpattern\Admin\Theme::names();
foreach ($themes as $t) {
$theme = \Textpattern\Admin\Theme::factory($t);
if ($theme) {
$m = $theme->manifest();
$title = empty($m['title']) ? ucwords($theme->name) : $m['title'];
$vals[$t] = $title;
unset($theme);
}
}
asort($vals, SORT_STRING);
return pluggable_ui('prefs_ui', 'theme_name',
selectInput($name, $vals, $val, '', '', $name));
}
/**
* Renders a HTML <select> list of available public site markup schemes to
* adhere to.
*
* @param string $name HTML name and id of the widget
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function doctypes($name, $val)
{
$vals = array(
'xhtml' => gTxt('XHTML'),
'html5' => gTxt('HTML5'),
);
return selectInput($name, $vals, $val, '', '', $name);
}
/**
* Renders a HTML <select> list of available publishing
* status values.
*
* @param string $name HTML name and id of the widget
* @param string $val Initial (or current) selected item
* @return string HTML
*/
function defaultPublishStatus($name, $val)
{
return selectInput($name, status_list(), $val, '', '', $name);
}
/**
* Gets the maximum allowed file upload size.
*
* Computes the maximum acceptable file size to the application if the
* user-selected value is larger than the maximum allowed by the current PHP
* configuration.
*
* @param int $user_max Desired upload size supplied by the administrator
* @return int Actual value; the lower of user-supplied value or system-defined value
*/
function real_max_upload_size($user_max)
{
// The minimum of the candidates, is the real max. possible size
$candidates = array($user_max,
ini_get('post_max_size'),
ini_get('upload_max_filesize'), );
$real_max = null;
foreach ($candidates as $item) {
$val = trim($item);
$modifier = strtolower(substr($val, -1));
switch ($modifier) {
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}
if ($val > 1) {
if (is_null($real_max)) {
$real_max = $val;
} elseif ($val < $real_max) {
$real_max = $val;
}
}
}
return $real_max;
}