<?php /*

 ocPortal
 Copyright (c) ocProducts, 2004-2012

 See text/EN/licence.txt for full licencing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license		http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright	ocProducts Ltd
 * @package		core_form_interfaces
 */

/**
 * Standard code module initialisation function.
 */
function init__form_templates()
{
	require_javascript('javascript_validation');

	global $WYSIWYG_ATTACHED;
	$WYSIWYG_ATTACHED=false;

	global $TABINDEX;
	$TABINDEX=50; // Base

	global $NO_DEV_MODE_FULLSTOP_CHECK;
	$NO_DEV_MODE_FULLSTOP_CHECK=false;

	require_code('input_filter');

	global $DOING_ALTERNATE_FIELDS_SET;
	$DOING_ALTERNATE_FIELDS_SET=mixed();

	require_css('forms');

	@header('X-Frame-Options: SAMEORIGIN'); // Clickjacking protection
}

/**
 * Insert hidden data for the maximum file size of form fields.
 *
 * @param  tempcode			Hidden fields
 * @param  ID_TEXT			Code representing the media types we are using limits for
 * @set image file
 */
function handle_max_file_size(&$hidden,$regular_max_size_type='file')
{
	require_code('files2');
	if (!$GLOBALS['FORUM_DRIVER']->is_staff(get_member()))
	{
		switch ($regular_max_size_type)
		{
			case 'image':
				require_code('images');
				$regular_max_size=get_max_image_size();
				break;
			case 'file':
			default:
				$regular_max_size=get_max_file_size();
				break;
		}
		$hidden->attach(form_input_hidden('MAX_FILE_SIZE',strval($regular_max_size)));
	} else
	{
		$hidden->attach(form_input_hidden('MAX_FILE_SIZE',strval(get_max_file_size())));
	}
}

/**
 * Get what we need to get attachments in a form-field interface.
 *
 * @param  ID_TEXT		The name of the field attachments are for
 * @return array			A pair: the attachments UI (tempcode), the hidden attachment field
 */
function get_attachments($posting_field_name)
{
	$image_types=str_replace(',',', ',get_option('valid_images'));

	require_lang('javascript');
	require_javascript('javascript_swfupload');
	require_css('swfupload');

	require_code('files2');
	$max_attach_size=get_max_file_size(get_member(),$GLOBALS['SITE_DB']);
	if (($max_attach_size==0) && (ocf_get_member_best_group_property(get_member(),'max_daily_upload_mb')==0))
	{
		return array(new ocp_tempcode(),new ocp_tempcode());
	}
	$attach_size_field=form_input_hidden('MAX_FILE_SIZE',strval($max_attach_size));

	$num_attachments=post_param_integer('num_attachments',has_js()?1:3);

	$attachments=new ocp_tempcode();
	for ($i=1;$i<=$num_attachments;$i++)
	{
		$attachments->attach(do_template('ATTACHMENT',array('_GUID'=>'c3b38ca70cbd1c5f9cf91bcae9ed1134','POSTING_FIELD_NAME'=>$posting_field_name,'I'=>strval($i))));
	}

	if (get_forum_type()=='ocf')
	{
		require_code('ocf_groups');
		require_lang('ocf');
		$max_attachments=ocf_get_member_best_group_property(get_member(),'max_attachments_per_post');
	} else $max_attachments=100;

	$attachment_template=do_template('ATTACHMENT',array('_GUID'=>'c3b38ca70cbd1c5f9cf91bcae9ed11dsds','POSTING_FIELD_NAME'=>$posting_field_name,'I'=>'__num_attachments__'));
	$attachments=do_template('ATTACHMENTS',array('_GUID'=>'054921e7c09412be479676759accf222','POSTING_FIELD_NAME'=>$posting_field_name,'ATTACHMENT_TEMPLATE'=>$attachment_template,'IMAGE_TYPES'=>$image_types,'ATTACHMENTS'=>$attachments,'MAX_ATTACHMENTS'=>strval($max_attachments),'NUM_ATTACHMENTS'=>strval($num_attachments)));

	return array($attachments,$attach_size_field);
}

/**
 * Creates a posting form, with attachment support.
 *
 * @param  mixed			The title of the form submission button
 * @param  LONG_TEXT		The default post to put in.
 * @param  mixed			Where the form is sent (URLPATH or Tempcode).
 * @param  tempcode		A form_input_hidden buildup of hidden fields (additional parameters sent to the target URL).
 * @param  tempcode		A buildup of leading extra fields, in a format compatible with the templates used by this function.
 * @param  ?mixed			The post comment (string or Tempcode). This gives information about how you should post. (NULL: no post comment)
 * @param  string			Extra info to put on the posting form
 * @param  ?tempcode		A buildup of trailing extra fields, in a format compatible with the templates used by this function. (NULL: none)
 * @param  ?tempcode		The parsed Comcode. (NULL: calculate)
 * @param  ?string		Javascript code to include (NULL: none)
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  boolean		Whether this is a required input field
 * @param  boolean		Whether the form supports previewing
 * @return tempcode		The posting form
 */
function get_posting_form($submit_name,$post,$post_url,$hidden_fields,$specialisation,$post_comment=NULL,$extra='',$specialisation2=NULL,$default_parsed=NULL,$javascript=NULL,$tabindex=NULL,$required=true,$has_preview=true)
{
	require_lang('javascript');
	require_javascript('javascript_posting');
	require_javascript('javascript_editing');
	require_javascript('javascript_ajax');
	require_javascript('javascript_swfupload');
	require_css('swfupload');

	require_lang('comcode');

	$tabindex=get_form_field_tabindex($tabindex);

	$post=filter_form_field_default(is_object($submit_name)?$submit_name->evaluate():$submit_name,$post);

	check_suhosin_request_size(strlen($post));

	if (is_null($post_comment)) $post_comment=do_lang_tempcode('POST_COMMENT');
	if (is_null($specialisation2)) $specialisation2=new ocp_tempcode();

	list($attachments,$attach_size_field)=get_attachments('post');

	$hidden_fields->attach($attach_size_field);

	$continue_url=get_self_url();

	$comcode_help=build_url(array('page'=>'userguide_comcode'),get_comcode_zone('userguide_comcode',false));

	$emoticon_chooser=$GLOBALS['FORUM_DRIVER']->get_emoticon_chooser();

	$comcode_editor=get_comcode_editor();
	$comcode_editor_small=get_comcode_editor('post',true);

	$w=/* (has_specific_permission(get_member(),'comcode_dangerous')) && */(has_js()) && (browser_matches('wysiwyg')) && (strpos($post,'{$,page hint: no_wysiwyg}')===false);

	$class='';
	global $JAVASCRIPT,$WYSIWYG_ATTACHED;
	if (!$WYSIWYG_ATTACHED)
		$JAVASCRIPT->attach(do_template('HTML_EDIT'));
	$WYSIWYG_ATTACHED=true;
	@header('Content-type: text/html; charset='.get_charset());

	if ($w) $class.=' wysiwyg';

	global $LAX_COMCODE;
	$temp=$LAX_COMCODE;
	$LAX_COMCODE=true;
	$GLOBALS['COMCODE_PARSE_URLS_CHECKED']=100; // Little hack to stop it checking any URLs
	/*if (is_null($default_parsed)) */$default_parsed=@comcode_to_tempcode($post,NULL,false,60,NULL,NULL,true);
	$LAX_COMCODE=$temp;

	return do_template('POSTING_FORM',array(
		'_GUID'=>'41259424ca13c437d5bc523ce18980fe',
		'REQUIRED'=>$required,
		'TABINDEX_PF'=>strval($tabindex)/*not called TABINDEX due to conflict with FORM_STANDARD_END*/,
		'JAVASCRIPT'=>$javascript,
		'PREVIEW'=>$has_preview,
		'COMCODE_EDITOR'=>$comcode_editor,
		'COMCODE_EDITOR_SMALL'=>$comcode_editor_small,
		'CLASS'=>$class,
		'COMCODE_URL'=>build_url(array('page'=>'userguide_comcode'),get_comcode_zone('userguide_comcode',false)),
		'EXTRA'=>$extra,
		'POST_COMMENT'=>$post_comment,
		'EMOTICON_CHOOSER'=>$emoticon_chooser,
		'SUBMIT_NAME'=>$submit_name,
		'HIDDEN_FIELDS'=>$hidden_fields,
		'COMCODE_HELP'=>$comcode_help,
		'URL'=>$post_url,
		'POST'=>$post,
		'DEFAULT_PARSED'=>$default_parsed,
		'CONTINUE_URL'=>$continue_url,
		'ATTACHMENTS'=>$attachments,
		'SPECIALISATION'=>$specialisation,
		'SPECIALISATION2'=>$specialisation2,
	));
}

/**
 * Creates a Comcode editor.
 *
 * @param  string			The name of the field the editor is working for
 * @param  boolean		Whether to make a cut-down version
 * @return tempcode		The Comcode editor
 */
