2007-07-19 12:40:28 +02:00
|
|
|
<?php
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* A ViewableData object is any object that can be rendered into a template/view.
|
|
|
|
*
|
|
|
|
* A view interrogates the object being currently rendered in order to get data to render into the template. This data
|
|
|
|
* is provided and automatically escaped by ViewableData. Any class that needs to be available to a view (controllers,
|
|
|
|
* {@link DataObject}s, page controls) should inherit from this class.
|
|
|
|
*
|
2012-04-12 08:02:46 +02:00
|
|
|
* @package framework
|
2008-02-25 03:10:37 +01:00
|
|
|
* @subpackage view
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2008-03-11 02:31:43 +01:00
|
|
|
class ViewableData extends Object implements IteratorAggregate {
|
2009-10-11 02:06:58 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* An array of objects to cast certain fields to. This is set up as an array in the format:
|
|
|
|
*
|
|
|
|
* <code>
|
|
|
|
* public static $casting = array (
|
|
|
|
* 'FieldName' => 'ClassToCastTo(Arguments)'
|
|
|
|
* );
|
|
|
|
* </code>
|
|
|
|
*
|
|
|
|
* @var array
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2010-12-11 03:01:13 +01:00
|
|
|
public static $casting = array(
|
2009-10-11 02:06:58 +02:00
|
|
|
'CSSClasses' => 'Varchar'
|
|
|
|
);
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* The default object to cast scalar fields to if casting information is not specified, and casting to an object
|
|
|
|
* is required.
|
|
|
|
*
|
|
|
|
* @var string
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-11-08 22:53:56 +01:00
|
|
|
public static $default_cast = 'HTMLText';
|
2007-07-19 12:40:28 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
private static $casting_cache = array();
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------------------------------------
|
2012-02-11 03:26:26 +01:00
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
/**
|
|
|
|
* A failover object to attempt to get data from if it is not present on this object.
|
|
|
|
*
|
|
|
|
* @var ViewableData
|
|
|
|
*/
|
|
|
|
protected $failover;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var ViewableData
|
|
|
|
*/
|
|
|
|
protected $customisedObject;
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
private $objCache = array();
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------------------------------------
|
2007-07-19 12:40:28 +02:00
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Converts a field spec into an object creator. For example: "Int" becomes "new Int($fieldName);" and "Varchar(50)"
|
|
|
|
* becomes "new Varchar($fieldName, 50);".
|
|
|
|
*
|
|
|
|
* @param string $fieldSchema The field spec
|
|
|
|
* @return string
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public static function castingObjectCreator($fieldSchema) {
|
2012-05-30 12:33:11 +02:00
|
|
|
Deprecation::notice('2.5', 'Use Object::create_from_string() instead');
|
2009-10-11 02:06:58 +02:00
|
|
|
}
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Convert a field schema (e.g. "Varchar(50)") into a casting object creator array that contains both a className
|
|
|
|
* and castingHelper constructor code. See {@link castingObjectCreator} for more information about the constructor.
|
|
|
|
*
|
|
|
|
* @param string $fieldSchema
|
|
|
|
* @return array
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public static function castingObjectCreatorPair($fieldSchema) {
|
2012-05-30 12:33:11 +02:00
|
|
|
Deprecation::notice('2.5', 'Use Object::create_from_string() instead');
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
// FIELD GETTERS & SETTERS -----------------------------------------------------------------------------------------
|
|
|
|
|
2008-03-11 02:31:43 +01:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Check if a field exists on this object or its failover.
|
|
|
|
*
|
|
|
|
* @param string $property
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function __isset($property) {
|
|
|
|
return $this->hasField($property) || ($this->failover && $this->failover->hasField($property));
|
2008-03-11 02:31:43 +01:00
|
|
|
}
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Get the value of a property/field on this object. This will check if a method called get{$property} exists, then
|
|
|
|
* check if a field is available using {@link ViewableData::getField()}, then fall back on a failover object.
|
|
|
|
*
|
|
|
|
* @param string $property
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function __get($property) {
|
|
|
|
if($this->hasMethod($method = "get$property")) {
|
|
|
|
return $this->$method();
|
|
|
|
} elseif($this->hasField($property)) {
|
|
|
|
return $this->getField($property);
|
|
|
|
} elseif($this->failover) {
|
|
|
|
return $this->failover->$property;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Set a property/field on this object. This will check for the existence of a method called set{$property}, then
|
|
|
|
* use the {@link ViewableData::setField()} method.
|
|
|
|
*
|
|
|
|
* @param string $property
|
|
|
|
* @param mixed $value
|
|
|
|
*/
|
|
|
|
public function __set($property, $value) {
|
|
|
|
if($this->hasMethod($method = "set$property")) {
|
|
|
|
$this->$method($value);
|
2007-07-19 12:40:28 +02:00
|
|
|
} else {
|
2009-10-11 02:06:58 +02:00
|
|
|
$this->setField($property, $value);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Check if a field exists on this object. This should be overloaded in child classes.
|
|
|
|
*
|
|
|
|
* @param string $field
|
|
|
|
* @return bool
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function hasField($field) {
|
|
|
|
return property_exists($this, $field);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Get the value of a field on this object. This should be overloaded in child classes.
|
|
|
|
*
|
|
|
|
* @param string $field
|
|
|
|
* @return mixed
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function getField($field) {
|
|
|
|
return $this->$field;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Set a field on this object. This should be overloaded in child classes.
|
|
|
|
*
|
|
|
|
* @param string $field
|
|
|
|
* @param mixed $value
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function setField($field, $value) {
|
|
|
|
$this->$field = $value;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------------------------------------
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Add methods from the {@link ViewableData::$failover} object, as well as wrapping any methods prefixed with an
|
|
|
|
* underscore into a {@link ViewableData::cachedCall()}.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function defineMethods() {
|
|
|
|
if($this->failover) {
|
2010-04-13 04:02:18 +02:00
|
|
|
if(is_object($this->failover)) $this->addMethodsFrom('failover');
|
|
|
|
else user_error("ViewableData::\$failover set to a non-object", E_USER_WARNING);
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
if(isset($_REQUEST['debugfailover'])) {
|
|
|
|
Debug::message("$this->class created with a failover class of {$this->failover->class}");
|
|
|
|
}
|
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
foreach($this->allMethodNames() as $method) {
|
|
|
|
if($method[0] == '_' && $method[1] != '_') {
|
2010-12-15 06:30:42 +01:00
|
|
|
$this->createMethod(
|
|
|
|
substr($method, 1), "return \$obj->cachedCall('$method', \$args, '" . substr($method, 1) . "');"
|
2009-10-11 02:06:58 +02:00
|
|
|
);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
parent::defineMethods();
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Merge some arbitrary data in with this object. This method returns a {@link ViewableData_Customised} instance
|
|
|
|
* with references to both this and the new custom data.
|
|
|
|
*
|
|
|
|
* Note that any fields you specify will take precedence over the fields on this object.
|
|
|
|
*
|
|
|
|
* @param array|ViewableData $data
|
|
|
|
* @return ViewableData_Customised
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function customise($data) {
|
|
|
|
if(is_array($data) && (empty($data) || ArrayLib::is_associative($data))) {
|
|
|
|
$data = new ArrayData($data);
|
|
|
|
}
|
2009-04-27 02:44:10 +02:00
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
if($data instanceof ViewableData) {
|
|
|
|
return new ViewableData_Customised($this, $data);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
throw new InvalidArgumentException (
|
|
|
|
'ViewableData->customise(): $data must be an associative array or a ViewableData instance'
|
|
|
|
);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* @param ViewableData $object
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function setCustomisedObj(ViewableData $object) {
|
|
|
|
$this->customisedObject = $object;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
// CASTING ---------------------------------------------------------------------------------------------------------
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Get the class a field on this object would be casted to, as well as the casting helper for casting a field to
|
|
|
|
* an object (see {@link ViewableData::castingHelper()} for information on casting helpers).
|
|
|
|
*
|
|
|
|
* The returned array contains two keys:
|
|
|
|
* - className: the class the field would be casted to (e.g. "Varchar")
|
|
|
|
* - castingHelper: the casting helper for casting the field (e.g. "return new Varchar($fieldName)")
|
|
|
|
*
|
|
|
|
* @param string $field
|
|
|
|
* @return array
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function castingHelperPair($field) {
|
2012-05-30 12:29:58 +02:00
|
|
|
Deprecation::notice('2.5', 'use castingHelper() instead');
|
2010-10-04 06:46:41 +02:00
|
|
|
return $this->castingHelper($field);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2010-10-13 05:38:23 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Return the "casting helper" (a piece of PHP code that when evaluated creates a casted value object) for a field
|
|
|
|
* on this object.
|
|
|
|
*
|
|
|
|
* @param string $field
|
|
|
|
* @return string
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function castingHelper($field) {
|
2010-10-13 02:42:17 +02:00
|
|
|
if($this->hasMethod('db') && $fieldSpec = $this->db($field)) {
|
2010-10-04 06:46:41 +02:00
|
|
|
return $fieldSpec;
|
|
|
|
}
|
2010-10-13 05:38:23 +02:00
|
|
|
|
2012-04-18 13:10:57 +02:00
|
|
|
$specs = Config::inst()->get(get_class($this), 'casting');
|
2010-10-04 06:46:41 +02:00
|
|
|
if(isset($specs[$field])) return $specs[$field];
|
2010-10-13 05:38:23 +02:00
|
|
|
|
|
|
|
if($this->failover) return $this->failover->castingHelper($field);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2008-02-25 03:10:37 +01:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Get the class name a field on this object will be casted to
|
2008-02-25 03:10:37 +01:00
|
|
|
*
|
2009-10-11 02:06:58 +02:00
|
|
|
* @param string $field
|
|
|
|
* @return string
|
2008-02-25 03:10:37 +01:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function castingClass($field) {
|
2010-10-04 06:46:41 +02:00
|
|
|
$spec = $this->castingHelper($field);
|
|
|
|
if(!$spec) return null;
|
|
|
|
|
|
|
|
$bPos = strpos($spec,'(');
|
|
|
|
if($bPos === false) return $spec;
|
2010-10-15 01:56:11 +02:00
|
|
|
else return substr($spec, 0, $bPos);
|
2008-02-25 03:10:37 +01:00
|
|
|
}
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Return the string-format type for the given field.
|
|
|
|
*
|
|
|
|
* @param string $field
|
|
|
|
* @return string 'xml'|'raw'
|
|
|
|
*/
|
|
|
|
public function escapeTypeForField($field) {
|
|
|
|
if(!$class = $this->castingClass($field)) {
|
|
|
|
$class = self::$default_cast;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2012-04-18 13:55:37 +02:00
|
|
|
return Config::inst()->get($class, 'escape_type', Config::FIRST_SET);
|
2009-10-11 02:06:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save the casting cache for this object (including data from any failovers) into a variable
|
|
|
|
*
|
|
|
|
* @param reference $cache
|
|
|
|
*/
|
|
|
|
public function buildCastingCache(&$cache) {
|
|
|
|
$ancestry = array_reverse(ClassInfo::ancestry($this->class));
|
|
|
|
$merge = true;
|
2008-09-11 00:16:35 +02:00
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
foreach($ancestry as $class) {
|
|
|
|
if(!isset(self::$casting_cache[$class]) && $merge) {
|
2009-10-12 08:58:40 +02:00
|
|
|
$mergeFields = is_subclass_of($class, 'DataObject') ? array('db', 'casting') : array('casting');
|
2007-07-19 12:40:28 +02:00
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
if($mergeFields) foreach($mergeFields as $field) {
|
2012-04-12 18:26:56 +02:00
|
|
|
$casting = Config::inst()->get($class, $field, Config::UNINHERITED);
|
2009-10-11 02:06:58 +02:00
|
|
|
if($casting) foreach($casting as $field => $cast) {
|
|
|
|
if(!isset($cache[$field])) $cache[$field] = self::castingObjectCreatorPair($cast);
|
2008-08-09 04:00:40 +02:00
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
if($class == 'ViewableData') $merge = false;
|
|
|
|
} elseif($merge) {
|
|
|
|
$cache = ($cache) ? array_merge(self::$casting_cache[$class], $cache) : self::$casting_cache[$class];
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
2009-10-12 02:40:15 +02:00
|
|
|
if($class == 'ViewableData') break;
|
2009-10-11 02:06:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TEMPLATE ACCESS LAYER -------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Render this object into the template, and get the result as a string. You can pass one of the following as the
|
|
|
|
* $template parameter:
|
|
|
|
* - a template name (e.g. Page)
|
|
|
|
* - an array of possible template names - the first valid one will be used
|
|
|
|
* - an SSViewer instance
|
|
|
|
*
|
|
|
|
* @param string|array|SSViewer $template the template to render into
|
|
|
|
* @param array $customFields fields to customise() the object with before rendering
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function renderWith($template, $customFields = null) {
|
|
|
|
if(!is_object($template)) {
|
|
|
|
$template = new SSViewer($template);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
$data = ($this->customisedObject) ? $this->customisedObject : $this;
|
|
|
|
|
2012-04-11 11:34:27 +02:00
|
|
|
if($customFields instanceof ViewableData) {
|
2009-10-11 02:06:58 +02:00
|
|
|
$data = $data->customise($customFields);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
if($template instanceof SSViewer) {
|
2012-04-11 11:34:27 +02:00
|
|
|
return $template->process($data, is_array($customFields) ? $customFields : null);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
throw new UnexpectedValueException (
|
|
|
|
"ViewableData::renderWith(): unexpected $template->class object, expected an SSViewer instance"
|
|
|
|
);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Get the value of a field on this object, automatically inserting the value into any available casting objects
|
|
|
|
* that have been specified.
|
|
|
|
*
|
|
|
|
* @param string $fieldName
|
|
|
|
* @param array $arguments
|
|
|
|
* @param bool $forceReturnedObject if TRUE, the value will ALWAYS be casted to an object before being returned,
|
|
|
|
* even if there is no explicit casting information
|
|
|
|
* @param string $cacheName a custom cache name
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function obj($fieldName, $arguments = null, $forceReturnedObject = true, $cache = false, $cacheName = null) {
|
|
|
|
if(!$cacheName) $cacheName = $arguments ? $fieldName . implode(',', $arguments) : $fieldName;
|
2008-09-11 00:16:35 +02:00
|
|
|
|
2009-10-12 02:40:15 +02:00
|
|
|
if(!isset($this->objCache[$cacheName])) {
|
2011-10-29 06:01:52 +02:00
|
|
|
// HACK: Don't call the deprecated FormField::Name() method
|
|
|
|
$methodIsAllowed = true;
|
|
|
|
if($this instanceof FormField && $fieldName == 'Name') $methodIsAllowed = false;
|
|
|
|
|
|
|
|
if($methodIsAllowed && $this->hasMethod($fieldName)) {
|
2009-10-12 02:40:15 +02:00
|
|
|
$value = $arguments ? call_user_func_array(array($this, $fieldName), $arguments) : $this->$fieldName();
|
2007-07-19 12:40:28 +02:00
|
|
|
} else {
|
2009-10-11 02:06:58 +02:00
|
|
|
$value = $this->$fieldName;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
if(!is_object($value) && ($this->castingClass($fieldName) || $forceReturnedObject)) {
|
|
|
|
if(!$castConstructor = $this->castingHelper($fieldName)) {
|
2010-10-04 06:46:41 +02:00
|
|
|
$castConstructor = $this->stat('default_cast');
|
2009-10-11 02:06:58 +02:00
|
|
|
}
|
|
|
|
|
2010-10-04 06:46:41 +02:00
|
|
|
$valueObject = Object::create_from_string($castConstructor, $fieldName);
|
2012-05-11 01:23:24 +02:00
|
|
|
$valueObject->setValue($value, ($this->hasMethod('toMap') ? $this->toMap() : null));
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
$value = $valueObject;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
if($cache) $this->objCache[$cacheName] = $value;
|
2007-07-19 12:40:28 +02:00
|
|
|
} else {
|
2009-10-11 02:06:58 +02:00
|
|
|
$value = $this->objCache[$cacheName];
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
if(!is_object($value) && $forceReturnedObject) {
|
2012-04-18 13:55:37 +02:00
|
|
|
$default = Config::inst()->get('ViewableData', 'default_cast', Config::FIRST_SET);
|
2010-10-12 23:47:48 +02:00
|
|
|
$value = new $default($fieldName);
|
2009-10-11 02:06:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $value;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* A simple wrapper around {@link ViewableData::obj()} that automatically caches the result so it can be used again
|
|
|
|
* without re-running the method.
|
|
|
|
*
|
|
|
|
* @param string $field
|
|
|
|
* @param array $arguments
|
|
|
|
* @param string $identifier an optional custom cache identifier
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function cachedCall($field, $arguments = null, $identifier = null) {
|
|
|
|
return $this->obj($field, $arguments, false, true, $identifier);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Checks if a given method/field has a valid value. If the result is an object, this will return the result of the
|
|
|
|
* exists method, otherwise will check if the result is not just an empty paragraph tag.
|
|
|
|
*
|
|
|
|
* @param string $field
|
2010-10-04 06:21:32 +02:00
|
|
|
* @param array $arguments
|
2009-10-11 02:06:58 +02:00
|
|
|
* @param bool $cache
|
|
|
|
* @return bool
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function hasValue($field, $arguments = null, $cache = true) {
|
|
|
|
$result = $cache ? $this->cachedCall($field, $arguments) : $this->obj($field, $arguments, false, false);
|
|
|
|
|
2010-10-15 04:30:57 +02:00
|
|
|
if(is_object($result) && $result instanceof Object) {
|
2009-10-11 02:06:58 +02:00
|
|
|
return $result->exists();
|
|
|
|
} else {
|
2010-10-15 04:30:57 +02:00
|
|
|
// Empty paragraph checks are a workaround for TinyMCE
|
2009-10-11 02:06:58 +02:00
|
|
|
return ($result && $result !== '<p></p>');
|
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
/**#@+
|
|
|
|
* @param string $field
|
|
|
|
* @param array $arguments
|
|
|
|
* @param bool $cache
|
2007-07-19 12:40:28 +02:00
|
|
|
* @return string
|
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the string value of a field on this object that has been suitable escaped to be inserted directly into a
|
|
|
|
* template.
|
|
|
|
*/
|
|
|
|
public function XML_val($field, $arguments = null, $cache = false) {
|
2009-10-15 23:48:24 +02:00
|
|
|
$result = $this->obj($field, $arguments, false, $cache);
|
2010-10-15 04:30:57 +02:00
|
|
|
return (is_object($result) && $result instanceof Object) ? $result->forTemplate() : $result;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Return the value of the field without any escaping being applied.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function RAW_val($field, $arguments = null, $cache = true) {
|
|
|
|
return Convert::xml2raw($this->XML_val($field, $arguments, $cache));
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-10-13 02:56:04 +02:00
|
|
|
* Return the value of a field in an SQL-safe format.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function SQL_val($field, $arguments = null, $cache = true) {
|
2010-10-13 02:56:04 +02:00
|
|
|
return Convert::raw2sql($this->RAW_val($field, $arguments, $cache));
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Return the value of a field in a JavaScript-save format.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function JS_val($field, $arguments = null, $cache = true) {
|
2010-10-13 02:56:04 +02:00
|
|
|
return Convert::raw2js($this->RAW_val($field, $arguments, $cache));
|
2009-10-11 02:06:58 +02:00
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Return the value of a field escaped suitable to be inserted into an XML node attribute.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function ATT_val($field, $arguments = null, $cache = true) {
|
2010-10-13 02:56:04 +02:00
|
|
|
return Convert::raw2att($this->RAW_val($field, $arguments, $cache));
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
/**#@-*/
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Get an array of XML-escaped values by field name
|
|
|
|
*
|
|
|
|
* @param array $elements an array of field names
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getXMLValues($fields) {
|
|
|
|
$result = array();
|
2007-07-19 12:40:28 +02:00
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
foreach($fields as $field) {
|
|
|
|
$result[$field] = $this->XML_val($field);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
return $result;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
// ITERATOR SUPPORT ------------------------------------------------------------------------------------------------
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Return a single-item iterator so you can iterate over the fields of a single record.
|
|
|
|
*
|
|
|
|
* This is useful so you can use a single record inside a <% control %> block in a template - and then use
|
|
|
|
* to access individual fields on this object.
|
|
|
|
*
|
|
|
|
* @return ArrayIterator
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function getIterator() {
|
|
|
|
return new ArrayIterator(array($this));
|
|
|
|
}
|
|
|
|
|
|
|
|
// UTILITY METHODS -------------------------------------------------------------------------------------------------
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* When rendering some objects it is necessary to iterate over the object being rendered, to do this, you need
|
|
|
|
* access to itself.
|
|
|
|
*
|
|
|
|
* @return ViewableData
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function Me() {
|
|
|
|
return $this;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2008-02-25 03:10:37 +01:00
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Return the directory if the current active theme (relative to the site root).
|
|
|
|
*
|
|
|
|
* This method is useful for things such as accessing theme images from your template without hardcoding the theme
|
|
|
|
* page - e.g. <img src="$ThemeDir/images/something.gif">.
|
|
|
|
*
|
|
|
|
* This method should only be used when a theme is currently active. However, it will fall over to the current
|
|
|
|
* project directory.
|
|
|
|
*
|
|
|
|
* @param string $subtheme the subtheme path to get
|
|
|
|
* @return string
|
2008-02-25 03:10:37 +01:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function ThemeDir($subtheme = false) {
|
|
|
|
if($theme = SSViewer::current_theme()) {
|
|
|
|
return THEMES_DIR . "/$theme" . ($subtheme ? "_$subtheme" : null);
|
2008-02-25 03:10:37 +01:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
return project();
|
2008-02-25 03:10:37 +01:00
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Get part of the current classes ancestry to be used as a CSS class.
|
|
|
|
*
|
|
|
|
* This method returns an escaped string of CSS classes representing the current classes ancestry until it hits a
|
|
|
|
* stop point - e.g. "Page DataObject ViewableData".
|
|
|
|
*
|
|
|
|
* @param string $stopAtClass the class to stop at (default: ViewableData)
|
|
|
|
* @return string
|
|
|
|
* @uses ClassInfo
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function CSSClasses($stopAtClass = 'ViewableData') {
|
|
|
|
$classes = array();
|
|
|
|
$classAncestry = array_reverse(ClassInfo::ancestry($this->class));
|
|
|
|
$stopClasses = ClassInfo::ancestry($stopAtClass);
|
|
|
|
|
|
|
|
foreach($classAncestry as $class) {
|
|
|
|
if(in_array($class, $stopClasses)) break;
|
|
|
|
$classes[] = $class;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
// optionally add template identifier
|
|
|
|
if(isset($this->template) && !in_array($this->template, $classes)) {
|
|
|
|
$classes[] = $this->template;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Convert::raw2att(implode(' ', $classes));
|
|
|
|
}
|
2012-02-11 03:08:39 +01:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Return debug information about this object that can be rendered into a template
|
|
|
|
*
|
2007-07-19 12:40:28 +02:00
|
|
|
* @return ViewableData_Debugger
|
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function Debug() {
|
|
|
|
return new ViewableData_Debugger($this);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-04-12 08:02:46 +02:00
|
|
|
* @package framework
|
2008-02-25 03:10:37 +01:00
|
|
|
* @subpackage view
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
|
|
|
class ViewableData_Customised extends ViewableData {
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* @var ViewableData
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
protected $original, $customised;
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Instantiate a new customised ViewableData object
|
|
|
|
*
|
|
|
|
* @param ViewableData $originalObject
|
|
|
|
* @param ViewableData $customisedObject
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function __construct(ViewableData $originalObject, ViewableData $customisedObject) {
|
|
|
|
$this->original = $originalObject;
|
|
|
|
$this->customised = $customisedObject;
|
|
|
|
|
|
|
|
$this->original->setCustomisedObj($this);
|
2007-07-19 12:40:28 +02:00
|
|
|
|
|
|
|
parent::__construct();
|
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
public function __call($method, $arguments) {
|
|
|
|
if($this->customised->hasMethod($method)) {
|
|
|
|
return call_user_func_array(array($this->customised, $method), $arguments);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
return call_user_func_array(array($this->original, $method), $arguments);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
public function __get($property) {
|
|
|
|
if(isset($this->customised->$property)) {
|
|
|
|
return $this->customised->$property;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
return $this->original->$property;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
public function __set($property, $value) {
|
|
|
|
$this->customised->$property = $this->original->$property = $value;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
public function hasMethod($method) {
|
|
|
|
return $this->customised->hasMethod($method) || $this->original->hasMethod($method);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
public function cachedCall($field, $arguments = null, $identifier = null) {
|
2009-11-21 02:41:06 +01:00
|
|
|
if($this->customised->hasMethod($field) || $this->customised->hasField($field)) {
|
|
|
|
$result = $this->customised->cachedCall($field, $arguments, $identifier);
|
|
|
|
} else {
|
2009-10-11 02:06:58 +02:00
|
|
|
$result = $this->original->cachedCall($field, $arguments, $identifier);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
public function obj($fieldName, $arguments = null, $forceReturnedObject = true, $cache = false, $cacheName = null) {
|
|
|
|
if($this->customised->hasField($fieldName) || $this->customised->hasMethod($fieldName)) {
|
|
|
|
return $this->customised->obj($fieldName, $arguments, $forceReturnedObject, $cache, $cacheName);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
return $this->original->obj($fieldName, $arguments, $forceReturnedObject, $cache, $cacheName);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Allows you to render debug information about a {@link ViewableData} object into a template.
|
|
|
|
*
|
2012-04-12 08:02:46 +02:00
|
|
|
* @package framework
|
2008-02-25 03:10:37 +01:00
|
|
|
* @subpackage view
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
|
|
|
class ViewableData_Debugger extends ViewableData {
|
2009-10-11 02:06:58 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
|
|
|
* @var ViewableData
|
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
protected $object;
|
2007-07-19 12:40:28 +02:00
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
/**
|
|
|
|
* @param ViewableData $object
|
|
|
|
*/
|
|
|
|
public function __construct(ViewableData $object) {
|
|
|
|
$this->object = $object;
|
2007-07-19 12:40:28 +02:00
|
|
|
parent::__construct();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-11 02:06:58 +02:00
|
|
|
* Return debugging information, as XHTML. If a field name is passed, it will show debugging information on that
|
|
|
|
* field, otherwise it will show information on all methods and fields.
|
|
|
|
*
|
|
|
|
* @param string $field the field name
|
2007-07-19 12:40:28 +02:00
|
|
|
* @return string
|
|
|
|
*/
|
2009-10-11 02:06:58 +02:00
|
|
|
public function forTemplate($field = null) {
|
|
|
|
// debugging info for a specific field
|
|
|
|
if($field) return "<b>Debugging Information for {$this->class}->{$field}</b><br/>" .
|
|
|
|
($this->object->hasMethod($field)? "Has method '$field'<br/>" : null) .
|
|
|
|
($this->object->hasField($field) ? "Has field '$field'<br/>" : null) ;
|
|
|
|
|
|
|
|
// debugging information for the entire class
|
|
|
|
$reflector = new ReflectionObject($this->object);
|
|
|
|
$debug = "<b>Debugging Information: all methods available in '{$this->object->class}'</b><br/><ul>";
|
|
|
|
|
|
|
|
foreach($this->object->allMethodNames() as $method) {
|
|
|
|
// check that the method is public
|
|
|
|
if($method[0] === strtoupper($method[0]) && $method[0] != '_') {
|
|
|
|
if($reflector->hasMethod($method) && $method = $reflector->getMethod($method)) {
|
|
|
|
if($method->isPublic()) {
|
|
|
|
$debug .= "<li>\${$method->getName()}";
|
|
|
|
|
|
|
|
if(count($method->getParameters())) {
|
|
|
|
$debug .= ' <small>(' . implode(', ', $method->getParameters()) . ')</small>';
|
|
|
|
}
|
|
|
|
|
|
|
|
$debug .= '</li>';
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$debug .= "<li>\$$method</li>";
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
$debug .= '</ul>';
|
|
|
|
|
2012-05-11 01:23:24 +02:00
|
|
|
if($this->object->hasMethod('toMap')) {
|
2009-10-11 02:06:58 +02:00
|
|
|
$debug .= "<b>Debugging Information: all fields available in '{$this->object->class}'</b><br/><ul>";
|
|
|
|
|
2012-05-11 01:23:24 +02:00
|
|
|
foreach($this->object->toMap() as $field => $value) {
|
2009-10-11 02:06:58 +02:00
|
|
|
$debug .= "<li>\$$field</li>";
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
$debug .= "</ul>";
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
// check for an extra attached data
|
|
|
|
if($this->object->hasMethod('data') && $this->object->data() != $this->object) {
|
2012-04-04 16:59:30 +02:00
|
|
|
$debug .= ViewableData_Debugger::create($this->object->data())->forTemplate();
|
2008-08-08 06:58:31 +02:00
|
|
|
}
|
2009-10-11 02:06:58 +02:00
|
|
|
|
|
|
|
return $debug;
|
2008-03-11 02:31:43 +01:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
}
|