/** * Confirms that Object::add_static_var() doesn't work for uninherited stats */ public function testAddStaticVarDoesntWorkFor() { Object::add_static_var('ObjectStaticTest_First', 'first', array('test_1b')); Object::add_static_var('ObjectStaticTest_Second', 'first', array('test_2b')); $this->assertNotContains('test_1b', Object::uninherited_static('ObjectStaticTest_First', 'first')); $this->assertNotContains('test_2b', Object::uninherited_static('ObjectStaticTest_Second', 'first')); }
/** * Checks that Object::add_static_var() also works for uninherited stats */ public function testAddStaticVarWorksForUninheritedStatics() { Object::add_static_var('ObjectStaticTest_First', 'first', array('test_1b')); Object::add_static_var('ObjectStaticTest_Second', 'first', array('test_2b')); // Check that it can be applied to parent and subclasses, and queried directly $this->assertContains('test_1b', Object::uninherited_static('ObjectStaticTest_First', 'first')); $this->assertContains('test_2b', Object::uninherited_static('ObjectStaticTest_Second', 'first')); // But it won't affect subclasses - this is *uninherited* static $this->assertNotContains('test_2b', Object::uninherited_static('ObjectStaticTest_Third', 'first')); $this->assertNotContains('test_2b', Object::uninherited_static('ObjectStaticTest_Fourth', 'first')); // Subclasses that don't have the static explicitly defined should allow definition, also // This also checks that add_static_var can be called after the first uninherited_static() // call (which can be buggy due to caching) Object::add_static_var('ObjectStaticTest_Fourth', 'first', array('test_4b')); $this->assertContains('test_4b', Object::uninherited_static('ObjectStaticTest_Fourth', 'first')); }
/** * Load the extra static definitions for the given extension * class name, called by {@link Object::add_extension()} * * @param string $class Class name of the owner class (or owner base class) * @param string $extension Class name of the extension class */ public static function load_extra_statics($class, $extension) { if (!empty(self::$extra_statics_loaded[$class][$extension])) { return; } self::$extra_statics_loaded[$class][$extension] = true; if (preg_match('/^([^(]*)/', $extension, $matches)) { $extensionClass = $matches[1]; } else { user_error("Bad extenion '{$extension}' - can't find classname", E_USER_WARNING); return; } // @deprecated 2.4 - use extraStatics() now, not extraDBFields() if (method_exists($extensionClass, 'extraDBFields')) { user_error('DataExtension::extraDBFields() is deprecated. Please use extraStatics() instead.', E_USER_NOTICE); $extraStaticsMethod = 'extraDBFields'; } else { $extraStaticsMethod = 'extraStatics'; } // If the extension has been manually applied to a subclass, we should ignore that. if (Object::has_extension(get_parent_class($class), $extensionClass)) { return; } // If there aren't any extraStatics we shouldn't try to load them. if (!method_exists($extensionClass, $extraStaticsMethod)) { return; } $statics = call_user_func(array($extensionClass, $extraStaticsMethod), $class, $extension); if ($statics) { foreach ($statics as $name => $newVal) { if (isset(self::$extendable_statics[$name])) { // Array to be merged if (self::$extendable_statics[$name]) { $origVal = Object::uninherited_static($class, $name); // Can't use add_static_var() here as it would merge the array rather than replacing Object::set_static($class, $name, array_merge((array) $origVal, $newVal)); // Value to be overwritten } else { Object::set_static($class, $name, $newVal); } } } DataObject::$cache_has_own_table[$class] = null; DataObject::$cache_has_own_table_field[$class] = null; } }
/** * Prepare static variables before processing a {@link get_static} or {@link set_static} * call. */ private static function prepare_statics($class) { // _cache_statics_prepared setting must come first to prevent infinite loops when we call // get_static below self::$_cache_statics_prepared[$class] = true; // load statics now for DataObject classes if (is_subclass_of($class, 'DataObject')) { $extensions = Object::uninherited_static($class, 'extensions'); if ($extensions) { foreach ($extensions as $extension) { DataObjectDecorator::load_extra_statics($class, $extension); } } } }
/** * Get any user defined searchable fields labels that * exist. Allows overriding of default field names in the form * interface actually presented to the user. * * The reason for keeping this separate from searchable_fields, * which would be a logical place for this functionality, is to * avoid bloating and complicating the configuration array. Currently * much of this system is based on sensible defaults, and this property * would generally only be set in the case of more complex relationships * between data object being required in the search interface. * * Generates labels based on name of the field itself, if no static property * {@link self::field_labels} exists. * * @uses $field_labels * @uses FormField::name_to_label() * * @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields * * @return array|string Array of all element labels if no argument given, otherwise the label of the field */ public function fieldLabels($includerelations = true) { $customLabels = $this->stat('field_labels'); $autoLabels = array(); // get all translated static properties as defined in i18nCollectStatics() $ancestry = ClassInfo::ancestry($this->class); $ancestry = array_reverse($ancestry); if ($ancestry) { foreach ($ancestry as $ancestorClass) { if ($ancestorClass == 'ViewableData') { break; } $types = array('db' => (array) Object::uninherited_static($ancestorClass, 'db')); if ($includerelations) { $types['has_one'] = (array) singleton($ancestorClass)->uninherited('has_one', true); $types['has_many'] = (array) singleton($ancestorClass)->uninherited('has_many', true); $types['many_many'] = (array) singleton($ancestorClass)->uninherited('many_many', true); } foreach ($types as $type => $attrs) { foreach ($attrs as $name => $spec) { $autoLabels[$name] = _t("{$ancestorClass}.{$type}_{$name}", FormField::name_to_label($name)); } } } } $labels = array_merge((array) $autoLabels, (array) $customLabels); $this->extend('updateFieldLabels', $labels); return $labels; }
/** * Prepare static variables before processing a {@link get_static} or {@link set_static} * call. */ private static function prepare_statics($class) { // _cache_statics_prepared setting must come first to prevent infinite loops when we call // get_static below self::$_cache_statics_prepared[$class] = true; // load statics now for DataObject classes if (is_subclass_of($class, 'DataObject')) { $extensions = Object::uninherited_static($class, 'extensions'); if ($extensions) { foreach ($extensions as $extension) { $extensionClass = $extension; if (preg_match('/^([^(]*)/', $extension, $matches)) { $extensionClass = $matches[1]; } if (is_subclass_of($extensionClass, 'DataExtension')) { DataExtension::load_extra_statics($class, $extension); } else { user_error("{$extensionClass} cannot be applied to {$class} without being a DataExtension", E_USER_ERROR); } } } } }
function getNounChildrenStatic($stat) { $res = array(); foreach (ClassInfo::ancestry($this->noun) as $class) { if (!is_subclass_of($class, 'RESTNoun')) { continue; } $local = Object::uninherited_static($class, $stat); if ($local) { if (is_array($res) && is_array($local)) { $res = array_merge($res, $local); } else { $res = $local; } } } return $res; }
/** * 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; foreach ($ancestry as $class) { if (!isset(self::$casting_cache[$class]) && $merge) { $mergeFields = is_subclass_of($class, 'DataObject') ? array('db', 'casting') : array('casting'); if ($mergeFields) { foreach ($mergeFields as $field) { $casting = Object::uninherited_static($class, $field); if ($casting) { foreach ($casting as $field => $cast) { if (!isset($cache[$field])) { $cache[$field] = self::castingObjectCreatorPair($cast); } } } } } if ($class == 'ViewableData') { $merge = false; } } elseif ($merge) { $cache = $cache ? array_merge(self::$casting_cache[$class], $cache) : self::$casting_cache[$class]; } if ($class == 'ViewableData') { break; } } }
public function clean() { foreach (array('many_many', 'belongs_many_many') as $relationName) { foreach ((array)Object::uninherited_static($this->object->ClassName, $relationName) as $relationName => $relationClass) { $this->handleRelation($this->object->ClassName, $relationName); } } }
/** * Check that the given action is allowed to be called from a URL. * It will interrogate {@link self::$allowed_actions} to determine this. */ function checkAccessAction($action) { $actionOrigCasing = $action; $action = strtolower($action); $allowedActions = $this->allowedActions(); if ($allowedActions) { // check for specific action rules first, and fall back to global rules defined by asterisk foreach (array($action, '*') as $actionOrAll) { // check if specific action is set if (isset($allowedActions[$actionOrAll])) { $test = $allowedActions[$actionOrAll]; if ($test === true || $test === 1 || $test === '1') { // Case 1: TRUE should always allow access return true; } elseif (substr($test, 0, 2) == '->') { // Case 2: Determined by custom method with "->" prefix return $this->{substr($test, 2)}(); } else { // Case 3: Value is a permission code to check the current member against return Permission::check($test); } } elseif (($key = array_search($actionOrAll, $allowedActions)) !== false && is_numeric($key)) { // Case 4: Allow numeric array notation (search for array value as action instead of key) return true; } } } // If we get here an the action is 'index', then it hasn't been specified, which means that // it should be allowed. if ($action == 'index' || empty($action)) { return true; } // Get actions defined for this specific class $relevantActions = null; if ($allowedActions !== null && method_exists($this, $action)) { $r = new ReflectionClass(get_class($this)); $m = $r->getMethod($actionOrigCasing); $relevantActions = Object::uninherited_static($m->getDeclaringClass()->getName(), 'allowed_actions'); } // If no allowed_actions are provided on in the whole inheritance chain, // or they aren't provided on the specific class... if ($allowedActions === null || $relevantActions === null) { // ... only let through actions that aren't handled by // magic methods we test this by calling the unmagic method_exists. if (method_exists($this, $action)) { $r = new ReflectionClass(get_class($this)); if ($r->hasMethod($actionOrigCasing)) { $m = $r->getMethod($actionOrigCasing); return $m && is_subclass_of($m->getDeclaringClass()->getName(), 'RequestHandler'); } else { throw new Exception("method_exists() true but ReflectionClass can't find method"); } } else { if (!$this->hasMethod($action)) { // Return true so that a template can handle this action return true; } } } return false; }
public function load() { // Add the SphinxSearchable extension to ForumThread and Post, // with an extra computed column that gives an age band. The // age bands are based on Created, as follows: // _ageband = 10 where object is <30 days old // _ageband = 9 where object is 30-90 days old // _ageband = 8 where object is 90-180 days old // _ageband = 7 where object is 180 days to 1 year old // _ageband = 6 older than one year. // The age band is calculated so that when added to @relevancy, // it can be sorted. This calculation is valid for data that // ages like Post and ForumThread, but not for other classes // we can search, like Member and Forum. In those cases, // we still have to add the extra field _ageband, but we set it // to 10 so it's sorted like it's recent. DataObject::add_extension('ForumThread', 'SphinxSearchable'); Object::set_static("ForumThread", "sphinx", array("extra_fields" => array("_ageband" => "if(datediff(now(),LastEdited)<30,10,if(datediff(now(),LastEdited)<90,9,if(datediff(now(),LastEdited)<180,8,if(datediff(now(),LastEdited)<365,7,6))))"))); DataObject::add_extension('Post', 'SphinxSearchable'); Object::set_static("Post", "sphinx", array("extra_fields" => array("_ageband" => "if(datediff(now(),Created)<30,10,if(datediff(now(),Created)<90,9,if(datediff(now(),Created)<180,8,if(datediff(now(),Created)<365,7,6))))"))); // For classes that might be indexed, add the extra field if they // are decorated with SphinxSearchable. foreach (self::$extra_search_classes as $c) { if (Object::has_extension($c, 'SphinxSearchable')) { $conf = Object::uninherited_static($c, "sphinx"); if (!$conf) { $conf = array(); } if (!isset($conf['extra_fields'])) { $conf['extra_fields'] = array(); } $conf['extra_fields']['_ageband'] = "10"; Object::set_static($c, "sphinx", $conf); } } }
/** * A helper function used by castingHelperPair() to build the cache. * @param array */ public function buildCastingHelperCache(&$cache) { $class = $this->class ? $this->class : get_class($this); $classes = ClassInfo::ancestry($class); foreach ($classes as $componentClass) { if ($componentClass == "ViewableData") { $isViewableData = true; } if ($componentClass == "DataObject") { $isDataObject = true; } if (isset($isDataObject) && $isDataObject) { $fields = Object::uninherited_static($componentClass, 'db'); if ($fields) { foreach ($fields as $fieldName => $fieldSchema) { $cache[$fieldName] = ViewableData::castingObjectCreatorPair($fieldSchema); } } } if (isset($isViewableData) && $isViewableData) { $fields = Object::uninherited_static($componentClass, 'casting'); if ($fields) { foreach ($fields as $fieldName => $fieldSchema) { $cache[$fieldName] = ViewableData::castingObjectCreatorPair($fieldSchema); } } } } }
/** * *@return String **/ function getMyCode() { $array = Object::uninherited_static($this->ClassName, 'defaults'); if (!isset($array["Code"])) { user_error($this->class . " does not have a default code specified"); } return $array["Code"]; }