function get_comcode_editor($field_name='post',$cut_down=false)
{
	require_lang('comcode');

	$buttons=new ocp_tempcode();
	$_buttons=array();

	// Non-wrappers
	if (!$cut_down) $_buttons[]=(get_option('is_on_gd')=='0')?'img':'thumb';
	if (has_specific_permission(get_member(),'comcode_dangerous'))
	{
		$_buttons[]='block';
	}
	$_buttons[]='comcode';
	if (!$cut_down) $_buttons[]='list'; // NB: list isn't actually a comcode tag, it's a textcode syntax

	// Links
	if (!$cut_down) $_buttons[]='url';
	if (has_zone_access(get_member(),'adminzone'))
	{
		$_buttons[]='page';
	}
	//if (!$cut_down) $_buttons[]='email';	Not enough space any more

	// Wrappers
	$_buttons[]='quote';
	if ((get_value('simplify_wysiwyg_by_permissions')!=='1') || (has_specific_permission(get_member(),'allow_html')))
		$_buttons[]='box';
	$_buttons[]='code';
	//$_buttons[]='hide';
	if (has_specific_permission(get_member(),'allow_html'))
	{
		if (!$cut_down) $_buttons[]='html';
	}
	foreach ($_buttons as $i=>$button)
	{
		$divider=false;
		if (($button=='url') || ($button=='quote') || ($i==0)) $divider=true;
		$buttons->attach(do_template('COMCODE_EDITOR_BUTTON',array('_GUID'=>'e4fe3bc16cec070e06532fedc598d075','DIVIDER'=>$divider,'FIELD_NAME'=>$field_name,'TITLE'=>do_lang_tempcode('INPUT_COMCODE_'.$button),'B'=>$button)));
	}

	$micro_buttons=new ocp_tempcode();
	if (!$cut_down)
	{
		$_micro_buttons=array(
			array('t'=>'b'),
			array('t'=>'i'),
		);
		foreach ($_micro_buttons as $button)
		{
			$micro_buttons->attach(do_template('COMCODE_EDITOR_MICRO_BUTTON',array('_GUID'=>'dbab001b3fa5480bb590ffed3ca81eaf','FIELD_NAME'=>$field_name,'TITLE'=>do_lang_tempcode('INPUT_COMCODE_'.$button['t']),'B'=>$button['t'])));
		}
	}
	return do_template('COMCODE_EDITOR',array('_GUID'=>'ebff3145776a0441d115f2e4e13617d6','POSTING_FIELD'=>$field_name,'BUTTONS'=>$buttons,'MICRO_BUTTONS'=>$micro_buttons));
}

/**
 * Find whether WYSIWYG is currently on.
 *
 * @return boolean		Whether it is
 */
function wysiwyg_on()
{
	return ((browser_matches('wysiwyg')) && ((!array_key_exists('use_wysiwyg',$_COOKIE)) || ($_COOKIE['use_wysiwyg']=='1')));
}

/**
 * Get the value of a scoped field restriction property. Returns "first-found".
 *
 * @param  string			The name of the property
 * @param  string			The name of the field it's scoped for
 * @param  ?string		The page name scoped for (NULL: current page)
 * @param  ?string		The page type scoped for (NULL: current type)
 * @return ?string		The property (NULL: non-existant)
 */
function get_field_restrict_property($property,$field,$page=NULL,$type=NULL)
{
	if (is_null($page)) $page=get_page_name();
	if (is_null($type)) $type=get_param('type',post_param('type','misc'));

	$restrictions=load_field_restrictions($page,$type);
	foreach ($restrictions as $_r=>$_restrictions)
	{
		$_r_exp=explode(',',$_r);
		foreach ($_r_exp as $__r)
		{
			if (simulated_wildcard_match($field,trim($__r),true))
			{
				foreach ($_restrictions as $bits)
				{
					list($restriction,$attributes)=$bits;
					if (strtolower($restriction)==strtolower($field)) return $bits['embed'];
				}
			}
		}
	}
	return NULL;
}

/**
 * Get the tempcode for a codename input line.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  ?string		The default value for this input field (NULL: blank)
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  ?integer		The maximum length of the field (NULL: default 80)
 * @return tempcode		The input field
 */
