public function __construct($parent, $name, $title = null, $options = null) { parent::__construct($parent, $name, $title, $options); list($parentClass, $componentClass, $parentField, $componentField, $table) = $parent->many_many($this->name); $this->parentField = $parentField; $this->componentField = $componentField; $this->joinTable = $table; $this->otherClass = $parent->class == $parentClass || ClassInfo::is_subclass_of($parent->class, $parentClass) ? $componentClass : $parentClass; }
/** * * @param string $dataPresenterClassName * @throws Exception */ function setDataPresenter($dataPresenterClassName) { if (!$dataPresenterClassName) { throw new Exception('Datapresenter for Datagrid must be set with a class'); } if (!class_exists($dataPresenterClassName)) { throw new Exception('Datapresenter for Datagrid must be set with an existing class'); } if ($dataPresenterClassName != 'DatagridPresenter' && !ClassInfo::is_subclass_of($dataPresenterClassName, 'DatagridPresenter')) { throw new Exception('Datapresenter "$dataPresenterClassName" must inherit DatagridPresenter'); } $this->dataPresenterClassName = $dataPresenterClassName; return $this; }
public function __construct(DataObject $controller, $name, $title = null, $className = null, $source = array(), $addTitle = null, $defaultsProperties = array(), $form = null) { $hasOne = false; if (!$title) { $this->title = self::name_to_label($name); } if (!$className) { if (substr($name, -2) == 'ID') { $name = substr($name, 0, -2); } if (!($hasOne = $className = $controller->has_one($name)) && !($className = $controller->belongs_to($name)) && (($settings = $controller->has_many($name)) || ($settings = $controller->many_many($name)))) { if (is_array($settings)) { $className = $settings[1]; } else { $className = $settings; } $this->fieldType = 'CheckboxSetField'; } if (!$className) { trigger_error('Couldn\'t determine class type from field name "' . $name . '". Please define the class name.'); } if ($hasOne) { $name .= 'ID'; } } else { if ($rels = $controller->has_many() + $controller->many_many()) { foreach ($rels as $rel => $class) { if ($class == $className || ClassInfo::is_subclass_of($class, $className)) { $this->fieldType = 'CheckboxSetField'; break; } } } } if (!class_exists($className)) { trigger_error($className . ' class doesn\'t exist'); } $this->setDefaults($defaultsProperties); $this->className = $className; $this->controller = $controller; parent::__construct($name, $title, $source, null, $form); }
/** * See {@link get_class_for_file_extension()}. * * @param String|array * @param String */ static function set_class_for_file_extension($exts, $class) { if (!is_array($exts)) { $exts = array($exts); } foreach ($exts as $ext) { if (ClassInfo::is_subclass_of($ext, 'File')) { throw new InvalidArgumentException(sprintf('Class "%s" (for extension "%s") is not a valid subclass of File', $class, $ext)); } self::$class_for_file_extension[$ext] = $class; } }
/** * Determine if a table is supporting the Versioned extensions (e.g. $table_versions does exists) * * @param string $table Table name * @return boolean */ function canBeVersioned($table) { return ClassInfo::exists($table) && ClassInfo::is_subclass_of($table, 'DataObject') && DataObject::has_own_table($table); }
/** * Return true if this dynamic template can be applied to a specified class. * @param $class * @return bool */ public function appliesToClass($class) { if (!isset($this->metadata)) { return true; } // no metadata so no class constraints if (!isset($this->metadata["classes"]) || count($this->metadata["classes"]) == 0) { return true; } // Check each item in classes. Each will be the name of a base class. If // the class of the item passed in is a subclass, this template applies // to it. foreach ($this->metadata["classes"] as $classConstraint) { if (ClassInfo::is_subclass_of($class, $classConstraint)) { return true; } } return false; }
/** * Handles widgets attached to a page through one or more {@link WidgetArea} elements. * Iterated through each $has_one relation with a {@link WidgetArea} * and looks for connected widgets by their database identifier. * Assumes URLs in the following format: <URLSegment>/widget/<Widget-ID>. * * @return RequestHandler */ function handleWidget() { $SQL_id = $this->request->param('ID'); if (!$SQL_id) { return false; } // find WidgetArea relations $widgetAreaRelations = array(); $hasOnes = $this->dataRecord->has_one(); if (!$hasOnes) { return false; } foreach ($hasOnes as $hasOneName => $hasOneClass) { if ($hasOneClass == 'WidgetArea' || ClassInfo::is_subclass_of($hasOneClass, 'WidgetArea')) { $widgetAreaRelations[] = $hasOneName; } } // find widget $widget = null; foreach ($widgetAreaRelations as $widgetAreaRelation) { if ($widget) { break; } $widget = $this->dataRecord->{$widgetAreaRelation}()->Widgets(sprintf('"Widget"."ID" = %d', $SQL_id))->First(); } if (!$widget) { user_error('No widget found', E_USER_ERROR); } // find controller $controllerClass = ''; foreach (array_reverse(ClassInfo::ancestry($widget->class)) as $widgetClass) { $controllerClass = "{$widgetClass}_Controller"; if (class_exists($controllerClass)) { break; } } if (!$controllerClass) { user_error(sprintf('No controller available for %s', $widget->class), E_USER_ERROR); } return new $controllerClass($widget); }
/** * Add an extension to a specific class. * As an alternative, extensions can be added to a specific class * directly in the {@link Object::$extensions} array. * See {@link SiteTree::$extensions} for examples. * Keep in mind that the extension will only be applied to new * instances, not existing ones (including all instances created through {@link singleton()}). * * @param string $class Class that should be decorated - has to be a subclass of {@link Object} * @param string $extension Subclass of {@link Extension} with optional parameters * as a string, e.g. "Versioned" or "Translatable('Param')" */ public static function add_extension($class, $extension) { if (!preg_match('/^([^(]*)/', $extension, $matches)) { return false; } $extensionClass = $matches[1]; if (!class_exists($extensionClass)) { user_error(sprintf('Object::add_extension() - Can\'t find extension class for "%s"', $extensionClass), E_USER_ERROR); } if (!ClassInfo::is_subclass_of($extensionClass, 'Extension')) { user_error(sprintf('Object::add_extension() - Extension "%s" is not a subclass of Extension', $extensionClass), E_USER_ERROR); } // unset some caches self::$cached_statics[$class]['extensions'] = null; $subclasses = ClassInfo::subclassesFor($class); $subclasses[] = $class; if ($subclasses) { foreach ($subclasses as $subclass) { unset(self::$classes_constructed[$subclass]); unset(self::$extra_methods[$subclass]); } } // merge with existing static vars $extensions = self::uninherited_static($class, 'extensions'); // We use unshift rather than push so that module extensions are added before built-in ones. // in particular, this ensures that the Versioned rewriting is done last. if ($extensions) { array_unshift($extensions, $extension); } else { $extensions = array($extension); } self::set_static($class, 'extensions', $extensions); // load statics now for DataObject classes if (ClassInfo::is_subclass_of($class, 'DataObject')) { DataObjectDecorator::load_extra_statics($class, $extension); } }
/** * Remove an extension from a class. * Keep in mind that this won't revert any datamodel additions * of the extension at runtime, unless its used before the * schema building kicks in (in your _config.php). * Doesn't remove the extension from any {@link Object} * instances which are already created, but will have an * effect on new extensions. * Clears any previously created singletons through {@link singleton()} * to avoid side-effects from stale extension information. * * @todo Add support for removing extensions with parameters * * @param string $class * @param string $extension Classname of an {@link Extension} subclass, without parameters */ public static function remove_extension($class, $extension) { // unload statics now for DataObject classes if (ClassInfo::is_subclass_of($class, 'DataObject')) { if (!preg_match('/^([^(]*)/', $extension, $matches)) { user_error("Bad extension '{$extension}'", E_USER_WARNING); } else { $extensionClass = $matches[1]; DataObjectDecorator::unload_extra_statics($class, $extensionClass); } } if (self::has_extension($class, $extension)) { self::set_static($class, 'extensions', array_diff(self::uninherited_static($class, 'extensions'), array($extension))); } // unset singletons to avoid side-effects global $_SINGLETONS; $_SINGLETONS = array(); // unset some caches self::$cached_statics[$class]['extensions'] = null; $subclasses = ClassInfo::subclassesFor($class); $subclasses[] = $class; if ($subclasses) { foreach ($subclasses as $subclass) { unset(self::$classes_constructed[$subclass]); unset(self::$extra_methods[$subclass]); } } }
/** * Matches a URL pattern * The pattern can contain a number of segments, separated by / (and an extension indicated by a .) * * The parts can be either literals, or, if they start with a $ they are interpreted as variables. * - Literals must be provided in order to match * - $Variables are optional * - However, if you put ! at the end of a variable, then it becomes mandatory. * * For example: * - admin/crm/list will match admin/crm/$Action/$ID/$OtherID, but it won't match admin/crm/$Action!/$ClassName! * * The pattern can optionally start with an HTTP method and a space. For example, "POST $Controller/$Action". * This is used to define a rule that only matches on a specific HTTP method. */ function match($pattern, $shiftOnSuccess = false) { // Check if a specific method is required if (preg_match('/^([A-Za-z]+) +(.*)$/', $pattern, $matches)) { $requiredMethod = $matches[1]; if ($requiredMethod != $this->httpMethod) { return false; } // If we get this far, we can match the URL pattern as usual. $pattern = $matches[2]; } // Special case for the root URL controller if (!$pattern) { return $this->dirParts == array() ? array('Matched' => true) : false; } // Check for the '//' marker that represents the "shifting point" $doubleSlashPoint = strpos($pattern, '//'); if ($doubleSlashPoint !== false) { $shiftCount = substr_count(substr($pattern, 0, $doubleSlashPoint), '/') + 1; $pattern = str_replace('//', '/', $pattern); $patternParts = explode('/', $pattern); } else { $patternParts = explode('/', $pattern); $shiftCount = sizeof($patternParts); } $matched = true; $arguments = array(); foreach ($patternParts as $i => $part) { $part = trim($part); // Match a variable if (isset($part[0]) && $part[0] == '$') { // A variable ending in ! is required if (substr($part, -1) == '!') { $varRequired = true; $varName = substr($part, 1, -1); } else { $varRequired = false; $varName = substr($part, 1); } // Fail if a required variable isn't populated if ($varRequired && !isset($this->dirParts[$i])) { return false; } $arguments[$varName] = isset($this->dirParts[$i]) ? $this->dirParts[$i] : null; if ($part == '$Controller' && (!ClassInfo::exists($arguments['Controller']) || !ClassInfo::is_subclass_of($arguments['Controller'], 'Controller'))) { return false; } // Literal parts with extension } else { if (isset($this->dirParts[$i]) && $this->dirParts[$i] . '.' . $this->extension == $part) { continue; // Literal parts must always be there } else { if (!isset($this->dirParts[$i]) || $this->dirParts[$i] != $part) { return false; } } } } if ($shiftOnSuccess) { $this->shift($shiftCount); // We keep track of pattern parts that we looked at but didn't shift off. // This lets us say that we have *parsed* the whole URL even when we haven't *shifted* it all $this->unshiftedButParsedParts = sizeof($patternParts) - $shiftCount; } $this->latestParams = $arguments; // Load the arguments that actually have a value into $this->allParams // This ensures that previous values aren't overridden with blanks foreach ($arguments as $k => $v) { if ($v || !isset($this->allParams[$k])) { $this->allParams[$k] = $v; } } if ($arguments === array()) { $arguments['_matched'] = true; } return $arguments; }
function getReturnedTypes() { $classes = array(); if ($this->noun == 'RESTRoot') { $classes = RESTRoot::get_registered(); } else { foreach ($this->methodBlocks as $name => $block) { $return = $block['return']['details']; $words = explode(' ', $return); $type = count($words) ? $words[0] : ''; $name = preg_replace('/^get([A-Z])/', '$1', $name); if ($name == 'Items') { $name = "{id}"; } $type = preg_replace('/^\\[([^\\]]+)\\]$/', '$1', $type); if ($type && ClassInfo::is_subclass_of($type, 'RESTNoun')) { $classes[$name] = $type; } } } // Build a new class inspector for each $res = new ArrayList(); foreach ($classes as $func => $class) { $inspect = new RESTDocGenerator_NestingInspector($class, $this, $func); $res->push($inspect); } return $res; }
function addClassNameToOrderItems_150() { $explanation = "\r\n\t\t\t<h1>150. Add a class name to all buyables.</h1>\r\n\t\t\t<p>ClassNames used to be implied, this is now saved as OrderItem.BuyableClassName.</p>\r\n\t\t"; if ($this->retrieveInfoOnly) { return $explanation; } else { echo $explanation; } $rows = DB::query("\r\n\t\t\tSELECT \"OrderAttribute\".\"ID\", \"ClassName\"\r\n\t\t\tFROM \"OrderAttribute\"\r\n\t\t\t\tINNER JOIN \"OrderItem\" ON \"OrderItem\".\"ID\" = \"OrderAttribute\".\"ID\"\r\n\t\t\tWHERE \"BuyableClassName\" = '' OR \"BuyableClassName\" IS NULL;\r\n\t\t"); if ($rows) { foreach ($rows as $row) { $orderItemPostFix = "_OrderItem"; $id = $row["ID"]; $className = str_replace($orderItemPostFix, "", $row["ClassName"]); if (class_exists($className) && ClassInfo::is_subclass_of($className, "DataObject")) { DB::query("\r\n\t\t\t\t\t\tUPDATE \"OrderItem\"\r\n\t\t\t\t\t\tSET \"BuyableClassName\" = '{$className}'\r\n\t\t\t\t\t\tWHERE \"ID\" = {$id};\r\n\t\t\t\t\t"); $this->DBAlterationMessageNow("Updating Order.BuyableClassName ( ID = {$id} ) to {$className}.", "created"); } else { $this->DBAlterationMessageNow("Order Item with ID = {$id} does not have a valid class name. This needs investigation.", "deleted"); } } } else { $this->DBAlterationMessageNow("No order items could be found that need updating."); } return 0; }
protected function storeCustomModelClass() { if (isset($_REQUEST['CustomModelClass']) && ClassInfo::is_subclass_of($_REQUEST['CustomModelClass'], $this->modelClass)) { Session::set($this->class . ".CustomModelClass", $_REQUEST['CustomModelClass']); } }