* @copyright Catalyst IT Ltd, Morphoss Ltd
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
*/
require_once("DataUpdate.php");
require_once("DataEntry.php");
/**
* A class for the fields in the editor
* @package awl
*/
class EditorField
{
var $Field;
var $Sql;
var $Value;
var $Attributes;
var $LookupSql;
var $OptionList;
/**
* Creates an EditorField for use in the Editor, possibly initialising the SQL for calculating it's
* value, and lookup_sql for use in drop-down lists.
*
* @param unknown $field
* @param string $sql
* @param string $lookup_sql
*/
function __construct( $field, $sql="", $lookup_sql="" ) {
global $session;
$this->Field = $field;
$this->Sql = $sql;
$this->LookupSql = $lookup_sql;
$this->Attributes = array();
}
function Set($value) {
$this->Value = $value;
}
/**
* Set the SQL used for this field, if it is more than just a field name.
* @param unknown $sql
*/
function SetSql( $sql ) {
$this->Sql = $sql;
}
/**
* Set the lookup SQL to use to populate a SELECT for this field.
* @param string $lookup_sql
*/
function SetLookup( $lookup_sql ) {
$this->LookupSql = $lookup_sql;
}
/**
* Set the SELECT values explicitly, if they are not available in SQL.
*
* For example:
*
* SetOptionList(array('M' => 'Male', 'F' => 'Female', 'O' => 'Other'), 'F', array('maxwidth' => 6, 'translate' => true));
*
* This would present Male/Female/Other drop-down, when in another language the values
* would be translated (if available), e.g. in German as Männlich/Weiblich/Andere, except
* that in this case Männli/Weibli/Andere, since the values would be truncated to maxwidth.
*
* @param array $options An array of key => value pairs
* @param string $current The currently selected key
* @param string $parameters An array of parameters (maxwidth & translate are the only valid parameters)
*/
function SetOptionList( $options, $current = null, $parameters = null) {
if ( gettype($options) == 'array' ) {
$this->OptionList = '';
if ( is_array($parameters) ) {
if ( isset($parameters['maxwidth']) ) $maxwidth = max(4,intval($parameters['maxwidth']));
if ( isset($parameters['translate']) ) $translate = true;
}
foreach( $options AS $k => $v ) {
if (is_array($current)) {
$selected = ( ( in_array($k,$current,true) || in_array($v,$current,true)) ? ' selected="selected"' : '' );
}
else {
$selected = ( ( "$k" == "$current" || "$v" == "$current" ) ? ' selected="selected"' : '' );
}
if ( isset($translate) ) $v = translate( $v );
if ( isset($maxwidth) ) $v = substr( $v, 0, $maxwidth);
$this->OptionList .= "";
}
}
else {
$this->OptionList = $options;
}
}
function GetTarget() {
if ( $this->Sql == "" ) return $this->Field;
return "$this->Sql AS $this->Field";
}
/**
* Add some kind of attribute to this field, such as a 'class' => 'fancyinputthingy'
*
* @param string $k The attribute name
* @param string $v The attribute value
*/
function AddAttribute( $k, $v ) {
$this->Attributes[$k] = $v;
}
/**
* Render a LABEL around something. In particular it is useful to render a label around checkbox fields to include their labels and make them clickable.
*
* The label value itself must be in the '_label' attribute, and the field must also have an 'id' attribute.
*
* @param string $wrapme The rendered field to be wrapped
* @return string
*/
function RenderLabel( $wrapme ) {
if ( !isset($this->Attributes['_label']) || !isset($this->Attributes['id'])) return $wrapme;
$class = (isset($this->Attributes['class']) ? $this->Attributes['class'] : 'entry');
$title = (isset($this->Attributes['title']) ? ' title="'.str_replace('"', ''', $this->Attributes['title']) . '"' : '');
return( sprintf( '',
$this->Attributes['id'], $class, $title, $wrapme, $this->Attributes['_label']) );
}
/**
* Render the array of attributes for inclusion in the input tag.
* @return string
*/
function RenderAttributes() {
$attributes = "";
if ( count($this->Attributes) == 0 ) return $attributes;
foreach( $this->Attributes AS $k => $v ) {
if ( $k == '_label' ) continue;
$attributes .= " $k=\"" . str_replace('"', ''', $v) . '"';
}
return $attributes;
}
}
/**
* The class for the Editor form in full
* @package awl
*/
class Editor
{
var $Title;
var $Action;
var $Fields;
var $OrderedFields;
var $BaseTable;
var $Joins;
var $Where;
var $NewWhere;
var $Order;
var $Limit;
var $Query;
var $Template;
var $RecordAvailable;
var $Record;
var $SubmitName;
var $Id;
/**
* Constructs an editor widget, with a title and fields.
*
* The second parameter maybe passed as a string, to be interpreted as the name of a table, from
* which all fields will be included, or as an array of specific fields, in which case you should
* make sure to call SetBaseTable('tablename') so the editor knows where to find those fields!
*
* @param string $title
* @param array or string $fields See above
*/
function __construct( $title = "", $fields = null ) {
global $c, $session, $form_id_increment;
$this->Title = $title;
$this->Order = "";
$this->Limit = "";
$this->Template = "";
$this->RecordAvailable = false;
$this->SubmitName = 'submit';
$form_id_increment = (isset($form_id_increment)? ++$form_id_increment : 1);
$this->Id = 'editor_'.$form_id_increment;
if ( isset($fields) ) {
if ( is_array($fields) ) {
foreach( $fields AS $k => $v ) {
$this->AddField($v);
}
}
else if ( is_string($fields) ) {
// We've been given a table name, so get all fields for it.
$this->BaseTable = $fields;
$field_list = awl_get_fields($fields);
foreach( $field_list AS $k => $v ) {
$this->AddField($k);
}
}
}
@dbg_error_log( 'editor', 'DBG: New editor called %s', $title);
}
/**
* Creates a new field in the Editor, possibly initialising the SQL for calculating it's
* value, and lookup_sql for use in drop-down lists.
*
* @param string $field The name for the field.
* @param string $sql The SQL for the target list. Think: "$sql AS $field"
* @param string $lookup_sql The SQL for looking up a list of possible stored values and displayed values.
*/
function &AddField( $field, $sql="", $lookup_sql="" ) {
$this->Fields[$field] = new EditorField( $field, $sql, $lookup_sql );
$this->OrderedFields[] = $field;
return $this->Fields[$field];
}
/**
* Set the SQL for this field for the target list. Think: "$sql AS $field"
* @param string $field
* @param string $sql
*/
function SetSql( $field, $sql ) {
$this->Fields[$field]->SetSql( $sql );
}
/**
* Set the SQL for looking up a list of possible stored values and displayed values.
* @param string $field
* @param string $lookup_sql
*/
function SetLookup( $field, $lookup_sql ) {
if (is_object($this->Fields[$field])) {
$this->Fields[$field]->SetLookup( $lookup_sql );
}
}
/**
* Gets the value of a field in the record currently assigned to this editor.
* @param string $value_field_name
*/
function Value( $value_field_name ) {
if ( !isset($this->Record->{$value_field_name}) ) return null;
return $this->Record->{$value_field_name};
}
/**
* Assigns the value of a field in the record currently associated with this editor.
* @param string $value_field_name
* @param string $new_value
*/
function Assign( $value_field_name, $new_value ) {
if ( !isset($this->Record) ) $this->Record = (object) array();
$this->Record->{$value_field_name} = $new_value;
}
/**
* Sets or returns the form ID used for differentiating this form from others in the page.
* @param string $id
*/
function Id( $id = null ) {
if ( isset($id) ) $this->Id = preg_replace( '#[^a-z0-9_+-]#', '', $id);
return $this->Id;
}
/**
* Set the explicit options & parameters for a list of stored/displayed values. See the
* description under EditorField::SetOptionList() for full details.
*
* @param string $field
* @param array $options A key => value array of valid store => display values.
* @param string $current The key of the current row
* @param string $parameters Set maxwidth & whether displayed values are translated.
*/
function SetOptionList( $field, $options, $current = null, $parameters = null) {
$this->Fields[$field]->SetOptionList( $options, $current, $parameters );
}
/**
* Add an attribute to this field.
* @param unknown $field
* @param unknown $k
* @param unknown $v
*/
function AddAttribute( $field, $k, $v ) {
$this->Fields[$field]->AddAttribute($k,$v);
}
/**
* Set the base table for the row query.
* @param unknown $base_table
*/
function SetBaseTable( $base_table ) {
$this->BaseTable = $base_table;
}
/**
* Set any joins
* @param unknown $join_list
*/
function SetJoins( $join_list ) {
$this->Joins = $join_list;
}
/**
* Accessor for the Title for the editor, which could set the title also.
*
* @param string $new_title The new title for the browser
* @return string The current title for the browser
*/
function Title( $new_title = null ) {
if ( isset($new_title) ) $this->Title = $new_title;
return $this->Title;
}
/**
* Set the name of the SUBMIT button
* @param unknown $new_submit
*/
function SetSubmitName( $new_submit ) {
$this->SubmitName = $new_submit;
}
function IsSubmit() {
return isset($_POST[$this->SubmitName]);
}
/**
* Magically knows whether you are in the processing the result of an update or a create.
* @return boolean
*/
function IsUpdate() {
$is_update = $this->Available();
if ( isset( $_POST['_editor_action']) && isset( $_POST['_editor_action'][$this->Id]) ) {
$is_update = ( $_POST['_editor_action'][$this->Id] == 'update' );
@dbg_error_log( 'editor', 'Checking update: %s => %d', $_POST['_editor_action'][$this->Id], $is_update );
}
return $is_update;
}
/**
* The opposite of IsUpdate. Really.
* @return boolean
*/
function IsCreate() {
return ! $this->IsUpdate();
}
/**
* Set the row selection criteria
* @param unknown $where_clause
*/
function SetWhere( $where_clause ) {
$this->Where = $where_clause;
}
/**
* Set the criteria used to find the new row after it got created.
* @param unknown $where_clause
*/
function WhereNewRecord( $where_clause ) {
$this->NewWhere = $where_clause;
}
/**
* Append more stuff to the WHERE clause
* @param unknown $operator
* @param unknown $more_where
*/
function MoreWhere( $operator, $more_where ) {
if ( $this->Where == "" ) {
$this->Where = $more_where;
return;
}
$this->Where = "$this->Where $operator $more_where";
}
function AndWhere( $more_where ) {
$this->MoreWhere("AND",$more_where);
}
function OrWhere( $more_where ) {
$this->MoreWhere("OR",$more_where);
}
/**
* Set this to be the form display template. It's better to use Layout($template) in general.
*
* @deprecated
* @param string $template
*/
function SetTemplate( $template ) {
deprecated('Editor::SetTemplate');
$this->Template = $template;
}
/**
* Like SetTemplate($template) except it surrounds the template with a ##form## ... if
* there is not a form already in the template.
*
* @param string $template
*/
function Layout( $template ) {
if ( strstr( $template, '##form##' ) === false && stristr( $template, '
or use Layout($template) which will take care of it for you.
* ##submit## A tag for the form.
* ##f.options## A list of options explicitly specified
* ##f.select## A select list from the lookup SQL specified
* ##f.checkbox## A checkbox, perhaps with a "_label" attribute
* ##f.input## A normal input field.
* ##f.file## A file upload field.
* ##f.money## A money input field.
* ##f.date## A date input field.
* ##f.textarea## A textarea
* ##f.hidden## A hidden input field
* ##f.password## An input field for entering passwords without them being echoed to the screen
* ##f.enc## Just print the value with special chars escaped for use in URLs.
* ##f.submit## An tag based on any SetAttributes() that may have been applied.
*
* @param array $matches The matches found which preg_replace_callback is calling us for.
* @return string What we want to replace this match with.
*/
function ReplaceEditorPart($matches)
{
global $session;
// $matches[0] is the complete match
switch( $matches[0] ) {
case "##form##": /** @todo It might be nice to construct a form ID */
return sprintf('