function form_input_codename($pretty_name,$description,$name,$default,$required,$tabindex=NULL,$_maxlength=NULL)
{
	if (is_null($default)) $default='';

	$default=filter_form_field_default($name,$default);

	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';
	$maxlength=get_field_restrict_property('maxlength',$name);
	if ((is_null($maxlength)) && (!is_null($_maxlength))) $maxlength=strval($_maxlength);
	$input=do_template('FORM_SCREEN_INPUT_CODENAME',array('_GUID'=>'4b1a3a3ebe6ac85c7c14bcec9d8ab88d','MAXLENGTH'=>$maxlength,'TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name,'DEFAULT'=>$default));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex);
}

/**
 * Get the tempcode for a text input line.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  ?string		The default value for this input field (NULL: blank)
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  ?integer		The maximum length of the field (NULL: default 255)
 * @param  string			The input type
 * @return tempcode		The input field
 */
function form_input_line($pretty_name,$description,$name,$default,$required,$tabindex=NULL,$_maxlength=NULL,$type='text')
{
	if (is_null($default)) $default='';

	$default=filter_form_field_default($name,$default);

	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';
	$maxlength=get_field_restrict_property('maxlength',$name);
	if ((is_null($maxlength)) && (!is_null($_maxlength))) $maxlength=strval($_maxlength);
	$input=do_template('FORM_SCREEN_INPUT_LINE',array('_GUID'=>'02789c9af25cbc971e86bfcc0ad322d5','MAXLENGTH'=>$maxlength,'TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name,'DEFAULT'=>$default,'TYPE'=>$type));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex);
}

/**
 * Get the tempcode for a username input line.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  ?string		The default value for this input field (NULL: blank)
 * @param  boolean		Whether this is a required input field
 * @param  boolean		Whether it is required than a valid username is given
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @return tempcode		The input field
 */
function form_input_username($pretty_name,$description,$name,$default,$required,$needs_match=true,$tabindex=NULL)
{
	if (is_null($default)) $default='';

	$default=filter_form_field_default($name,$default);

	require_javascript('javascript_ajax');
	require_javascript('javascript_ajax_people_lists');

	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';
	$input=do_template('FORM_SCREEN_INPUT_USERNAME',array('TABINDEX'=>strval($tabindex),'NEEDS_MATCH'=>$needs_match,'REQUIRED'=>$_required,'NAME'=>$name,'DEFAULT'=>$default));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex);
}

/**
 * Get the tempcode for a author/username input line.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  ?string		The default value for this input field (NULL: blank)
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @return tempcode		The input field
 */
function form_input_author($pretty_name,$description,$name,$default,$required,$tabindex=NULL)
{
	if (!addon_installed('authors')) return form_input_username($pretty_name,$description,$name,$default,$required,true,$tabindex);

	if (is_null($default)) $default='';

	$default=filter_form_field_default($name,$default);

	require_javascript('javascript_ajax');
	require_javascript('javascript_ajax_people_lists');

	$tabindex=get_form_field_tabindex($tabindex);

	$_description=new ocp_tempcode();
	$_description->attach($description);
	if (has_js())
	{
		$_description->attach(do_template('FORM_DESCRIP_SEP'));
		$keep=symbol_tempcode('KEEP');
		$extra=do_template('HYPERLINK_POPUP_WINDOW',array('_GUID'=>'fb25dc4777a166c143a1bc32ff0c3239','URL'=>find_script('authors').'?field_name='.urlencode($name).$keep->evaluate(),'TITLE'=>do_lang_tempcode('AUTHOR'),'CAPTION'=>do_lang_tempcode('BROWSE_SENTENCE')));
		$_description->attach($extra);
	}

	$_required=($required)?'_required':'';
	$input=do_template('FORM_SCREEN_INPUT_AUTHOR',array('TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name,'DEFAULT'=>$default));
	return _form_input($name,$pretty_name,$_description,$input,$required,false,$tabindex);
}

/**
 * Get the tempcode for a email-address input line.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  ?string		The default value for this input field (NULL: blank)
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @return tempcode		The input field
 */
function form_input_email($pretty_name,$description,$name,$default,$required,$tabindex=NULL)
{
	if (is_null($default)) $default='';

	$default=filter_form_field_default($name,$default);

	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';
	$input=do_template('FORM_SCREEN_INPUT_EMAIL',array('TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name,'DEFAULT'=>$default));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex);
}

/**
 * Get the tempcode for a colour input.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  ?string		The default value for this input field (NULL: blank)
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  boolean		Whether to display as a true/standard ocPortal input field, neatly along other different kinds of input fields
 * @return tempcode		The input field
 */
function form_input_colour($pretty_name,$description,$name,$default,$required,$tabindex=NULL,$true_field=false)
{
	if (!has_js())
	{
		return form_input_line($pretty_name,$description,$name,$default,$required,$tabindex);
	}

	require_javascript('javascript_theme_colours');

	if (is_null($default)) $default='';

	$default=filter_form_field_default($name,$default);

	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';
	$input=do_template('FORM_SCREEN_INPUT_COLOUR',array('_GUID'=>'9a1a8061cebd717ea98522984d9465af','TRUE_FIELD'=>$true_field,'REQUIRED'=>$required,'PRETTY_NAME'=>$pretty_name,'DESCRIPTION'=>$description,'TABINDEX'=>strval($tabindex),'_REQUIRED'=>$_required,'NAME'=>$name,'DEFAULT'=>$default));
	if ($true_field) $input=_form_input($name,$pretty_name,$description,$input,$required,false,$tabindex,false,true);
	return $input;
}

/**
 * Get the tempcode for a page-link input.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  ?string		The default value for this input field (NULL: blank)
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  ?ID_TEXT		Page type to show (NULL: all)
 * @param  boolean		Whether to also get the title for the page
 * @return tempcode		The input field
 */
function form_input_page_link($pretty_name,$description,$name,$default,$required,$tabindex=NULL,$page_type=NULL,$get_title_too=false)
{
	if (!has_js())
	{
		return form_input_line($pretty_name,$description,$name,$default,$required,$tabindex);
	}

	require_lang('menus');

	require_javascript('javascript_ajax');
	require_javascript('javascript_tree_list');
	require_javascript('javascript_more');

	// Display
	$input=do_template('PAGE_LINK_CHOOSER',array('AS_FIELD'=>true,'GET_TITLE_TOO'=>$get_title_too,'NAME'=>$name,'VALUE'=>$default,'PAGE_TYPE'=>$page_type));

	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex,false,true);
}

/**
 * Get the tempcode for a comcode-enabled text input line.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  ?string		The default value for this input field (NULL: blank)
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @return tempcode		The input field
 */
function form_input_line_comcode($pretty_name,$description,$name,$default,$required,$tabindex=NULL)
{
	require_lang('comcode');

	if (is_null($default)) $default='';

	$default=filter_form_field_default($name,$default);

	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';
	$input=do_template('FORM_SCREEN_INPUT_LINE',array('MAXLENGTH'=>get_field_restrict_property('maxlength',$name),'TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name,'DEFAULT'=>$default));
	return _form_input($name,$pretty_name,$description,$input,$required,true,$tabindex);
}

/**
 * Get the tempcode for a DHTML input field that takes multiple lines. A new line is added when the prior one isn't blank.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The base parameter name which this input field is for (as this takes multiple parameters, they are named <name><x>). This name must end with '_'.
 * @param  array			An array of lines to use as default (at least this many lines, filled by this array, will be presented by default)
 * @param  integer		The minimum number of inputs allowed.
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  string			CSS class for input.
 * @set    line email
 * @return tempcode		The input field
 */
function form_input_line_multi($pretty_name,$description,$name,$default_array,$num_required,$tabindex=NULL,$class='line')
{
	require_javascript('javascript_multi');

	if (substr($name,-1)!='_' && substr($name,-2)!='[]') $name.='_';

	$tabindex=get_form_field_tabindex($tabindex);

	$default_array[0]=filter_form_field_default($name,array_key_exists(0,$default_array)?$default_array[0]:'');

	$input=new ocp_tempcode();
	$i=0;
	foreach ($default_array as $default)
	{
		$_required=($i<$num_required)?'_required':'';
		$input->attach(do_template('FORM_SCREEN_INPUT_LINE_MULTI',array('_GUID'=>'e2da34b7564cebfd83da2859e4abd020','CLASS'=>$class,'MAXLENGTH'=>get_field_restrict_property('maxlength',$name),'PRETTY_NAME'=>$pretty_name,'TABINDEX'=>strval($tabindex),'NAME_STUB'=>$name,'I'=>strval($i),'REQUIRED'=>$_required,'DEFAULT'=>$default)));
		$i++;
	}
	$num_to_show_initially=has_js()?$num_required:max($num_required,10);
	for (;$i<$num_to_show_initially;$i++)
	{
		$input->attach(do_template('FORM_SCREEN_INPUT_LINE_MULTI',array('_GUID'=>'10fcbe72e80ea1be07c3dd1fd9e0719e','CLASS'=>$class,'MAXLENGTH'=>get_field_restrict_property('maxlength',$name),'PRETTY_NAME'=>$pretty_name,'TABINDEX'=>strval($tabindex),'NAME_STUB'=>$name,'I'=>strval($i),'REQUIRED'=>($i>=$num_required)?'':'_required','DEFAULT'=>'')));
	}
	return _form_input(preg_replace('#\[\]$#','',$name),$pretty_name,$description,$input,$num_required>0,false,$tabindex,false,true);
}

/**
 * Get the tempcode for a DHTML input field that takes multiple textareas. A new textarea is added when the prior one isn't blank.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The base parameter name which this input field is for (as this takes multiple parameters, they are named <name><x>). This name must end with '_'.
 * @param  array			An array of texts to use as default (at least this many textareas, filled by this array, will be presented by default)
 * @param  integer		The minimum number of textareas allowed.
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @return tempcode		The input field
 */
function form_input_text_multi($pretty_name,$description,$name,$default_array,$num_required,$tabindex=NULL)
{
	require_javascript('javascript_multi');

	if (substr($name,-1)!='_') $name.='_';

	$tabindex=get_form_field_tabindex($tabindex);

	$default_array[0]=filter_form_field_default($name,array_key_exists(0,$default_array)?$default_array[0]:'');

	$input=new ocp_tempcode();
	$i=0;
	foreach ($default_array as $default)
	{
		$_required=($i<$num_required)?'_required':'';
		$input->attach(do_template('FORM_SCREEN_INPUT_TEXT_MULTI',array('_GUID'=>'0d9e3c073d09d1ce3725f47813375c28','PRETTY_NAME'=>$pretty_name,'TABINDEX'=>strval($tabindex),'NAME_STUB'=>$name,'I'=>strval($i),'REQUIRED'=>$_required,'DEFAULT'=>$default)));
		$i++;
	}
	if (!has_js()) $num_required=max($num_required,10);
	for (;$i<$num_required;$i++)
	{
		$input->attach(do_template('FORM_SCREEN_INPUT_TEXT_MULTI',array('_GUID'=>'2e816a71ef5a9ac9e1aac4bd1c13b5bd','PRETTY_NAME'=>$pretty_name,'TABINDEX'=>strval($tabindex),'NAME_STUB'=>$name,'I'=>strval($i),'REQUIRED'=>'_required','DEFAULT'=>'')));
	}
	return _form_input($name,$pretty_name,$description,$input,$num_required>0,false,$tabindex,false,true);
}

/**
 * Get the tempcode for a username input line.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The base parameter name which this input field is for (as this takes multiple parameters, they are named <name><x>). This name must end with '_'.
 * @param  array			An array of lines to use as default (at least this many lines, filled by this array, will be presented by default)
 * @param  integer		The minimum number of inputs allowed
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @return tempcode		The input field
 */
function form_input_username_multi($pretty_name,$description,$name,$default_array,$num_required,$needs_match=true,$tabindex=NULL)
{
	if (substr($name,-1)!='_') $name.='_';

	require_javascript('javascript_multi');
	require_javascript('javascript_ajax');
	require_javascript('javascript_ajax_people_lists');

	$tabindex=get_form_field_tabindex($tabindex);

	$input=new ocp_tempcode();
	$i=0;
	foreach ($default_array as $default)
	{
		if (is_null($default)) $default='';
		$default=filter_form_field_default($name,$default);

		$_required=($i<$num_required)?'_required':'';
		$input->attach(do_template('FORM_SCREEN_INPUT_USERNAME_MULTI',array('_GUID'=>'f2adcb1464b13e339a0336db6d5228cb','PRETTY_NAME'=>$pretty_name,'TABINDEX'=>strval($tabindex),'NEEDS_MATCH'=>$needs_match,'NAME_STUB'=>$name,'I'=>strval($i),'REQUIRED'=>$_required,'DEFAULT'=>$default)));
		$i++;
	}
	if (!has_js()) $num_required=max($num_required,10);
	if ($num_required>$i) $_num_required=$num_required; else $_num_required=$i+1;
	for (;$i<$_num_required;$i++)
	{
		$_required=($i<$num_required)?'_required':'';

		$input->attach(do_template('FORM_SCREEN_INPUT_USERNAME_MULTI',array('_GUID'=>'4bc8a187ee5fac91275f66f78478a3c6','PRETTY_NAME'=>$pretty_name,'TABINDEX'=>strval($tabindex),'NEEDS_MATCH'=>$needs_match,'NAME_STUB'=>$name,'I'=>strval($i),'REQUIRED'=>$_required,'DEFAULT'=>'')));
	}

	return _form_input($name,$pretty_name,$description,$input,$num_required>0,false,$tabindex);
}

/**
 * Get the tempcode for a text input (textarea).
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  string			The default value for this input field
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  boolean		Whether the field scrolls
 * @return tempcode		The input field
 */
function form_input_text($pretty_name,$description,$name,$default,$required,$tabindex=NULL,$scrolls=false)
{
	$tabindex=get_form_field_tabindex($tabindex);

	$default=filter_form_field_default($name,$default);

	check_suhosin_request_size(strlen($default));

	$_required=($required)?'_required':'';

	$input=do_template('FORM_SCREEN_INPUT_TEXT',array('_GUID'=>'01626015c6ae36b1027e35e66a8b5d0b','RAW'=>true,'SCROLLS'=>$scrolls,'TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name,'DEFAULT'=>$default));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex,true);
}

/**
 * Get the tempcode for a comcode-enabled text input (textarea).
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  string			The default value for this input field
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  boolean		Force non-WYSIWYG and non default-Comcode parsing
 * @param  mixed			A secondary side description for this input field
 * @param  ?tempcode		The parsed Comcode. (NULL: calculate)
 * @param  boolean		Whether the field scrolls
 * @return tempcode		The input field
 */
function form_input_text_comcode($pretty_name,$description,$name,$default,$required,$tabindex=NULL,$force_non_wysiwyg=false,$description_side='',$default_parsed=NULL,$scrolls=false)
{
	if ((browser_matches('wysiwyg')) && (!$force_non_wysiwyg) && (strpos($default,'{$,page hint: no_wysiwyg}')===false))
		return form_input_huge_comcode($pretty_name,$description,$name,$default,$required,$tabindex,10,$description_side,$default_parsed,$scrolls);

	require_lang('comcode');

	require_javascript('javascript_editing');
	require_javascript('javascript_ajax');

	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';
	$default_parsed=new ocp_tempcode();

	$default=filter_form_field_default($name,$default);

	check_suhosin_request_size(strlen($default));

	if (!$force_non_wysiwyg)
	{
		global $JAVASCRIPT,$WYSIWYG_ATTACHED;
		if (!$WYSIWYG_ATTACHED)
			$JAVASCRIPT->attach(do_template('HTML_EDIT'));
		$WYSIWYG_ATTACHED=true;
		@header('Content-type: text/html; charset='.get_charset());

		$w=/* (has_specific_permission(get_member(),'comcode_dangerous')) && */(browser_matches('wysiwyg')) && (has_js()) && (strpos($default,'{$,page hint: no_wysiwyg}')===false);
		if ($w) $_required.=' wysiwyg';
		global $LAX_COMCODE;
		$temp=$LAX_COMCODE;
		$LAX_COMCODE=true;
		$GLOBALS['COMCODE_PARSE_URLS_CHECKED']=100; // Little hack to stop it checking any URLs
		/*if (is_null($default_parsed)) */$default_parsed=@comcode_to_tempcode($default,NULL,false,60,NULL,NULL,true);
		$LAX_COMCODE=$temp;
	} else
	{
		$w=false;
		$default_parsed=new ocp_tempcode();
	}

	$input=do_template('FORM_SCREEN_INPUT_TEXT',array('_GUID'=>'ff53196e943e7b19bc72fc3bbb3238b5','SCROLLS'=>$scrolls,'ROWS'=>((is_object($description_side)) || ($description_side!=''))?'16':'8','TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name,'DEFAULT'=>$default,'DEFAULT_PARSED'=>$default_parsed));

	return _form_input($name,$pretty_name,$description,$input,$required,true,$tabindex,$w,false,$description_side);
}

/**
 * Get the tempcode for a huge comcode-enabled text input (textarea). These need extra space to fit. This function is also used as an automatic replacement for form_input_text_comcode if WYSIWYG is available (as WYSIWYG needs more space too)
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  string			The default value for this input field
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  integer		The number of rows to use
 * @param  mixed			A secondary side description for this input field
 * @param  ?tempcode		The parsed Comcode. (NULL: calculate)
 * @param  boolean		Whether the field scrolls
 * @return tempcode		The input field
 */
function form_input_huge_comcode($pretty_name,$description,$name,$default,$required,$tabindex=NULL,$rows=20,$description_side='',$default_parsed=NULL,$scrolls=false)
{
	require_lang('comcode');

	require_javascript('javascript_editing');
	require_javascript('javascript_ajax');

	$tabindex=get_form_field_tabindex($tabindex);

	$default=filter_form_field_default($name,$default);

	check_suhosin_request_size(strlen($default));

	$_required=($required)?'_required':'';
	$default_parsed=new ocp_tempcode();

	global $JAVASCRIPT,$WYSIWYG_ATTACHED;
	if (!$WYSIWYG_ATTACHED)
		$JAVASCRIPT->attach(do_template('HTML_EDIT'));
	$WYSIWYG_ATTACHED=true;
	@header('Content-type: text/html; charset='.get_charset());

	$w=/* (has_specific_permission(get_member(),'comcode_dangerous')) && */(browser_matches('wysiwyg')) && (has_js()) && (strpos($default,'{$,page hint: no_wysiwyg}')===false);
	if ($w) $_required.=' wysiwyg';
	global $LAX_COMCODE;
	$temp=$LAX_COMCODE;
	$LAX_COMCODE=true;
	$GLOBALS['COMCODE_PARSE_URLS_CHECKED']=100; // Little hack to stop it checking any URLs
	/*if (is_null($default_parsed)) */$default_parsed=@comcode_to_tempcode($default,NULL,false,60,NULL,NULL,true);
	$LAX_COMCODE=$temp;

	$_comcode=do_template('COMCODE_MESSAGE',array('_GUID'=>'fbcf2413f754ca5829b9f4c908746843','NAME'=>$name,'W'=>$w,'URL'=>build_url(array('page'=>'userguide_comcode'),get_comcode_zone('userguide_comcode',false))));

	return do_template('FORM_SCREEN_INPUT_HUGE_COMCODE',array('_GUID'=>'b8231827be2f4a00e12fcd8986119588','SCROLLS'=>$scrolls,'DESCRIPTION_SIDE'=>$description_side,'REQUIRED'=>$required,'_REQUIRED'=>$_required,'TABINDEX'=>strval($tabindex),'COMCODE'=>$_comcode,'PRETTY_NAME'=>$pretty_name,'DESCRIPTION'=>$description,'NAME'=>$name,'DEFAULT'=>$default,'DEFAULT_PARSED'=>$default_parsed,'ROWS'=>strval($rows)));
}

/**
 * Get the tempcode for a huge text input (textarea).
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  string			The default value for this input field
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  integer		The number of rows to use
 * @param  mixed			A secondary side description for this input field
 * @param  boolean		Whether the field scrolls
 * @return tempcode		The input field
 */
function form_input_huge($pretty_name,$description,$name,$default,$required,$tabindex=NULL,$rows=20,$description_side='',$scrolls=false)
{
	$tabindex=get_form_field_tabindex($tabindex);

	$default=filter_form_field_default($name,$default);

	check_suhosin_request_size(strlen($default));

	$_required=($required)?'_required':'';
	$default_parsed=new ocp_tempcode();

	return do_template('FORM_SCREEN_INPUT_HUGE',array('_GUID'=>'9d51961cd53c3fcadb8f83b905b2bbea','RAW'=>true,'SCROLLS'=>$scrolls,'DESCRIPTION_SIDE'=>$description_side,'REQUIRED'=>$required,'_REQUIRED'=>$_required,'TABINDEX'=>strval($tabindex),'PRETTY_NAME'=>$pretty_name,'DESCRIPTION'=>$description,'NAME'=>$name,'DEFAULT'=>$default,'ROWS'=>strval($rows)));
}

/**
 * Get the tempcode for a password input.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  string			The default value for this input field
 * @return tempcode		The input field
 */
function form_input_password($pretty_name,$description,$name,$required,$tabindex=NULL,$default='')
{
	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';
	$input=do_template('FORM_SCREEN_INPUT_PASSWORD',array('TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name,'VALUE'=>$default));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex);
}

/**
 * Get the tempcode for a checkbox input.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  boolean		Whether this is ticked by default
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  ID_TEXT		The value the checkbox passes when ticked
 * @return tempcode		The input field
 */
function form_input_tick($pretty_name,$description,$name,$ticked,$tabindex=NULL,$value='1')
{
	$tabindex=get_form_field_tabindex($tabindex);

	$ticked=(filter_form_field_default($name,$ticked?'1':'0')=='1');

	$input=do_template('FORM_SCREEN_INPUT_TICK',array('_GUID'=>'340a68c271b838d327f042d101df27eb','VALUE'=>$value,'CHECKED'=>$ticked,'TABINDEX'=>strval($tabindex),'NAME'=>$name));
	return _form_input($name,$pretty_name,$description,$input,false,false,$tabindex);
}

/**
 * Get the tempcode for a bank of tick boxes.
 *
 * @param  array			A list of tuples: (prettyname, name, value, description)
 * @param  mixed			A description for this input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  mixed			A human intelligible name for this input field (blank: use default)
 * @param  boolean		Whether to place each tick on a new line
 * @param  ?ID_TEXT		Name for custom value to be entered to (NULL: no custom value allowed)
 * @param  ?string		Value for custom value (NULL: no custom value known)
 * @return tempcode		The input field
 */
function form_input_various_ticks($options,$description,$_tabindex=NULL,$_pretty_name='',$simple_style=false,$custom_name=NULL,$custom_value=NULL)
{
	if (count($options)==0) return new ocp_tempcode();

	$options=array_values($options);

	if (is_null($_tabindex))
	{
		$tabindex=get_form_field_tabindex(NULL);
	} else
	{
		$_tabindex++;
		$tabindex=$_tabindex;
	}

	if ((is_string($_pretty_name)) && ($_pretty_name=='')) $_pretty_name=do_lang_tempcode('OPTIONS');

	$input=new ocp_tempcode();

	if (count($options[0])!=3)
	{
		$options=array(array($options,NULL,new ocp_tempcode()));
	}
	foreach ($options as $_option)
	{
		$out=array();
		foreach ($_option[0] as $option)
		{
			list($pretty_name,$name,$value,$_description)=$option;

			$value=(filter_form_field_default($name,$value?'1':'0')=='1');

			$out[]=array('CHECKED'=>$value,'TABINDEX'=>strval($tabindex),'NAME'=>$name,'PRETTY_NAME'=>$pretty_name,'DESCRIPTION'=>$_description);
		}

		$input->attach(do_template('FORM_SCREEN_INPUT_VARIOUS_TICKS',array('_GUID'=>'a6212f61304a101fb2754e334a8b4212','CUSTOM_NAME'=>$custom_name,'CUSTOM_VALUE'=>$custom_value,'SECTION_TITLE'=>$_option[2],'EXPANDED'=>$_option[1],'SIMPLE_STYLE'=>$simple_style,'BRETHREN_COUNT'=>strval(count($out)),'OUT'=>$out)));
	}
	return _form_input('',$_pretty_name,$description,$input,false,false,$tabindex);
}

/**
 * Get the tempcode for a file upload input.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  boolean		Whether this is a required input field. Set this to false if you are using this field on an edit form and already have an uploaded file -- therefore you'd know no new file would mean not to replace the existing file
 * @param  ?string		The default value for the field (NULL: none) (blank: none). Should only be passed if $required is false, because it creates a delete button for the existing file, implying that leaving it with no file is valid
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  boolean		Whether swf-upload-style is preferred
 * @param  string			File-type filter to limit to, comma-separated file extensions (might not be supported)
 * @return tempcode		The input field
 */
function form_input_upload($pretty_name,$description,$name,$required,$default=NULL,$tabindex=NULL,$swfupload=true,$filter='')
{
	require_lang('javascript');
	if ($swfupload)
	{
		require_javascript('javascript_swfupload');
		require_css('swfupload');
	}

	if ($default==='') $default=NULL;

	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';
	$is_image=false;
	$existing_url='';
	if (!is_null($default))
	{
		require_code('images');
		$is_image=is_image($default);
		$existing_url=$default;
		if (url_is_local($existing_url)) $existing_url=get_custom_base_url().'/'.$existing_url;
	}
	$input=do_template('FORM_SCREEN_INPUT_UPLOAD',array('_GUID'=>'f493edcc5298bb32fff8635f2d316d21','FILTER'=>$filter,'PRETTY_NAME'=>$pretty_name,'EXISTING_URL'=>$existing_url,'IS_IMAGE'=>$is_image,'SWFUPLOAD'=>$swfupload,'EDIT'=>((!is_null($default)) && (!$required)),'TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex);
}

/**
 * Get the tempcode for a multiple file upload input.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  string			The base name which this input field is for
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  ?array			The default value for the field (NULL: none)
 * @param  boolean		Whether swf-upload-style is preferred
 * @param  string			File-type filter to limit to, comma-separated file extensions (might not be supported)
 * @return tempcode		The input field
 */
function form_input_upload_multi($pretty_name,$description,$name,$required,$tabindex=NULL,$default=NULL,$swfupload=true,$filter='')
{
	require_lang('javascript');
	if ($swfupload)
	{
		require_javascript('javascript_swfupload');
		require_css('swfupload');
	}
	require_javascript('javascript_multi');

	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';
	$is_image=false;
	$existing_url='';
	if (!is_null($default))
	{
		require_code('images');
		$is_image=is_image($default[0]);
		$existing_url=$default[0];
		if (url_is_local($existing_url)) $existing_url=get_custom_base_url().'/'.$existing_url;
	}
	$input=do_template('FORM_SCREEN_INPUT_UPLOAD_MULTI',array('TABINDEX'=>strval($tabindex),'FILTER'=>$filter,'REQUIRED'=>$_required,'SWFUPLOAD'=>$swfupload,'NAME'=>$name,'I'=>'1','NAME_STUB'=>$name));
	return _form_input('',$pretty_name,$description,$input,$required,false,$tabindex,false,true);
}

/**
 * Get the tempcode for a listbox.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  tempcode		The list entries for our list
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  boolean		Whether this is an inline displayed list as opposed to a dropdown
 * @param  boolean		Whether this is required
 * @return tempcode		The input field
 */
function form_input_list($pretty_name,$description,$name,$content,$tabindex=NULL,$inline_list=false,$required=true)
{
	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';
	$input=do_template('FORM_SCREEN_INPUT_LIST',array('TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name,'CONTENT'=>$content,'INLINE_LIST'=>$inline_list));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex);
}

/**
 * Get the tempcode for an AJAX-powered tree listbox.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  ?ID_TEXT		The ID to do under (NULL: root)
 * @param  string			The ajax tree-list hook that drives our data
 * @param  array			A map of special options
 * @param  boolean		Whether this is a required input field
 * @param  ?string		The default value for the field (NULL: none)
 * @param  boolean		Whether to use the server-ID in the list instead of the ID in the list
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  boolean		Whether multiple selections are allowed
 * @return tempcode		The input field
 */
function form_input_tree_list($pretty_name,$description,$name,$root_id,$hook,$options,$required,$default=NULL,$use_server_id=false,$tabindex=NULL,$multi_select=false)
{
	require_javascript('javascript_tree_list');
	require_javascript('javascript_more');

	require_code('hooks/systems/ajax_tree/'.$hook);
	$object=object_factory('Hook_'.$hook);

	if ((!has_js()) || (get_option('tree_lists')=='0'))
	{
		$simple_content=$object->simple($root_id,$options,$default);

		if ($simple_content->is_empty())
		{
			if ($required)
				inform_exit(do_lang_tempcode('NO_OF_THIS',$pretty_name));
			return new ocp_tempcode();
		}
		if ($multi_select)
		{
			return form_input_multi_list($pretty_name,$description,$name,$simple_content,$tabindex,10,$required);
		}

		return form_input_list($pretty_name,$description,$name,$simple_content,$tabindex,false,$required);
	}

	$tabindex=get_form_field_tabindex($tabindex);

	require_javascript('javascript_ajax');

	$nice_label=$default;
	/*if (!is_null($default)) Now we auto-expand to it
	{
		$simple_content=$object->simple($root_id,$options,$default);

		$simple_content_evaluated=$simple_content->evaluate();
		$matches=array();
		if (preg_match('#<option [^>]*value="'.str_replace('#','\#',preg_quote($default)).'"[^>]*>([^>]* &gt; )?([^>]*)</option>#',$simple_content_evaluated,$matches)!=0)
		{
			$nice_label=preg_replace('# \(.*\)#','',trim($matches[2]));
		}
	}*/

	$_required=($required)?'_required':'';
	$input=do_template('FORM_SCREEN_INPUT_TREE_LIST',array('_GUID'=>'21e9644eeac24356f38459ebe37f693a','MULTI_SELECT'=>$multi_select,'NICE_LABEL'=>(is_null($nice_label) || $nice_label=='-1')?'':$nice_label,'USE_SERVER_ID'=>$use_server_id,'TABINDEX'=>strval($tabindex),'NAME'=>$name,'REQUIRED'=>$_required,'DEFAULT'=>is_null($default)?'':$default,'HOOK'=>$hook,'ROOT_ID'=>is_null($root_id)?'':$root_id,'OPTIONS'=>serialize($options)));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex);
}

/**
 * Get the tempcode for a huge listbox.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  tempcode		The list entries for our list
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  boolean		Whether this is an inline displayed list as opposed to a dropdown
 * @param  boolean		Whether this is required
 * @param  ?integer		Size of list (NULL: default)
 * @return tempcode		The input field
 */
function form_input_huge_list($pretty_name,$description,$name,$content,$tabindex=NULL,$inline_list=false,$required=true,$size=NULL)
{
	$tabindex=get_form_field_tabindex($tabindex);

	$_required=($required)?'_required':'';

	return do_template('FORM_SCREEN_INPUT_HUGE_LIST',array('TABINDEX'=>strval($tabindex),'SIZE'=>is_null($size)?NULL:strval($size),'REQUIRED'=>$_required,'PRETTY_NAME'=>$pretty_name,'DESCRIPTION'=>$description,'NAME'=>$name,'CONTENT'=>$content,'INLINE_LIST'=>$inline_list));
}

/**
 * Get the tempcode for a listbox with multiple selections.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  tempcode		The list entries for our list
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  integer		How much space the list takes up
 * @param  boolean		Whether at least one must be selected
 * @return tempcode		The input field
 */
function form_input_multi_list($pretty_name,$description,$name,$content,$tabindex=NULL,$size=5,$required=false)
{
	$tabindex=get_form_field_tabindex($tabindex);

	$input=do_template('FORM_SCREEN_INPUT_MULTI_LIST',array('TABINDEX'=>strval($tabindex),'SIZE'=>strval($size),'NAME'=>$name,'CONTENT'=>$content));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex);
}

/**
 * Get the tempcode for a complex input that chooses partials from a list ('all', 'all-except-these', or 'these').
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  string			The base name which this input field is for
 * @param  tempcode		A list culmulation to select against
 * @param  string			The current type of partial selection
 * @set    + - *
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @return tempcode		The input field
 */
function form_input_all_and_not($pretty_name,$description,$base,$list,$type='+',$tabindex=NULL)
{
	$tabindex=get_form_field_tabindex($tabindex);

	$type=filter_form_field_default($base,$type);

	$radios=new ocp_tempcode();
	$radios->attach(form_input_radio_entry($base,'*',$type=='*',do_lang_tempcode('USE_ALL'),$tabindex));
	$radios->attach(form_input_radio_entry($base,'-',$type=='-',do_lang_tempcode('USE_ALL_EXCEPT_SELECTED'),$tabindex));
	$radios->attach(form_input_radio_entry($base,'+',$type=='+',do_lang_tempcode('USE_ALL_SELECTED'),$tabindex));
	$input=do_template('FORM_SCREEN_INPUT_ALL_AND_NOT',array('TABINDEX'=>strval($tabindex),'BASE'=>$base,'RADIOS'=>$radios,'LIST'=>$list));
	return _form_input($base.'_list',$pretty_name,$description,$input,false,false,$tabindex);
}

/**
 * Get the tempcode for a radio group.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  tempcode		The radio buttons for our radio group
 * @param  boolean		Whether a radio selection is required
 * @param  boolean		Whether this is a picture-based radio list
 * @param  string			Default value (only appropriate if has picture contents)
 * @return tempcode		The input field
 */
function form_input_radio($pretty_name,$description,$name,$content,$required=false,$picture_contents=false,$selected_path='')
{
	$map=array('_GUID'=>'26021f9ae8a0cd83b93874bfa80052ca','NAME'=>$name,'REQUIRED'=>$required,'CONTENT'=>$content);
	if ($picture_contents)
	{
		$map=array_merge($map,array('CODE'=>$selected_path,));
	}
	$input=do_template('FORM_SCREEN_INPUT_RADIO_LIST',$map);
	return _form_input(($GLOBALS['DOING_ALTERNATE_FIELDS_SET']!==NULL)?$name:'',$pretty_name,$description,$input,$required);
}

/**
 * Get the tempcode to choose a picture from the given list of images in the theme image system, with sub-categorisation.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  array			A list of image IDs (codes) we can choose from
 * @param  ?URLPATH		The currently selected entry in our picture choosing, by URL (NULL: none)
 * @param  ?string		The currently selected entry in our picture choosing, by code (NULL: none)
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  boolean		Whether to allow the selection of 'no' picture
 * @param  ?object		The database connection to the OCF install we are choosing images from (NULL: site db)
 * @param  ?ID_TEXT		Theme to use (NULL: current theme)
 * @param  ?ID_TEXT		Language to use (NULL: current language)
 * @param  boolean		Whether choices are presented in a list (as opposed to a grid); useful when sizes are irregular
 * @param  boolean		Whether to show direct codenames, rather than trying to prettify them into titles
 * @return tempcode		The input field
 */
function form_input_theme_image($pretty_name,$description,$name,$ids,$selected_url=NULL,$selected_code=NULL,$tabindex=NULL,$allow_none=false,$db=NULL,$theme=NULL,$lang=NULL,$linear=false,$direct_titles=false)
{
	if (is_null($db)) $db=$GLOBALS['SITE_DB'];

	if (count($ids)==0) return new ocp_tempcode();

	$tabindex=get_form_field_tabindex($tabindex);

	$selected_code=filter_form_field_default($name,is_null($selected_code)?'':$selected_code);
	if ($selected_code=='') $selected_code=NULL;

	// Split into lists of categories based on path division
	$current_path=''; // Initialise type to string
	$current_path=NULL;
	$category=array();
	$categories=array();
	foreach ($ids as $id)
	{
		$slash_pos=strrpos($id,'/');
		if ($slash_pos===false) $slash_pos=0;
		$new_path=substr($id,0,$slash_pos);
		if ($new_path!==$current_path)
		{
			if (!is_null($current_path))
			{
				if (!array_key_exists($current_path,$categories)) $categories[$current_path]=array();
				$categories[$current_path]=array_merge($categories[$current_path],$category);
				$category=array();
			}
			$current_path=$new_path;
		}
		$category[]=$id;
	}
	if (!is_null($current_path))
	{
		if (!array_key_exists($current_path,$categories)) $categories[$current_path]=array();
		$categories[$current_path]=array_merge($categories[$current_path],$category);
	}

	// Sorting but fudge it so 'ocf_default_avatars/default_set' always comes first
	ksort($categories);
	$avatars=(array_key_exists(0,$category)) && (substr($category[0],0,20)=='ocf_default_avatars/');
	if ((array_key_exists('ocf_default_avatars/default_set',$categories)) && ($avatars))
	{
		$def=$categories['ocf_default_avatars/default_set'];
		unset($categories['ocf_default_avatars/default_set']);
		$categories=array_merge(array('ocf_default_avatars/default_set'=>$def),$categories);
	}
	// Add in the 'N/A' option
	if (($allow_none) && (!array_key_exists('',$categories)))
	{
		if (count($categories)==1)
		{
			array_unshift($categories[$current_path],'');
		} else
		{
			$categories[do_lang('NA')]=array('');
		}
	}

	// Show each category
	$content=new ocp_tempcode();
	foreach ($categories as $cat=>$ids)
	{
		$cat=titleify($cat);

		if ($avatars)
		{
			$cut_pos=strpos($cat,'/');
			$cut_pos=($cut_pos===false)?($avatars?strlen($cat):0):($cut_pos+1);
			$cat=ucwords(substr($cat,$cut_pos)); // Make the category name a bit nicer
		}

		if ((!$avatars) && ($cat=='')) $cat=do_lang('GENERAL');

		$_category=new ocp_tempcode();
		$i=0;
		$category_expanded=false;
		foreach ($ids as $id)
		{
			if ($selected_url!==NULL)
			{
				$pos=strpos($selected_url,'/'.$id);
				$selected=($pos!==false) && ($id!='');
				if ($selected) $selected_code=$id;
			} else
			{
				$selected=($selected_code==$id);
			}
			if ($selected) $category_expanded=true;

			if ($id=='')
			{
				if (is_null($selected_code)) $selected=true;
				$url=find_theme_image('na',false,false,$theme,$lang,$db);
				$pretty=do_lang_tempcode('NA_EM');
			} else
			{
				$url=find_theme_image($id,$theme!='default',false,$theme,$lang,$db);
				if ($url=='')
					$url=find_theme_image($id,false,false,'default',$lang,$db);
				$pretty=$direct_titles?make_string_tempcode($id):make_string_tempcode(ucfirst((strrpos($id,'/')===false)?$id:substr($id,strrpos($id,'/')+1)));
			}
			if ($url=='') continue;

			$temp=do_template('FORM_SCREEN_INPUT_THEME_IMAGE_ENTRY',array('_GUID'=>'10005e2f08b44bfe17fce68685b4c884','LINEAR'=>$linear,'CHECKED'=>$selected,'PRETTY'=>$pretty,'NAME'=>$name,'CODE'=>$id,'URL'=>$url));
			$_category->attach($temp);

			$i++;
		}

		$_category=do_template('FORM_SCREEN_INPUT_THEME_IMAGE_CATEGORY',array('_GUID'=>'c2f429315b73bcaacc3bff8db11c0056','DISPLAY'=>$category_expanded?'block':'none','CATEGORY'=>$_category,'CATEGORY_NAME'=>(count($categories)==1)?'':$cat));
		$content->attach($_category);
	}

	$input=do_template('FORM_SCREEN_INPUT_RADIO_LIST',array('_GUID'=>'35fed772f022cf561f823543e56d63e8','REQUIRED'=>false,'NAME'=>$name,'CODE'=>is_null($selected_code)?'':$selected_code,'TABINDEX'=>strval($tabindex),'CONTENT'=>$content));

	return _form_input($GLOBALS['DOING_ALTERNATE_FIELDS_SET']?$name:'',$pretty_name,$description,$input,false);
}

/**
 * Get the tempcode for a date input, or if cron is not on, return blank.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The parameter name stub for this input field (it's actually a composite field, read in by passing this stub to post_param_date)
 * @param  boolean		Whether this is a required field
 * @param  boolean		Whether this field is empty by default
 * @param  boolean		Whether to input time for this field also
 * @param  ?mixed			The default timestamp to use (either TIME or array of time components) (NULL: now)
 * @param  integer		The number of years to allow selection from (all into the future, as this field type is not meant for inputting past dates)
 * @param  ?integer		The year to start from (NULL: this year)
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @return tempcode		The input field
 */
function form_input_date__scheduler($pretty_name,$description,$stub,$null_ok,$null_default,$do_time,$default_time=NULL,$total_years_to_show=10,$year_start=NULL,$tabindex=NULL)
{
	if (cron_installed())
	{
		return form_input_date($pretty_name,$description,$stub,$null_ok,$null_default,$do_time,$default_time,$total_years_to_show,$year_start,$tabindex);
	}
	return new ocp_tempcode();
}

/**
 * Get the tempcode for a date input.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The parameter name stub for this input field (it's actually a composite field, read in by passing this stub to post_param_date)
 * @param  boolean		Whether this is a required field
 * @param  boolean		Whether this field is empty by default
 * @param  boolean		Whether to input time for this field also
 * @param  ?mixed			The default timestamp to use (either TIME or array of time components) (NULL: now)
 * @param  ?integer		The number of years to allow selection from (all into the future, as this field type is not meant for inputting past dates) (NULL: no limit)
 * @param  ?integer		The year to start from (NULL: this year)
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  ?boolean		Whether this is rendered in pink as a required field (NULL: depend on $null_ok)
 * @param  boolean		Whether to input date for this field (if false, will just do time)
 * @param  ?ID_TEXT		Timezone to input in (NULL: current user's timezone)
 * @param  boolean		Convert $default_time to $timezone
 * @return tempcode		The input field
 */
function form_input_date($pretty_name,$description,$stub,$null_ok,$null_default,$do_time,$default_time=NULL,$total_years_to_show=10,$year_start=NULL,$tabindex=NULL,$required=NULL,$do_date=true,$timezone=NULL,$handle_timezone=true)
{
	if (is_null($required)) $required=!$null_ok;

	$tabindex=get_form_field_tabindex($tabindex);

	require_lang('dates');

	require_javascript('javascript_multi');
	require_javascript('javascript_yahoo');
	require_javascript('javascript_yahoo_events');
	require_javascript('javascript_date_chooser');
	require_css('date_chooser');

	if (is_null($year_start)) $year_start=intval(date('Y'));

	$untuned_year_start=$year_start; // The $year_start may go down if our default date requires it, but we need to know what our $total_years_to_show should really be relative to

	$default_minute=mixed();
	$default_hour=mixed();
	$default_month=mixed();
	$default_day=mixed();
	$default_year=mixed();

	if ((is_array($default_time)) && ($default_time[4]<1970) && (@strftime('%Y',@mktime(0,0,0,1,1,1963))!='1963')) // Some systems can't do negative timestamps. Actually the maximum negative integer size is also an issue
	{
		list($default_minute,$default_hour,$default_month,$default_day,$default_year)=$default_time;
		if (is_null($default_minute)) $default_minute=0;
		if (is_null($default_hour)) $default_hour=0;
	} else
	{
		if (is_array($default_time))
		{
			if (is_null($default_time[4]))
			{
				$default_time=NULL;
			} else
			{
				list($default_minute,$default_hour,$default_month,$default_day,$default_year)=$default_time;
				$default_time=mktime($default_hour,$default_minute,0,$default_month,$default_day,$default_year);
			}
		}

		$_default_time=filter_form_field_default($stub,is_null($default_time)?'':strval($default_time));
		$default_time=($_default_time=='')?NULL:intval($_default_time);

		if ((!is_null($default_time)) && ($handle_timezone))
		{
			if (is_null($timezone)) $timezone=get_users_timezone();
			$default_time=tz_time($default_time,$timezone);
		}

		$default_minute=is_null($default_time)?NULL:intval(date('i',$default_time));
		$default_hour=is_null($default_time)?NULL:intval(date('H',$default_time));
		$default_month=is_null($default_time)?NULL:intval(date('n',$default_time));
		$default_day=is_null($default_time)?NULL:intval(date('j',$default_time));
		$default_year=is_null($default_time)?NULL:intval(date('Y',$default_time));
	}

	if ((is_integer($default_year)) && ($default_year<$year_start)) $year_start=$default_year;

	ob_start();
	for ($minute=0;$minute<60;$minute++)
	{
		$_minute=strval($minute);
		//$temp=form_input_list_entry($_minute,$minute===$default_minute,($minute<10)?str_pad($_minute,2,'0',STR_PAD_LEFT):$_minute);
		//$temp->evaluate_echo();
		echo '<option '.(($minute===$default_minute)?'selected="selected" ':'').'value="'.escape_html($_minute).'">'.escape_html(($minute<10)?str_pad($_minute,2,'0',STR_PAD_LEFT):$_minute).'</option>'; // XHTMLXHTML
	}
	$minutes=ob_get_contents();
	ob_end_clean();
	ob_start();
	for ($hour=0;$hour<24;$hour++)
	{
		$text_hour=locale_filter(gmdate(do_lang('time_hour'),intval($hour*60*60)));
		//$temp=form_input_list_entry(strval($hour),$hour===$default_hour,$text_hour);
		//$temp->evaluate_echo();
		echo '<option '.(($hour===$default_hour)?'selected="selected" ':'').'value="'.escape_html(strval($hour)).'">'.escape_html($text_hour).'</option>'; // XHTMLXHTML
	}
	$hours=ob_get_contents();
	ob_end_clean();

	$time=($do_time)?do_template('FORM_SCREEN_INPUT_TIME',array('_GUID'=>'2d5886e54c7a14e69a8f84bbe62ec84a','NULL_OK'=>$null_ok,'DISABLED'=>$null_default && has_js(),'TABINDEX'=>strval($tabindex),'MINUTES'=>$minutes,'HOURS'=>$hours,'STUB'=>$stub)):new ocp_tempcode();
	if (!$do_date)
		return _form_input($stub,$pretty_name,$description,$time,$required,false,$tabindex,false,true);
	$null=($null_ok)?do_template('FORM_SCREEN_INPUT_DATE_NULL',array('_GUID'=>'22859d15f1b295b08036e1d0308d371a','TICKED'=>!$null_default,'TABINDEX'=>strval($tabindex),'STUB'=>$stub)):new ocp_tempcode();
	ob_start();
	for ($i=1;$i<=31;$i++)
	{
		//$temp=form_input_list_entry(strval($i),($i===$default_day));
		//$temp->evaluate_echo();
		echo '<option '.(($i===$default_day)?'selected="selected" ':'').'value="'.escape_html(strval($i)).'">'.escape_html(strval($i)).'</option>'; // XHTMLXHTML
	}
	$days=ob_get_contents();
	ob_end_clean();
	ob_start();
	for ($i=1;$i<=12;$i++)
	{
		switch ($i)
		{
			case 1:
				$month_text=do_lang_tempcode('JANUARY');
				break;
			case 2:
				$month_text=do_lang_tempcode('FEBRUARY');
				break;
			case 3:
				$month_text=do_lang_tempcode('MARCH');
				break;
			case 4:
				$month_text=do_lang_tempcode('APRIL');
				break;
			case 5:
				$month_text=do_lang_tempcode('MAY');
				break;
			case 6:
				$month_text=do_lang_tempcode('JUNE');
				break;
			case 7:
				$month_text=do_lang_tempcode('JULY');
				break;
			case 8:
				$month_text=do_lang_tempcode('AUGUST');
				break;
			case 9:
				$month_text=do_lang_tempcode('SEPTEMBER');
				break;
			case 10:
				$month_text=do_lang_tempcode('OCTOBER');
				break;
			case 11:
				$month_text=do_lang_tempcode('NOVEMBER');
				break;
			case 12:
				$month_text=do_lang_tempcode('DECEMBER');
				break;
		}

		//$temp=form_input_list_entry(strval($i),($i===$default_month),$month_text);
		//$temp->evaluate_echo();
		echo '<option '.(($i===$default_month)?'selected="selected" ':'').'value="'.escape_html(strval($i)).'">'.$month_text->evaluate().'</option>'; // XHTMLXHTML
	}
	$months=ob_get_contents();
	ob_end_clean();
	ob_start();
	if ((!is_null($total_years_to_show)) && ($total_years_to_show<0))
	{
		$yt=$year_start+$total_years_to_show/*remember $total_years_to_show is negative so this is a subtraction in effect*/;
		for ($i=max($untuned_year_start,$year_start);$i>=$yt;$i--)
		{
			//$temp=form_input_list_entry(strval($i),$i===$default_year);
			//$temp->evaluate_echo();
			echo '<option '.(($i===$default_year)?'selected="selected" ':'').'value="'.escape_html(strval($i)).'">'.escape_html(strval($i)).'</option>'; // XHTMLXHTML
		}
	} else
	{
		if (is_null($total_years_to_show))
		{
			$yt=max($untuned_year_start,$year_start)+5;
		} else
		{
			$yt=max($untuned_year_start,$year_start)+$total_years_to_show;
		}
		for ($i=$year_start;$i<=$yt;$i++)
		{
			//$temp=form_input_list_entry(strval($i),$i===$default_year);
			//$temp->evaluate_echo();
			echo '<option '.(($i===$default_year)?'selected="selected" ':'').'value="'.escape_html(strval($i)).'">'.escape_html(strval($i)).'</option>'; // XHTMLXHTML
		}
	}
	$years=ob_get_contents();
	ob_end_clean();
	$input=do_template('FORM_SCREEN_INPUT_DATE',array('_GUID'=>'5ace58dd0f540f70fb3bd440fb02a430','NULL_OK'=>$null_ok,'DISABLED'=>$null_default,'TABINDEX'=>strval($tabindex),'YEARS'=>$years,'MONTHS'=>$months,'DAYS'=>$days,'STUB'=>$stub,'NULL'=>$null,'TIME'=>$time,'UNLIMITED'=>is_null($total_years_to_show)));
	return _form_input($stub,$pretty_name,$description,$input,$required,false,$tabindex,false,true);
}

/**
 * Get the tempcode for an integer-only input.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  ?integer		The default value for this input field (NULL: no default)
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @return tempcode		The input field
 */
function form_input_integer($pretty_name,$description,$name,$default,$required,$tabindex=NULL)
{
	$tabindex=get_form_field_tabindex($tabindex);

	$_default=filter_form_field_default($name,is_null($default)?'':strval($default));
	$default=($_default=='')?NULL:intval($_default);

	$_required=($required)?'_required':'';
	$input=do_template('FORM_SCREEN_INPUT_INTEGER',array('TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name,'DEFAULT'=>is_null($default)?'':strval($default)));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex);
}

/**
 * Get the tempcode for a float-only input.
 *
 * @param  mixed			A human intelligible name for this input field
 * @param  mixed			A description for this input field
 * @param  ID_TEXT		The name which this input field is for
 * @param  ?float			The default value for this input field (NULL: no default)
 * @param  boolean		Whether this is a required input field
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @return tempcode		The input field
 */
function form_input_float($pretty_name,$description,$name,$default,$required,$tabindex=NULL)
{
	$tabindex=get_form_field_tabindex($tabindex);

	$_default=filter_form_field_default($name,is_null($default)?'':strval($default));
	$default=($_default=='')?NULL:floatval($_default);

	$_required=($required)?'_required':'';
	$input=do_template('FORM_SCREEN_INPUT_FLOAT',array('TABINDEX'=>strval($tabindex),'REQUIRED'=>$_required,'NAME'=>$name,'DEFAULT'=>is_null($default)?'':strval($default)));
	return _form_input($name,$pretty_name,$description,$input,$required,false,$tabindex);
}

/**
 * Start off a field set.
 *
 * IMPORTANT: Note that this function uses global state -- any fields generated between alternate_fields_set__start and alternate_fields_set__end will be rendered using field set templating.
 *
 * @param  ID_TEXT		The codename for this field set
 * @return tempcode		Tempcode to start attaching the field set to
 */
function alternate_fields_set__start($set_name)
{
	global $DOING_ALTERNATE_FIELDS_SET;
	if (!is_null($DOING_ALTERNATE_FIELDS_SET)) warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
	$DOING_ALTERNATE_FIELDS_SET=$set_name;
	return new ocp_tempcode();
}

/**
 * Show a field set that has just been finished off.
 *
 * @param  ID_TEXT		The codename for this field set
 * @param  mixed			The human-readable name for this field set
 * @param  mixed			The human-readable description for this field set
 * @param  tempcode		The field set tempcode
 * @param  boolean		Whether it is required that this field set be filled in
 * @return tempcode		The field set
 */
function alternate_fields_set__end($set_name,$pretty_name,$description,$fields,$required)
{
	global $DOING_ALTERNATE_FIELDS_SET;
	if ($DOING_ALTERNATE_FIELDS_SET===NULL) return $fields; // Didn't actually start set, probably because some logic said not to - so just flow to append as normal

	$set=do_template('FORM_SCREEN_FIELDS_SET',array('_GUID'=>'ae81cf68280aef067de1e8e71b2919a7','FIELDS'=>$fields,'PRETTY_NAME'=>$pretty_name,'SET_NAME'=>$set_name,'REQUIRED'=>$required));
	if (is_null($DOING_ALTERNATE_FIELDS_SET)) warn_exit(do_lang_tempcode('INTERNAL_ERROR'));
	$DOING_ALTERNATE_FIELDS_SET=NULL;
	return _form_input('',$pretty_name,$description,$set,$required);
}

/**
 * Helper function to show an input field.
 *
 * @param  ID_TEXT		The codename for this field (blank: N/A)
 * @param  mixed			The human-readable name for this field
 * @param  mixed			The human-readable description for this field
 * @param  tempcode		The actual raw input field
 * @param  boolean		Whether it is required that this field be filled in
 * @param  boolean		Whether this field may contain comcode
 * @param  ?integer		The tab index (NULL: none specified)
 * @param  boolean		Whether it is a textarea field
 * @param  boolean		Whether to skip displaying a label for the field
 * @param  mixed			A secondary side description for this input field
 * @return tempcode		The field
 */
function _form_input($name,$pretty_name,$description,$input,$required,$comcode=false,$tabindex=NULL,$w=false,$skip_label=false,$description_side='')
{
	check_suhosin_request_quantity(2);

	if (($GLOBALS['DEV_MODE']) && (user_lang()==fallback_lang()))
	{
		$_description=trim(strip_tags(is_object($description)?$description->evaluate():$description));
		if (($_description!='') && (substr($_description,-1)!='.') && (substr($_description,-1)!='!') && (substr($_description,-1)!='?') && (substr($_description,-1)!=']') && (substr($_description,-1)!=')') && (!$GLOBALS['NO_DEV_MODE_FULLSTOP_CHECK']))
		{
			fatal_exit('Description fields should end in full stops ['.$_description.'].');
		}
	}

	$_comcode=$comcode?do_template('COMCODE_MESSAGE',array('_GUID'=>'7668b8365e34b2484be7c2c271f82e79','NAME'=>$name,'W'=>$w,'URL'=>build_url(array('page'=>'userguide_comcode'),get_comcode_zone('userguide_comcode',false)))):new ocp_tempcode();

	global $DOING_ALTERNATE_FIELDS_SET;
	if ($DOING_ALTERNATE_FIELDS_SET!==NULL)
	{
		$tpl=do_template('FORM_SCREEN_FIELDS_SET_ITEM',array('_GUID'=>'23f2e2df7fcacc01d9f5158dc635e73d','SET_NAME'=>$DOING_ALTERNATE_FIELDS_SET,'REQUIRED'=>$required,'SKIP_LABEL'=>$skip_label,'NAME'=>$name,'PRETTY_NAME'=>$pretty_name,'DESCRIPTION'=>$description,'DESCRIPTION_SIDE'=>$description_side,'INPUT'=>$input,'COMCODE'=>$_comcode));
		return $tpl;
	}

	$tpl=do_template('FORM_SCREEN_FIELD',array('_GUID'=>'fa1402b7ad8319372f4bb5b152be7852','REQUIRED'=>$required,'SKIP_LABEL'=>$skip_label,'NAME'=>$name,'PRETTY_NAME'=>$pretty_name,'DESCRIPTION'=>$description,'DESCRIPTION_SIDE'=>$description_side,'INPUT'=>$input,'COMCODE'=>$_comcode));
	return $tpl;
}

/**
 * Look for editing conflicts, and setup editing pinging.
 *
 * @param  ?ID_TEXT		The ID we're editing (NULL: get from param, 'id')
 * @param  boolean		Whether to only care about staff conflicts
 * @return array			A pair: warning details, ping url
 */
function handle_conflict_resolution($id=NULL,$only_staff=false)
{
	if (($only_staff) && (!$GLOBALS['FORUM_DRIVER']->is_staff(get_member()))) return array(NULL,NULL);

	if (is_null($id)) $id=get_param('id','',true);

	require_javascript('javascript_ajax');
	$last_edit_screen_time=$GLOBALS['SITE_DB']->query('SELECT * FROM '.$GLOBALS['SITE_DB']->get_table_prefix().'edit_pings WHERE '.db_string_equal_to('the_page',get_page_name()).' AND '.db_string_equal_to('the_type',get_param('type','misc')).' AND '.db_string_equal_to('the_id',$id).' AND the_member<>'.strval((integer)get_member()).' ORDER BY the_time DESC',1);
	if ((array_key_exists(0,$last_edit_screen_time)) && ($last_edit_screen_time[0]['the_time']>time()-20))
	{
		$username=$GLOBALS['FORUM_DRIVER']->get_username($last_edit_screen_time[0]['the_member']);
		if (is_null($username)) $username='?';
		$warning_details=do_template('WARNING_BOX',array('WARNING'=>do_lang_tempcode('EDIT_CONFLICT_WARNING',escape_html($username))));
	} else $warning_details=NULL;
	$keep=symbol_tempcode('KEEP');
	$ping_url=find_script('edit_ping').'?page='.urlencode(get_page_name()).'&type='.urlencode(get_param('type','misc')).'&id='.urlencode($id).$keep->evaluate();

	return array($warning_details,$ping_url);
}

/**
 * Helper function for tab-index linearisation (serves as a filter).
 *
 * @param  ?integer		Requested tab-index (NULL: no specific request)
 * @return integer		Used tab-index
 */
function get_form_field_tabindex($tabindex=NULL)
{
	if ($tabindex===NULL)
	{
		global $TABINDEX;
		$tabindex=$TABINDEX;
		$TABINDEX++;
	}
	return $tabindex;
}

/**
 * Get the tempcode for a radio input. (You would gather together the outputs of several of these functions, then put them in as the $content in a form_input_radio function call).
 *
 * @param  string			The name of the radio button group this will be put in (i.e. the name the value presented here will be possibly matched against)
 * @param  string			The value for this entry
 * @param  boolean		Whether this entry is selected by default or not
 * @param  mixed			The text associated with this choice (blank: just use name for text)
 * @param  ?integer		The tab index of the field (NULL: not specified)
 * @param  mixed			An additional long description (blank: no description)
 * @return tempcode		The input field
 */
function form_input_radio_entry($name,$value,$selected=false,$text='',$tabindex=NULL,$description='')
{
	$tabindex=get_form_field_tabindex($tabindex);

	if ((is_string($text)) && ($text=='')) $text=$name;

	$selected=(filter_form_field_default($name,$selected?'1':'')=='1');

	return do_template('FORM_SCREEN_INPUT_RADIO_LIST_ENTRY',array('_GUID'=>'e2fe4ba6e8b3f705651dba13ea27f61d','DESCRIPTION'=>$description,'CHECKED'=>$selected,'TABINDEX'=>strval($tabindex),'NAME'=>$name,'VALUE'=>$value,'TEXT'=>$text));
}

