<?php
/**
* Functions for Plugin handling.
*
* 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.' );
/**
* Recursive helper function to display a field of the plugin's settings (by manipulating a Form).
*
* Used for PluginSettings ("Edit plugin") and PluginUserSettings ("Edit user settings") as well as widgets.
*
* @todo dh> Allow to move setting sets up and down (order). Control goes into /inc/CONTROL/settings/plugins.php.
* @todo NOTE: fp> I'm using this outside of Plugins; I'm not sure about proper factorization yet.
* This should probably be an extension of the Form class. Sth like "AutoForm" ;)
*
* @param string Settings path, e.g. 'locales[0]' or 'setting'
* @param array Meta data for this setting.
* @param Form (by reference)
* @param string Settings type ('Settings' or 'UserSettings' or 'GroupSettings' or 'Widget' or 'Skin')
* @param Plugin|Widget
* @param mixed Target (User object for 'UserSettings', Group object for 'GroupSettings')
* @param mixed Value to really use (used for recursion into array type settings)
*/
function autoform_display_field( $parname, $parmeta, & $Form, $set_type, $Obj, $set_target = NULL, $use_value = NULL )
{
global $debug;
static $has_array_type;
if( ! is_array( $parmeta ) )
{ // Must be array:
return;
}
if( ! empty($parmeta['no_edit']) )
{ // this setting is not editable
return;
}
$params = array();
if( $use_value === NULL )
{ // outermost level
$has_array_type = false; // for adding a note about JS
$outer_most = true;
}
else
{
$outer_most = false;
}
// Set input group
if( isset( $parmeta['group'] ) )
{
$group = $parmeta['group'];
if( substr( $group, -1 ) === ']' )
{ // If group name is in array format like "edit_plugin_1_set_sample_sets[0][group_name]",
// then param name must be like "edit_plugin_1_set_sample_sets[0][group_name_param_name]":
$parname = substr( $group, 0, strlen( $group ) - 1 ).$parname.']';
}
else
{ // If group name is simple like "group_name",
// then param name must be like "group_name_param_name"
$parname = $group.$parname;
}
}
else
{
$group = NULL;
}
// Passthrough some attributes to elements:
foreach( $parmeta as $k => $v )
{
if( in_array( $k, array( 'id', 'class', 'onchange', 'onclick', 'onfocus', 'onkeyup', 'onkeydown', 'onreset', 'onselect', 'cols', 'rows', 'maxlength', 'placeholder' ) ) )
{
$params[$k] = $v;
}
}
if( ! empty($parmeta['multiple']) )
{ // "multiple" attribute for "select" inputs:
$params['multiple'] = 'multiple';
}
if( isset($parmeta['note']) )
{
$params['note'] = $parmeta['note'];
}
if( ! isset($parmeta['type']) || $parmeta['type'] == 'html_input' )
{
$parmeta['type'] = 'text';
}
elseif( $parmeta['type'] == 'html_textarea' )
{
$parmeta['type'] = 'textarea';
}
if( strpos($parmeta['type'], 'select_') === 0 )
{ // 'allow_none' setting for select_* types
if( isset($parmeta['allow_none']) )
{
$params['allow_none'] = $parmeta['allow_none'];
}
}
$help_icon = NULL;
if( isset($parmeta['help']) )
{
if( $parmeta['help'] === true )
{ // link to $parname-target:
$help_target = '#'.preg_replace( array('~\]?\[\d+\]\[~', '~\]$~'), array('_',''), $parname );
}
else
{
$help_target = $parmeta['help'];
}
$help_icon = $Obj->get_help_link( $help_target );
}
$set_label = isset($parmeta['label']) ? $parmeta['label'] : '';
if( ! empty($parmeta['disabled']) )
{
$params['disabled'] = 'disabled';
}
// "Layout" settings:
if( isset($parmeta['layout']) )
{
switch( $parmeta['layout'] )
{
case 'begin_fieldset':
$fieldset_title = $set_label;
if( $Form->layout == 'accordion' && ! empty( $Form->_opentags['fieldset'] ) )
{ // Use accordion style only for top level:
$Form->switch_layout( 'fieldset' );
}
$fieldset_params = array();
if( isset( $parmeta['fold'] ) && $parmeta['fold'] === true )
{ // Enable folding for the fieldset:
$fieldset_params['fold'] = $parmeta['fold'];
if( isset( $parmeta['deny_fold'] ) )
{ // TRUE to don't allow fold the block and keep it opened always on page loading:
$fieldset_params['deny_fold'] = $parmeta['deny_fold'];
}
// Unique ID of fieldset to store in user settings or in user per collection settings:
$fieldset_params['id'] = isset( $parmeta['id'] ) ? $parmeta['id'] : $parname;
}
$Form->begin_fieldset( $fieldset_title.$help_icon, $fieldset_params );
break;
case 'end_fieldset':
$Form->end_fieldset();
if( isset( $Form->saved_layouts[0] ) && $Form->saved_layouts[0] == 'accordion' && ! empty( $Form->_opentags['fieldset'] ) )
{ // We should switch back to accordion style:
$Form->switch_layout( NULL );
}
break;
case 'separator':
echo '<hr />';
break;
case 'html': // Output HTML code here
if( ! isset($parmeta['value']) )
{
$parmeta['value'] = '<div class="error">HTML layout usage:<pre>'.
htmlentities("'layout' => 'html',\n'value' => '<em>My HTML code</em>',").'</pre></div>';
}
echo $parmeta['value'];
break;
}
return;
}
if( ! empty($help_icon) )
{ // Append help icon to note:
if( empty($params['note']) )
{
$params['note'] = $help_icon;
}
else
{
$params['note'] .= ' '.$help_icon;
}
}
if( isset( $parmeta['input_prefix'] ) )
{ // Use prefix before input element if it is defined:
$params['input_prefix'] = $parmeta['input_prefix'];
}
if( isset( $parmeta['input_suffix'] ) )
{ // Use suffix after input element if it is defined:
$params['input_suffix'] = $parmeta['input_suffix'];
}
if( $set_type == 'Skin' &&
in_array( $parmeta['type'], array( 'text', 'integer', 'color', 'select', 'select_object' ) ) )
{ // Append action icon to restore setting to default value by JavaScript:
$params['input_suffix'] = ( isset( $params['input_suffix'] ) ? $params['input_suffix'] : '' ).
' '.get_icon( 'reload', 'imgtag', array(
'data-default-value' => $Obj->get_setting_default_value( $parname, $group ),
'title' => T_('Restore to default value'),
'class' => 'pointer'
) );
}
if( isset($use_value) )
{
$set_value = $use_value;
}
else
{
switch( $set_type )
{
case 'CollSettings':
$set_value = $Obj->get_coll_setting( $parname, $set_target, false, $group );
$error_value = NULL;
break;
case 'MsgSettings':
$set_value = $Obj->get_msg_setting( $parname, $group );
$error_value = NULL;
break;
case 'EmailSettings':
$set_value = $Obj->get_email_setting( $parname, $group );
$error_value = NULL;
break;
case 'Skin':
$set_value = $Obj->get_setting( $parname, $group );
$error_value = NULL;
break;
case 'Widget':
case 'PluginWidget':
$set_value = $Obj->get_param( $parname, NULL, $group );
$error_value = NULL;
break;
case 'UserSettings':
// NOTE: this assumes we come here only on recursion or with $use_value set..!
$set_value = $Obj->UserSettings->get( $parname, $set_target->ID );
$tmp_params = array(
'name' => $parname,
'value' => & $set_value,
'meta' => $parmeta,
'User' => $set_target,
'action' => 'display' );
$error_value = $Obj->PluginUserSettingsValidateSet( $tmp_params );
break;
case 'GroupSettings':
// NOTE: this assumes we come here only on recursion or with $use_value set..!
$set_value = $Obj->GroupSettings->get( $parname, $set_target->ID );
$tmp_params = array(
'name' => $parname,
'value' => & $set_value,
'meta' => $parmeta,
'Group' => $set_target,
'action' => 'display' );
$error_value = $Obj->PluginGroupSettingsValidateSet( $tmp_params );
break;
case 'Settings':
// NOTE: this assumes we come here only on recursion or with $use_value set..!
$set_value = $Obj->Settings->get( $parname );
$tmp_params = array(
'name' => $parname,
'value' => & $set_value,
'meta' => $parmeta,
'action' => 'display' );
$error_value = $Obj->PluginSettingsValidateSet( $tmp_params );
break;
case 'SharedSettings':
$set_value = $Obj->get_shared_setting( $parname, $group );
$error_value = NULL;
break;
default:
debug_die( "unhandled set_type $set_type" );
break;
}
if( $error_value )
{ // add error
param_error( $Obj->get_param_prefix().$parname, NULL, $error_value ); // only add the error to the field
}
}
// Display input element:
$input_name = $Obj->get_param_prefix().$parname;
if( substr($parmeta['type'], 0, 6) == 'select' && ! empty($parmeta['multiple']) )
{ // a "multiple" select:
$input_name .= '[]';
}
// Get a value from _POST request to display it e.g. when some error was created during update
$value_from_request = get_param( $input_name );
if( $value_from_request !== NULL )
{
$set_value = $value_from_request;
}
if( ! empty( $parmeta['hide'] ) )
{ // Hide this field on the editing form:
$original_form_fieldstart = $Form->fieldstart;
$Form->fieldstart = preg_replace( '/>$/', 'style="display:none">', $Form->fieldstart );
if( isset( $Form->fieldstart_checkbox ) )
{
$original_form_fieldstart_checkbox = $Form->fieldstart_checkbox;
$Form->fieldstart_checkbox = preg_replace( '/>$/', 'style="display:none">', $Form->fieldstart_checkbox );
}
if( isset( $Form->fieldstart_radio ) )
{
$original_form_fieldstart_radio = $Form->fieldstart_radio;
$Form->fieldstart_radio = preg_replace( '/>$/', 'style="display:none">', $Form->fieldstart_radio );
}
}
switch( $parmeta['type'] )
{
case 'begin_line':
$Form->begin_line( $set_label );
break;
case 'end_line':
$Form->end_line( $set_label );
break;
case 'string':
echo $set_label;
break;
case 'checkbox':
$Form->checkbox_input( $input_name, $set_value, $set_label, $params );
break;
case 'checklist':
$options = array();
foreach( $parmeta['options'] as $meta_option )
{
$meta_option_checked = $set_value === NULL ?
/* default value */ $meta_option[2] :
/* saved value */ ! empty( $set_value[ $meta_option[0] ] );
$meta_option_disabled = isset( $meta_option[4] ) ? $meta_option[4] : NULL;
$meta_option_note = isset( $meta_option[5] ) ? $meta_option[5] : NULL;
$meta_option_class = isset( $meta_option[6] ) ? $meta_option[6] : NULL;
$meta_option_hidden = isset( $meta_option[7] ) ? $meta_option[7] : NULL;
$meta_option_label_attribs = isset( $meta_option[8] ) ? $meta_option[8] : NULL;
$options[] = array( $input_name.'['.$meta_option[0].']', 1, $meta_option[1], $meta_option_checked, $meta_option_disabled, $meta_option_note, $meta_option_class, $meta_option_hidden, $meta_option_label_attribs );
}
$Form->checklist( $options, $input_name, $set_label, false, false, $params );
break;
case 'textarea':
$textarea_rows = isset($parmeta['rows']) ? $parmeta['rows'] : 3;
$Form->textarea_input( $input_name, $set_value, $textarea_rows, $set_label, $params );
break;
case 'select':
$params['force_keys_as_values'] = true; // so that numeric keys get used as values! autoform_validate_param_value() checks for the keys only.
if( ! empty( $parmeta['multiple'] ) )
{ // Set specific size of multiple selector or use automatic size depending on count of options:
$params['size'] = ( isset( $parmeta['size'] ) ? $parmeta['size'] : count( $parmeta['options'] ) );
}
$Form->select_input_array( $input_name, $set_value, $parmeta['options'], $set_label, isset($parmeta['note']) ? $parmeta['note'] : NULL, $params );
break;
case 'select_object':
$Form->select_input_object( $input_name, $set_value, $parmeta['object'], $set_label, $params );
break;
case 'select_blog':
$BlogCache = & get_BlogCache();
$Form->select_input_object( $input_name, $set_value, $BlogCache, $set_label, $params );
break;
case 'select_group':
$GroupCache = & get_GroupCache();
$Form->select_input_object( $input_name, $set_value, $GroupCache, $set_label, $params );
break;
case 'select_user':
$UserCache = & get_UserCache();
$UserCache->clear();
$users_SQL = $UserCache->get_SQL_object();
$users_SQL->LIMIT( isset( $parmeta['users_limit'] ) ? intval( $parmeta['users_limit'] ) : 20 );
$UserCache->load_by_sql( $users_SQL );
if( ! isset( $params['loop_object_method'] ) )
{
$params['loop_object_method'] = 'get_preferred_name';
}
$Form->select_input_object( $input_name, $set_value, $UserCache, $set_label, $params );
break;
case 'radio':
if( isset( $parmeta['field_lines'] ) )
{
$params['lines'] = $parmeta['field_lines'];
}
$options = array();
foreach( $parmeta['options'] as $l_key => $l_options )
{
$options[$l_key] = array(
'value' => $l_options[0],
'label' => $l_options[1]
);
if( isset( $l_options[2] ) )
{
$options[$l_key]['note'] = $l_options[2];
}
if( isset( $l_options[4] ) )
{ // Convert "inline attribs" to "params" array:
preg_match_all( '#(\w+)=[\'"](.*)[\'"]#', $l_options[4], $matches, PREG_SET_ORDER );
foreach( $matches as $l_set_nr => $l_match )
{
$options[$l_key][$l_match[1]] = $l_match[2];
}
}
if( isset( $l_options[3] ) )
{
$options[$l_key]['suffix'] = $l_options[3];
}
}
$Form->radio_input( $input_name, $set_value, $options, $set_label, $params );
break;
case 'array':
case 'array:integer':
case 'array:array:integer':
case 'array:string':
case 'array:array:string':
case 'array:regexp':
$has_array_type = true;
$has_color_field = false;
// Always use 'fieldset' layout to display it the same way from normal and ajax calls
$Form->switch_layout( 'fieldset' );
if( substr_count( $parname, '[' ) % 2 )
{ // this refers to a specific array type set (with index pos at the end), e.g. when adding a field through AJAX:
$pos_last_bracket = strrpos($parname, '[');
$k_nb = substr( $parname, $pos_last_bracket+1, -1 );
$disp_arrays = array( '' => $set_value ); // empty key..
$parname = substr($parname, 0, $pos_last_bracket);
}
else
{ // display all values hold in this set:
$disp_whole_set = true;
$disp_arrays = $set_value;
$fieldset_title = $set_label;
if( $debug )
{
$fieldset_title .= ' [debug: '.$parname.']';
}
$fieldset_params = array();
if( isset( $parmeta['fold'] ) && $parmeta['fold'] === true )
{ // Enable folding for the fieldset:
$fieldset_params['fold'] = $parmeta['fold'];
if( isset( $parmeta['deny_fold'] ) )
{ // TRUE to don't allow fold the block and keep it opened always on page loading:
$fieldset_params['deny_fold'] = $parmeta['deny_fold'];
}
}
// Unique ID of fieldset to store in user settings or in user per collection settings:
$fieldset_params['id'] = isset( $parmeta['id'] ) ? $parmeta['id'] : $parname;
$Form->begin_fieldset( $fieldset_title, $fieldset_params );
if( ! empty($params['note']) )
{
echo '<p class="notes">'.$params['note'].'</p>';
}
$k_nb = 0;
}
// check if a color field is among the entries
foreach( $parmeta['entries'] as $entry )
{
if( isset( $entry['type'] ) && $entry['type'] == 'color' )
{
$has_color_field = true;
break;
}
}
$user_ID = $set_type == 'UserSettings' ? $set_target->ID : '';
if( is_array( $set_value ) && ! empty($set_value) )
{ // Display value of the setting. It may be empty, if there's no set yet.
foreach( $disp_arrays as $k => $v )
{
$fieldset_params = array(
'class' => 'bordered',
// Unique ID of fieldset(Also used to store a folding state in user settings or in user per collection settings):
'id' => isset( $parmeta['id'] ) ? $parmeta['id'] : $parname.'_'.$k_nb,
);
$remove_action = '';
if( ! isset($parmeta['min_count']) || count($set_value) > $parmeta['min_count'] )
{ // provide icon to remove this set
$remove_action = '<span class="pull-right">'.action_icon(
T_('Remove'),
'minus',
regenerate_url( 'action', array('action=del_settings_set&set_path='.$parname.'['.$k.']'.( $set_type == 'UserSettings' ? '&user_ID='.$user_ID : '' ), 'plugin_ID='.$Obj->ID) ),
T_('Remove'),
5, 3, /* icon/text prio */
// attach onclick event to remove the whole fieldset:
array(
'onclick' => '
jQuery( \'#fieldset_wrapper_'.$parname.'_'.$k_nb.'\' ).remove();
'.( isset( $parmeta['max_number'] ) ? '
if( jQuery( \'[id^=fieldset_wrapper_'.$parname.'_\' ).length < '.intval( $parmeta['max_number'] ).' )
{
jQuery( \'#button_add_new_setting_'.$parname.'\' ).show();
}' : '' ).'
return false;',
)
).'</span>';
}
if( isset( $parmeta['fold'] ) && $parmeta['fold'] === true )
{ // Enable folding for the fieldset:
$fieldset_params['fold'] = $parmeta['fold'];
if( isset( $parmeta['deny_fold'] ) )
{ // TRUE to don't allow fold the block and keep it opened always on page loading:
$fieldset_params['deny_fold'] = $parmeta['deny_fold'];
}
$fieldset_params['id'] = $parname;
}
$Form->begin_fieldset( '#'.$k_nb.$remove_action, $fieldset_params );
if( isset($parmeta['key']) )
{ // KEY FOR THIS ENTRY:
if( ! strlen($k) && isset($parmeta['key']['defaultvalue']) )
{ // key is not given/set and we have a default:
$l_value = $parmeta['key']['defaultvalue'];
}
else
{
$l_value = $k;
}
// RECURSE:
autoform_display_field( $parname.'['.$k_nb.'][__key__]', $parmeta['key'], $Form, $set_type, $Obj, $set_target, $l_value );
}
foreach( $parmeta['entries'] as $l_set_name => $l_set_entry )
{
if( isset( $set_value[$k][$l_set_name] ) )
{ // Use a saved value:
$l_value = $set_value[$k][$l_set_name];
}
else
{ // Use default value if it is defined:
$l_value = isset( $l_set_entry['defaultvalue'] ) ? $l_set_entry['defaultvalue'] : NULL;
}
// RECURSE:
autoform_display_field( $parname.'['.$k_nb.']['.$l_set_name.']', $l_set_entry, $Form, $set_type, $Obj, $set_target, $l_value );
}
$Form->end_fieldset();
$k_nb++;
}
}
// This div is used to insert new set of setting:
echo '<div id="block_add_new_setting_'.$parname.'_'.$Obj->ID.'" data-param-num="'.$k_nb.'"></div>';
// TODO: fix this for AJAX callbacks, when removing and re-adding items (dh):
if( ! is_ajax_request() )
{ // Don't display "Add" button twice after AJAX request:
global $Blog;
$set_path = $parname.'['.$k_nb.']';
echo action_icon(
sprintf( T_('Add a new set of «%s»'), $set_label ),
'add',
regenerate_url( 'action', array('action=add_settings_set', 'set_path='.$set_path.( $set_type == 'UserSettings' ? '&user_ID='.get_param('user_ID') : '' ), 'plugin_ID='.$Obj->ID) ),
T_('Add'),
5, 3, /* icon/text prio */
array(
// Set button id to hide/show it depending on current number of settings:
'id' => 'button_add_new_setting_'.$parname,
// Hide button to add new set of setting when max number is reached:
'style' => ( isset( $parmeta['max_number'] ) && $k_nb >= $parmeta['max_number'] ? 'display:none' : '' ),
// Replace the 'add new' div with a new set of setting:
'onclick' => 'jQuery.get( \''.get_htsrv_url().'async.php\',
{
action: \'add_plugin_sett_set\',
plugin_ID: '.$Obj->ID.',
set_type: \''.$set_type.'\',
param_name: \''.$parname.'\',
//param_num: jQuery( \'[id^=fieldset_wrapper_'.$parname.'_\' ).length
param_num: jQuery( \'#block_add_new_setting_'.$parname.'_'.$Obj->ID.'\' ).data( \'param-num\' )
'.( isset( $Blog ) ? ',blog: '.$Blog->ID : '' ).'
'.( $set_type == 'UserSettings' ? ',user_ID: '.get_param( 'user_ID' ) : '' ).'
},
function( r, status )
{
jQuery( \'#block_add_new_setting_'.$parname.'_'.$Obj->ID.'\' ).replaceWith( ajax_debug_clear( r ) );
'.( $has_color_field ? 'evo_initialize_colorpicker_inputs();' : '' ).'
'.( isset( $parmeta['max_number'] ) ? '
if( jQuery( \'[id^=fieldset_wrapper_'.$parname.'_\' ).length >= '.intval( $parmeta['max_number'] ).' )
{
jQuery( \'#button_add_new_setting_'.$parname.'\' ).hide();
}' : '' ).'
}
);
return false;'
)
);
}
if( ! empty($disp_whole_set) )
{ // close the surrounding fieldset:
$Form->end_fieldset();
}
$Form->switch_layout( NULL );
break;
case 'password':
$params['type'] = 'password'; // same as text input, but type=password
case 'float':
case 'integer':
case 'text':
// Default: "text input"
if( isset($parmeta['size']) )
{
$size = (int)$parmeta['size'];
}
else
{ // Default size:
$size = 15;
}
if( isset($parmeta['maxlength']) )
{
$params['maxlength'] = (int)$parmeta['maxlength'];
}
else
{ // do not use size as maxlength, if not given!
$params['maxlength'] = '';
}
if( isset( $parmeta['hide_label'] ) )
{ // This param is used to hide a label
$params['hide_label'] = $parmeta['hide_label'];
}
if( $parmeta['type'] == 'integer' )
{ // Set special type 'number' for integer param to initialize control arrows to allow increase/decrease a value:
$params['type'] = 'number';
if( isset( $parmeta['valid_range']['min'] ) )
{ // Restrict with min value:
$params['min'] = $parmeta['valid_range']['min'];
}
if( isset( $parmeta['valid_range']['max'] ) )
{ // Restrict with max value:
$params['max'] = $parmeta['valid_range']['max'];
}
// Input number element doesn't support attribute "size", so we have only one way to set width with style:
$params['style'] = 'width:'.( 40 + $size * 8 ).'px';
}
if( isset( $parmeta['suffix'] ) )
{ // Set suffix text after input element:
$params['input_suffix'] = $parmeta['suffix'];
}
$Form->text_input( $input_name, $set_value, $size, $set_label, '', $params ); // TEMP: Note already in params
break;
case 'usertag':
$size = isset( $parmeta['size'] ) ? intval( $parmeta['size'] ) : 30;
$Form->usertag_input( $input_name, $set_value, $size, $set_label, '', $params );
break;
case 'itemtag':
$size = isset( $parmeta['size'] ) ? intval( $parmeta['size'] ) : 30;
$Form->tag_input( $input_name, $set_value, $size, $set_label, '', $params );
break;
case 'info':
$Form->info( $parmeta['label'], $parmeta['info'] );
break;
case 'color':
if( isset( $parmeta['transparency'] ) )
{
$params['transparency'] = $parmeta['transparency'];
}
$Form->color_input( $input_name, $set_value, $set_label, '', $params );
break;
case 'fileselect':
if( isset( $parmeta['size'] ) )
{
$params['max_file_num'] = $parmeta['size'];
}
$params['root'] = isset( $parmeta['root'] ) ? $parmeta['root'] : '';
$params['path'] = isset( $parmeta['path'] ) ? $parmeta['path'] : '';
$params['size_name'] = isset( $parmeta['thumbnail_size'] ) ? $parmeta['thumbnail_size'] : 'crop-64x64';
$params['max_file_num'] = isset( $parmeta['max_file_num'] ) ? $parmeta['max_file_num'] : 1;
$params['initialize_with'] = isset( $parmeta['initialize_with'] ) ? $parmeta['initialize_with'] : '';
$params['note'] = isset( $parmeta['note'] ) ? $parmeta['note'] : '';
$Form->fileselect( $input_name, $set_value, $set_label, $params['note'], $params );
break;
case 'input_group':
if( ! empty( $parmeta['inputs'] ) && is_array( $parmeta['inputs'] ) )
{
$Form->begin_line( $parmeta['label'], $input_name );
foreach( $parmeta['inputs'] as $l_parname => $l_parmeta )
{
$l_parmeta['group'] = $parname; // inject group
autoform_display_field( $l_parname, $l_parmeta, $Form, $set_type, $Obj, $set_target, $use_value );
}
$Form->end_line();
}
break;
case 'item_selector':
$Form->item_selector( $input_name, $set_value, $set_label, $parmeta );
break;
default:
debug_die( 'Unsupported type ['.$parmeta['type'].'] from GetDefaultSettings()!' );
}
if( isset( $original_form_fieldstart ) )
{ // Revert original field start html code:
$Form->fieldstart = $original_form_fieldstart;
}
if( isset( $original_form_fieldstart_checkbox ) )
{ // Revert original field start html code:
$Form->fieldstart_checkbox = $original_form_fieldstart_checkbox;
}
if( isset( $original_form_fieldstart_radio ) )
{ // Revert original field start html code:
$Form->fieldstart_radio = $original_form_fieldstart_radio;
}
if( $outer_most && $has_array_type )
{ // Note for Non-Javascript users:
echo '<script></script><noscript>';
echo '<p class="note">'.T_('Note: before adding a new set you have to save any changes.').'</p>';
echo '</noscript>';
}
}
/**
* Helper method for "add_settings_set" and "delete_settings_set" action.
*
* Walks the given settings path and either inits the target entry or unsets it ($init_value=NULL).
*
* @param object Plugin or Skin
* @param string Settings type ("Settings" or "UserSettings")
* @param string The settings path, e.g. 'setting[0]foo[1]'. (Is used as array internally for recursion.)
* @param mixed The initial value of the setting, typically array() - NULL to unset it (action "delete_settings_set" uses it)
* @return array|false
*/
function _set_setting_by_path( & $Plugin, $set_type, $path, $init_value = array() )
{
$r = get_plugin_settings_node_by_path( $Plugin, $set_type, $path, true );
if( $r === false )
{
return false;
}
// Make return value handier. Note: list() would copy and destroy the references (setting and set_node)!
$set_name = & $r['set_name'];
$set_node = & $r['set_node'];
$set_meta = & $r['set_meta'];
$set_parent = & $r['set_parent'];
$set_key = & $r['set_key'];
$setting = & $r['setting'];
#pre_dump( $r );
#if( isset($set_node) && $init_value !== NULL )
#{ // Setting already exists (and we do not want to delete), e.g. page reload!
# return false;
# /*
# while( isset($l_setting[ $path[0] ]) )
# { // bump the index until not set
# $path[0]++;
# }
# */
#}
#else
if( is_null($init_value) )
{ // NULL is meant to unset it
unset($set_parent[$set_key]);
}
else
{ // Init entries:
// destroys reference: $set_node = $init_value;
// Copy meta entries:
foreach( $set_meta['entries'] as $k => $v )
{
if( isset( $v['defaultvalue'] ) )
{ // set to defaultvalue
$set_node[$k] = $v['defaultvalue'];
}
else
{
if( isset($v['type']) && strpos( $v['type'], 'array' ) === 0 )
{
$set_node[$k] = array();
}
else
{
$set_node[$k] = '';
}
}
}
}
switch( $set_type )
{
case 'Settings':
case 'CollSettings':
case 'PluginWidget':
$Plugin->Settings->set( $set_name, $setting );
break;
case 'Widget':
$Plugin->set( $set_name, $setting );
break;
case 'UserSettings':
$Plugin->UserSettings->set( $set_name, $setting );
break;
case 'GroupSettings':
$Plugin->GroupSettings->set( $set_name, $setting );
break;
case 'MsgSettings':
$set_name = ( $set_name == 'msg_apply_rendering' ? '' : 'msg_' ).$set_name;
$Plugin->Settings->set( $set_name, $setting );
break;
case 'EmailSettings':
$set_name = ( $set_name == 'email_apply_rendering' ? '' : 'email_' ).$set_name;
$Plugin->Settings->set( $set_name, $setting );
break;
case 'Skin':
$Skin = & $Plugin;
$Skin->set_setting( $set_name, $setting );
break;
case 'SharedSettings':
$set_name = ( $set_name == 'shared_apply_rendering' ? '' : 'shared_' ).$set_name;
$Plugin->Settings->set( $set_name, $setting );
break;
default:
debug_die( 'Invalid plugin type param!' );
}
return $setting;
}
/**
* Get a node from settings by path (e.g. "locales[0][questions]")
*
* @param object Plugin or Skin
* @param string Settings type ("Settings" or "UserSettings")
* @param string The settings path, e.g. 'setting[0]foo[1]' or even 'setting[]'. (Is used as array internally for recursion.)
* @return array Array(
* - 'set_name': setting name (string); key of the first level
* - 'set_node': selected setting node, may be NULL (by reference)
* - 'set_meta': meta info (from GetDefault[User]Settings()) for selected node (array)
* - 'set_parent': parent node (by reference)
* - 'set_key': key in parent node (by reference)
* - 'setting': whole settings (array)
*/
function get_plugin_settings_node_by_path( & $Plugin, $set_type, $path, $create = false )
{
// Init:
if( ! preg_match( '~^\w+(\[\w+\])+$~', $path ) )
{
debug_die( 'Invalid path param!' );
}
$path = preg_split( '~(\[|\]\[?)~', $path, -1 ); // split by "[" and "][", so we get an array with setting name and index alternating
$foo = array_pop($path); // remove last one
if( ! empty($foo) )
debug_die('Assertion failed!');
$set_name = $path[0];
// meta info for this setting:
$tmp_params = array( 'for_editing' => true );
switch( $set_type )
{
case 'Settings':
$setting = $Plugin->Settings->get( $set_name );
$defaults = $Plugin->GetDefaultSettings( $tmp_params );
break;
case 'UserSettings':
$setting = $Plugin->UserSettings->get( $set_name );
$defaults = $Plugin->GetDefaultUserSettings( $tmp_params );
break;
case 'GroupSettings':
$setting = $Plugin->GroupSettings->get( $set_name );
$defaults = $Plugin->GetDefaultGroupSettings( $tmp_params );
break;
case 'CollSettings':
$setting = $Plugin->Settings->get( $set_name );
$defaults = $Plugin->get_coll_setting_definitions( $tmp_params );
break;
case 'MsgSettings':
$param_name = ( $set_name == 'msg_apply_rendering' ? '' : 'msg_' ).$set_name;
$setting = $Plugin->Settings->get( $param_name );
$defaults = $Plugin->get_msg_setting_definitions( $tmp_params );
break;
case 'EmailSettings':
$param_name = ( $set_name == 'email_apply_rendering' ? '' : 'email_' ).$set_name;
$setting = $Plugin->Settings->get( $param_name );
$defaults = $Plugin->get_email_setting_definitions( $tmp_params );
break;
case 'PluginWidget':
$setting = $Plugin->Settings->get( $set_name );
$defaults = $Plugin->get_widget_param_definitions( $tmp_params );
break;
case 'Widget':
$setting = $Plugin->get_param( $set_name );
$defaults = $Plugin->get_param_definitions( $tmp_params );
break;
case 'Skin':
$Skin = & $Plugin;
$setting = $Skin->get_setting( $set_name );
$defaults = $Skin->get_param_definitions( $tmp_params );
break;
case 'SharedSettings':
$param_name = ( $set_name == 'shared_apply_rendering' ? '' : 'shared_' ).$set_name;
$setting = $Plugin->Settings->get( $param_name );
$defaults = $Plugin->get_shared_setting_definitions( $tmp_params );
break;
default:
debug_die( 'Invalid plugin type param!' );
}
if( ! is_array( $setting ) )
{ // this may happen, if there was a non-array setting stored previously:
// discard those!
$setting = array();
}
if( ! isset( $defaults[ $set_name ] ) )
{
//debug_die( 'Invalid setting ('.$set_name.') - no meta data!' );
return false;
}
$found_node = & $setting;
$defaults_node = & $defaults;
$set_meta = $defaults[$set_name];
$set_parent = NULL;
$set_key = NULL;
$count = 0;
while( count($path) )
{
$count++;
$loop_name = array_shift($path);
if( $count > 1 )
{
$set_parent = & $found_node[$loop_name];
$set_key = NULL;
$defaults_node = & $defaults_node['entries'][$loop_name];
$found_node = & $found_node[$loop_name];
}
else
{
$defaults_node = & $defaults_node[$loop_name];
$set_parent = & $setting;
}
if( count($path) )
{ // has an index => array
$loop_index = array_shift($path);
#$set_parent = & $set_parent[$loop_name];
$set_key = $loop_index;
if( $set_key === '' )
{ // []-syntax: append entry
if( $create && ! count($path) )
{ // only create, if at the end
$found_node[] = array();
}
$found_node = & $found_node[ array_pop(array_keys($found_node)) ];
}
else
{ // specific key:
if( ! isset($found_node[$loop_index]) )
{
$found_node[$loop_index] = array();
}
$found_node = & $found_node[$loop_index];
}
}
}
#echo '<h1>RETURN</h1>'; pre_dump( $set_parent, $set_key );
return array(
'set_name' => $set_name,
'set_node' => & $found_node,
'set_meta' => $defaults_node,
'set_parent' => & $set_parent,
'set_key' => & $set_key,
'setting' => & $setting );
}
/**
* Set Plugin settings from params.
*
* fp> WARNING: also used outside of plugins. Work in progress.
*
* This handled plugin specific params when saving a user profile (PluginUserSettings) or plugin settings (PluginSettings).
*
* @param string Settings path, e.g. 'locales[0]' or 'setting'
* @param array Meta data for this setting.
* @param Plugin|Widget
* @param string Type of Settings (either 'Settings' or 'UserSettings' or 'GroupSettings').
* @param mixed Target (User object for 'UserSettings', Group object for 'GroupSettings')
* @param mixed NULL to use value from request, OR set value what you want to force
*/
function autoform_set_param_from_request( $parname, $parmeta, & $Obj, $set_type, $set_target = NULL, $set_value = NULL )
{
if( ! is_array( $parmeta ) )
{ // Must be array:
return;
}
if( isset($parmeta['layout']) )
{ // a layout "setting"
return;
}
if( ( ! empty( $parmeta['disabled'] ) || ! empty( $parmeta['no_edit'] ) )
&& $set_value === NULL )
{ // the setting is disabled, but allow to update the value when it is forced by $set_value
return;
}
if( ! empty( $parmeta['inputs'] ) )
{
foreach( $parmeta['inputs'] as $l_parname => $l_parmeta )
{
$l_parmeta['group'] = $parname; // inject group into meta
autoform_set_param_from_request( $l_parname, $l_parmeta, $Obj, $set_type, $set_target, $set_value );
}
return;
}
// set input group
if( isset( $parmeta['group'] ) )
{
$parname = $parmeta['group'].$parname;
}
$l_param_type = 'string';
$l_param_default = '';
if( isset($parmeta['type']) )
{
if( substr($parmeta['type'], 0, 6) == 'select' && ! empty($parmeta['multiple']) )
{ // a "multiple" select:
$l_param_type = 'array';
}
switch( $parmeta['type'] )
{
case 'array':
case 'array:integer':
case 'array:array:integer':
case 'array:string':
case 'array:array:string':
case 'array:regexp':
// this settings has a type
$l_param_type = $parmeta['type'];
break;
case 'checkbox':
$l_param_type = 'integer';
$l_param_default = 0;
break;
case 'checklist':
$l_param_type = 'array';
$l_param_default = array();
break;
case 'textarea':
$l_param_type = 'text';
break;
case 'html_input':
case 'html_textarea':
$l_param_type = 'html';
break;
default:
}
}
if( $set_value === NULL )
{ // Get the value from request:
$l_value = param( $Obj->get_param_prefix().$parname, $l_param_type, $l_param_default );
// Store values of unchecked checkboxes manually because their empty values are not passed to a submitted form:
switch( $l_param_type )
{
case 'array':
case 'array:integer':
case 'array:array:integer':
case 'array:string':
case 'array:array:string':
case 'array:regexp':
if( ! empty( $parmeta['entries'] ) )
{
foreach( $l_value as $l_index => $l_index_values )
{
if( count( $parmeta['entries'] ) != count( $l_index_values ) )
{ // If some entry(like checkbox) is missed:
foreach( $parmeta['entries'] as $parmeta_entry_key => $parmeta_entry_data )
{
if( isset( $parmeta_entry_data['type'] ) &&
$parmeta_entry_data['type'] == 'checkbox' &&
! isset( $l_index_values[ $parmeta_entry_key ] ) )
{ // If field is checkbox but value is not passed to a submitted form becauase it was unchecked:
$l_value[ $l_index ][ $parmeta_entry_key ] = 0;
}
}
}
}
}
break;
}
}
else
{ // Force value
$l_value = $set_value;
}
if( isset( $parmeta['type'] ) )
{ // Prepare values before store them in DB:
if( strpos( $parmeta['type'], 'array' ) === 0 )
{ // Make keys (__key__) in arrays unique and remove them
handle_array_keys_in_plugin_settings( $l_value );
}
elseif( $parmeta['type'] == 'integer' )
{ // Convert to correct integer value:
if( $l_value !== '' )
{ // Don't convert empty string '' to integer 0:
$l_value = intval( $l_value );
}
if( empty( $l_value ) && ! empty( $parmeta['allow_empty'] ) &&
isset( $parmeta['valid_range'], $parmeta['valid_range']['min'] ) && $parmeta['valid_range']['min'] > 0 )
{ // Convert 0 to empty value for integer field if it allows empty values:
$l_value = NULL;
}
}
}
if( ! autoform_validate_param_value( $Obj->get_param_prefix().$parname, $l_value, $parmeta ) )
{
return;
}
// Validate form values:
switch( $set_type )
{
case 'CollSettings':
$error_value = NULL;
$Obj->set_coll_setting( $parname, $l_value );
break;
case 'Skin':
$error_value = NULL;
$Obj->set_setting( $parname, $l_value );
break;
case 'Widget':
$error_value = NULL;
if( isset( $parmeta['type'] ) && $parmeta['type'] == 'checklist' && $parname == 'renderers' )
{ // Save "stealth" and "always" plugin render options:
// (they are hidden or disabled checkboxes of the form and cannot be submitted automatically)
global $Plugins;
$widget_Blog = & $Obj->get_Blog();
$l_value = $Plugins->validate_renderer_list( array_keys( $l_value ), array(
'Blog' => & $widget_Blog,
'setting_name' => 'shared_apply_rendering',
) );
$l_value = array_fill_keys( $l_value, 1 );
}
$Obj->set( $parname, $l_value, false, ( isset( $parmeta['group'] ) ? $parmeta['group'] : NULL ) );
break;
case 'UserSettings':
// Plugin User settings:
$dummy = array(
'name' => $parname,
'value' => & $l_value,
'meta' => $parmeta,
'User' => $set_target,
'action' => 'set' );
$error_value = $Obj->PluginUserSettingsValidateSet( $dummy );
// Update the param value, because a plugin might have changed it (through reference):
$GLOBALS[ $Obj->get_param_prefix().$parname ] = $l_value;
if( empty( $error_value ) )
{
$Obj->UserSettings->set( $parname, $l_value, $set_target->ID );
}
break;
case 'GroupSettings':
// Plugin Group settings:
$dummy = array(
'name' => $parname,
'value' => & $l_value,
'meta' => $parmeta,
'Group' => $set_target,
'action' => 'set' );
$error_value = $Obj->PluginGroupSettingsValidateSet( $dummy );
// Update the param value, because a plugin might have changed it (through reference):
$GLOBALS[ $Obj->get_param_prefix().$parname ] = $l_value;
if( empty( $error_value ) )
{
$Obj->GroupSettings->set( $parname, $l_value, $set_target->ID );
}
break;
case 'Settings':
// Plugin global settings:
case 'MsgSettings':
// Plugin messages settings:
case 'EmailSettings':
// Plugin emails settings:
case 'SharedSettings':
// Plugin shared settings:
$dummy = array(
'name' => $parname,
'value' => & $l_value,
'meta' => $parmeta,
'action' => 'set' );
$error_value = $Obj->PluginSettingsValidateSet( $dummy );
// Update the param value, because a plugin might have changed it (through reference):
$GLOBALS[ $Obj->get_param_prefix().$parname ] = $l_value;
if( empty( $error_value ) )
{ // Set new value:
if( $set_type == 'MsgSettings' && $parname != 'msg_apply_rendering' )
{ // Use prefix 'msg_' for all message settings except of "msg_apply_rendering":
$Obj->Settings->set( 'msg_'.$parname, $l_value );
}
elseif( $set_type == 'EmailSettings' && $parname != 'email_apply_rendering' )
{ // Use prefix 'email_' for all email settings except of "email_apply_rendering":
$Obj->Settings->set( 'email_'.$parname, $l_value );
}
elseif( $set_type == 'SharedSettings' && $parname != 'shared_apply_rendering' )
{ // Use prefix 'shared_' for all shared settings except of "shared_apply_rendering":
$Obj->Settings->set( 'shared_'.$parname, $l_value );
}
else
{ // Global settings:
$Obj->Settings->set( $parname, $l_value );
}
}
break;
default:
debug_die( "unhandled set_type $set_type" );
break;
}
if( $error_value )
{ // A validation error has occured, record error message:
param_error( $Obj->get_param_prefix().$parname, $error_value );
}
}
/**
* Validates settings according to their meta info recursively.
*
* @todo Init "checkbox" values in "array" type settings (they do not get send) (dh)
* @param string Param name
* @param array Meta info
* @return boolean
*/
function autoform_validate_param_value( $param_name, $value, $meta )
{
global $Messages;
if( ! is_array( $meta ) )
{ // Must be array:
return;
}
if( is_array( $value ) && isset( $meta['entries'] ) )
{
$r = true;
if( isset( $meta['key'] ) )
{ // validate keys:
foreach( array_keys( $value ) as $k )
{
if( ! autoform_validate_param_value( $param_name.'['.$k.'][__key__]', $k, $meta['key'] ) )
{
$r = false;
}
}
}
// Check max_count/min_count
// dh> TODO: find a way to link it to the form's fieldset (and add an "error" class to it)
if( isset( $meta['max_count'] ) && count( $value ) > $meta['max_count'] )
{
$r = false;
$label = isset( $meta['label'] ) ? $meta['label'] : $param_name;
$Messages->add( sprintf( T_('Too many entries in the "%s" set. It must have %d at most.'), $label, $meta['max_count'] ), 'error' );
}
elseif( isset( $meta['min_count'] ) && count( $value ) < $meta['min_count'] )
{
$r = false;
$label = isset( $meta['label'] ) ? $meta['label'] : $param_name;
$Messages->add( sprintf( T_('Too few entries in the "%s" set. It must have %d at least.'), $label, $meta['min_count'] ), 'error' );
}
foreach( $meta['entries'] as $mk => $mv )
{
foreach( $value as $vk => $vv )
{
if( isset( $mv['type'] ) && $mv['type'] == 'input_group' &&
! empty( $mv['inputs'] ) && is_array( $mv['inputs'] ) )
{ // Validate each input field of the setting with type "input_group":
foreach( $mv['inputs'] as $k => $v )
{
if( isset( $vv[ $mk.$k ] ) && ! autoform_validate_param_value( $param_name.'['.$vk.']['.$mk.$k.']', $vv[ $mk.$k ], $v ) )
{ // If at least one setting of the grouped inputs has an error:
$r = false;
}
}
}
elseif( isset( $vv[ $mk ] ) && ! autoform_validate_param_value( $param_name.'['.$vk.']['.$mk.']', $vv[ $mk ], $mv ) )
{ // If single setting has an error:
$r = false;
}
}
}
return $r;
}
if( isset( $meta['type'] ) )
{
if( isset( $meta['allow_empty'] ) && $meta['allow_empty'] && empty( $value ) )
{ // Allow an empty value
return true;
}
switch( $meta['type'] )
{
case 'text':
if( isset( $meta['allow_empty'] ) && ! $meta['allow_empty'] && $value === '' )
{ // Display error if the text field is required to be not empty:
param_error( $param_name, sprintf( T_('The field «%s» cannot be empty.'), $meta['label'] ), T_('This field cannot be empty.') );
return false;
}
break;
case 'integer':
if( ! preg_match( '~^[-+]?\d+$~', $value ) )
{
param_error( $param_name, sprintf( T_('The value for «%s» must be numeric.'), $meta['label'] ), T_('The value must be numeric.') );
return false;
}
break;
case 'float':
if( ! preg_match( '~^[-+]?\d+(\.\d+)?$~', $value ) )
{
param_error( $param_name, sprintf( T_('The value for «%s» must be numeric.'), $meta['label'] ), T_('The value must be numeric.') );
return false;
}
break;
case 'radio':
$check_value = false;
foreach($meta['options'] as $arr)
{
if( ! is_array($arr) )
{
param_error( $param_name, sprintf( T_('Invalid option «%s».'), $arr ) );
return false;
}
if( $value == $arr[0] )
{
$check_value = true;
break;
}
}
if ( ! $check_value )
{
param_error( $param_name, sprintf( T_('Invalid option «%s».'), $value ) );
return false;
}
break;
case 'select':
$check_options = $value;
if( ! is_array($check_options) )
{ // no "multiple" select:
$check_options = array($check_options);
}
// Get all possible values for the select element:
$meta_options = array();
foreach( $meta['options'] as $meta_option_key => $meta_option_value )
{
if( is_array( $meta_option_value ) )
{ // It is a grouped options:
foreach( $meta_option_value as $meta_group_option_key => $meta_group_option_value )
{
$meta_options[] = $meta_group_option_key;
}
}
else
{ // Single option:
$meta_options[] = $meta_option_key;
}
}
// Check if the selected values can be used for the select element:
foreach( $check_options as $v )
{
if( empty( $v ) && ! empty( $meta['allow_none'] ) )
{ // empty is ok:
continue;
}
if( ! in_array( $v, $meta_options ) )
{
param_error( $param_name, sprintf( T_('Invalid option «%s».'), $v ) );
return false;
}
}
break;
case 'select_blog':
case 'select_group':
case 'select_user':
if( is_array($value) && empty($value) // empty "multiple" select
|| ( ! is_array($value) && ! strlen($value) ) )
{
if( empty($meta['allow_none']) )
{ // empty is not ok
param_error( $param_name, sprintf( T_('Invalid option «%s».'), $value ) );
return false;
}
}
else
{ // Try retrieving the value from the corresponding Cache:
switch( $meta['type'] )
{
case 'select_blog':
$Cache = & get_BlogCache();
break;
case 'select_group':
$Cache = & get_GroupCache();
break;
case 'select_user':
$Cache = & get_UserCache();
break;
}
$check_options = $value;
if( ! is_array($check_options) )
{ // no "multiple" select:
$check_options = array($check_options);
}
foreach($check_options as $v)
{
if( empty($v) && ! empty($meta['allow_none']) )
{ // empty is ok:
continue;
}
if( ! $Cache->get_by_ID($v, false, false) )
{
param_error( $param_name, sprintf( T_('Invalid option «%s».'), $v ) );
return false;
}
}
}
break;
case 'color':
if( ! is_color( $value ) )
{
param_error( $param_name, sprintf( T_('Invalid color code for «%s».'), $meta['label'] ) );
return false;
}
break;
}
}
// Check maxlength:
if( isset($meta['maxlength']) )
{
if( utf8_strlen($value) > $meta['maxlength'] )
{
param_error( $param_name, sprintf( T_('The value is too long.'), $value ) );
}
}
// Check valid pattern:
if( isset( $meta['valid_pattern'] ) )
{
$param_pattern = is_array( $meta['valid_pattern'] ) ? $meta['valid_pattern']['pattern'] : $meta['valid_pattern'];
if( ! preg_match( $param_pattern, $value ) )
{
$param_error = is_array( $meta['valid_pattern'] ) ? $meta['valid_pattern']['error'] : sprintf( T_('The value is invalid. It must match the regular expression «%s».'), $param_pattern );
param_error( $param_name, $param_error );
return false;
}
}
// Check valid range:
if( isset($meta['valid_range']) )
{
// Transform numeric indexes into associative keys:
if( ! isset($meta['valid_range']['min'], $meta['valid_range']['max'])
&& isset($meta['valid_range'][0], $meta['valid_range'][1]) )
{
$meta['valid_range']['min'] = $meta['valid_range'][0];
$meta['valid_range']['max'] = $meta['valid_range'][1];
}
if( isset($meta['valid_range'][2]) && ! isset($meta['valid_range']['error']) )
{
$meta['valid_range']['error'] = $meta['valid_range'][2];
}
if( ( isset( $meta['valid_range']['min'] ) && $value < $meta['valid_range']['min'] )
|| ( isset( $meta['valid_range']['max'] ) && $value > $meta['valid_range']['max'] ) )
{
if( isset( $meta['valid_range']['error'] ) )
{
$param_error = $meta['valid_range']['error'];
}
else
{
if( isset( $meta['valid_range']['min'] ) && isset( $meta['valid_range']['max'] ) )
{
$param_error = sprintf( T_('The value is invalid. It must be in the range from %s to %s.'), $meta['valid_range']['min'], $meta['valid_range']['max'] );
}
elseif( isset( $meta['valid_range']['max'] ) )
{
$param_error = sprintf( T_('The value is invalid. It must be smaller than or equal to %s.'), $meta['valid_range']['max'] );
}
else
{
$param_error = sprintf( T_('The value is invalid. It must be greater than or equal to %s.'), $meta['valid_range']['min'] );
}
}
param_error( $param_name, $param_error );
return false;
}
}
return true;
}
/**
* This handles the special "__key__" index in all array type values
* in the given array. It makes sure, that "__key__" is unique and
* replaces the original key of the value with it.
* @param array (by reference)
*/
function handle_array_keys_in_plugin_settings( & $a )
{
if( ! is_array( $a ) )
{
return;
}
$new_arr = array(); // use a new array to maintain order, also for "numeric" keys
foreach( array_keys( $a ) as $k )
{
$v = & $a[$k];
if( is_array( $v ) && isset( $v['__key__'] ) )
{
if( $k != $v['__key__'] )
{
$k = $v['__key__'];
if( ! strlen( $k ) || isset($a[ $k ]) )
{ // key already exists (or is empty):
$c = 1;
while( isset( $a[ $k.'_'.$c ] ) )
{
$c++;
}
$k = $k.'_'.$c;
}
}
unset( $v['__key__'] );
$new_arr[$k] = $v;
}
else
{
$new_arr[$k] = $v;
}
// Recurse:
foreach( array_keys( $v ) as $rk )
{
if( is_array( $v[$rk] ) )
{
handle_array_keys_in_plugin_settings($v[$rk]);
}
}
}
$a = $new_arr;
}
/**
* Helper function to do the action part of DB schema upgrades for "enable" and "install"
* actions.
*
* @param object Plugin
* @param boolean Force install DB for the plugin (used in installation of b2evo)
* @return boolean True, if no changes needed or done; false if we should break out to display "install_db_schema" action payload.
*/
function install_plugin_db_schema_action( & $Plugin, $force_install_db_deltas = false )
{
global $inc_path, $install_db_deltas, $DB, $Messages;
// Prepare vars for DB layout changes
$install_db_deltas_confirm_md5 = param( 'install_db_deltas_confirm_md5', 'string' );
$db_layout = $Plugin->GetDbLayout();
$install_db_deltas = array(); // This holds changes to make, if any (just all queries)
//pre_dump( $db_layout );
if( ! empty( $db_layout ) )
{ // The plugin has a DB layout attached
load_funcs('_core/model/db/_upgrade.funcs.php');
// Get the queries to make:
foreach( db_delta( $db_layout ) as $table => $queries )
{
foreach( $queries as $query_info )
{
foreach( $query_info['queries'] as $query )
{ // subqueries for this query (usually one, but may include required other queries)
$install_db_deltas[] = $query;
}
}
}
if( ! empty( $install_db_deltas ) )
{ // delta queries to make
if( empty( $install_db_deltas_confirm_md5 ) && !$force_install_db_deltas )
{ // delta queries have to be confirmed in payload
return false;
}
elseif( $install_db_deltas_confirm_md5 == md5( implode( '', $install_db_deltas ) ) || $force_install_db_deltas )
{ // Confirmed in first step:
foreach( $install_db_deltas as $query )
{
$DB->query( $query );
}
$Messages->add( T_('The database has been updated.'), 'success' );
}
else
{ // should not happen
$Messages->add( T_('The DB schema has been changed since confirmation.'), 'error' );
// delta queries have to be confirmed (again) in payload
return false;
}
}
}
return true;
}
?>