public function transform(FormField $field) { // Look for a performXXTransformation() method on the field itself. // performReadonlyTransformation() is a pretty commonly applied method. // Otherwise, look for a transformXXXField() method on this object. // This is more commonly done in custom transformations // We iterate through each array simultaneously, looking at [0] of both, then [1] of both. // This provides a more natural failover scheme. $transNames = array_reverse(array_map(function ($name) { return ClassInfo::shortName($name); }, array_values(ClassInfo::ancestry($this->class)))); $fieldClasses = array_reverse(array_map(function ($name) { return ClassInfo::shortName($name); }, array_values(ClassInfo::ancestry($field->class)))); $len = max(sizeof($transNames), sizeof($fieldClasses)); for ($i = 0; $i < $len; $i++) { // This is lets fieldClasses be longer than transNames if (!empty($transNames[$i])) { $funcName = 'perform' . $transNames[$i]; if ($field->hasMethod($funcName)) { //echo "<li>$field->class used $funcName"; return $field->{$funcName}($this); } } // And this one does the reverse. if (!empty($fieldClasses[$i])) { $funcName = 'transform' . $fieldClasses[$i]; if ($this->hasMethod($funcName)) { //echo "<li>$field->class used $funcName"; return $this->{$funcName}($field); } } } throw new \BadMethodCallException("FormTransformation:: Can't perform '{$this->class}' on '{$field->class}'"); }
protected function createCallableArray(&$extraArray, $interfaceToQuery, $variableMethod, $createObject = false) { $implementers = ClassInfo::implementorsOf($interfaceToQuery); if ($implementers) { foreach ($implementers as $implementer) { // Create a new instance of the object for method calls if ($createObject) { $implementer = new $implementer(); } // Get the exposed variables $exposedVariables = call_user_func(array($implementer, $variableMethod)); foreach ($exposedVariables as $varName => $details) { if (!is_array($details)) { $details = array('method' => $details, 'casting' => Config::inst()->get('SilverStripe\\View\\ViewableData', 'default_cast', Config::FIRST_SET)); } // If just a value (and not a key => value pair), use it for both key and value if (is_numeric($varName)) { $varName = $details['method']; } // Add in a reference to the implementing class (might be a string class name or an instance) $details['implementer'] = $implementer; // And a callable array if (isset($details['method'])) { $details['callable'] = array($implementer, $details['method']); } // Save with both uppercase & lowercase first letter, so either works $lcFirst = strtolower($varName[0]) . substr($varName, 1); $extraArray[$lcFirst] = $details; $extraArray[ucfirst($varName)] = $details; } } } }
/** * @inheritdoc * * @param HTTPRequest $request * @param Session $session * @param DataModel $model * * @return bool */ public function preRequest(HTTPRequest $request, Session $session, DataModel $model) { if (array_key_exists('flush', $request->getVars())) { foreach (ClassInfo::implementorsOf('SilverStripe\\Core\\Flushable') as $class) { $class::flush(); } } return true; }
/** * Create a new version from a database row * * @param array $record */ public function __construct($record) { $this->record = $record; $record['ID'] = $record['RecordID']; $className = $record['ClassName']; $this->object = ClassInfo::exists($className) ? new $className($record) : new DataObject($record); $this->failover = $this->object; parent::__construct(); }
public function index() { foreach (ClassInfo::subclassesFor($this->class) as $subclass) { echo $subclass . "\n"; /** @var CliController $task */ $task = Injector::inst()->create($subclass); $task->doInit(); $task->process(); } }
/** * @skipUpgrade */ public function testFindClassOrInterfaceFromCandidateImports() { $method = new ReflectionMethod($this->manifest, 'findClassOrInterfaceFromCandidateImports'); $method->setAccessible(true); $this->assertTrue(ClassInfo::exists('silverstripe\\test\\ClassA')); $this->assertEquals('PermissionProvider', $method->invokeArgs($this->manifest, ['\\PermissionProvider', 'Test\\Namespace', array('TestOnly', 'Controller')])); $this->assertEquals('PermissionProvider', $method->invokeArgs($this->manifest, array('PermissionProvider', 'Test\\NAmespace', array('PermissionProvider')))); $this->assertEmpty($method->invokeArgs($this->manifest, array('', 'TextNamespace', array('PermissionProvider')))); $this->assertEmpty($method->invokeArgs($this->manifest, array('', '', array()))); $this->assertEquals('silverstripe\\test\\ClassA', $method->invokeArgs($this->manifest, array('ClassA', 'Test\\Namespace', array('silverstripe\\test\\ClassA', 'PermissionProvider')))); $this->assertEquals('ClassA', $method->invokeArgs($this->manifest, array('\\ClassA', 'Test\\Namespace', array('silverstripe\\test')))); $this->assertEquals('ClassA', $method->invokeArgs($this->manifest, array('ClassA', 'silverstripe\\test', array('\\ClassA')))); $this->assertEquals('ClassA', $method->invokeArgs($this->manifest, array('Alias', 'silverstripe\\test', array('Alias' => '\\ClassA')))); $this->assertEquals('silverstripe\\test\\ClassA', $method->invokeArgs($this->manifest, array('ClassA', 'silverstripe\\test', array('silverstripe\\test\\ClassB')))); }
/** * @todo move to SQLSelect * @todo fix hack */ protected function applyBaseTableFields() { $classes = ClassInfo::dataClassesFor($this->modelClass); $baseTable = DataObject::getSchema()->baseDataTable($this->modelClass); $fields = array("\"{$baseTable}\".*"); if ($this->modelClass != $classes[0]) { $fields[] = '"' . $classes[0] . '".*'; } //$fields = array_keys($model->db()); $fields[] = '"' . $classes[0] . '".\\"ClassName\\" AS "RecordClassName"'; return $fields; }
/** * Returns false if the prefilterable parts of the rule aren't met, and true if they are * * @param $rules array - A hash of rules as allowed in the only or except portion of a config fragment header * @return bool - True if the rules are met, false if not. (Note that depending on whether we were passed an * only or an except rule, * which values means accept or reject a fragment */ public function matchesPrefilterVariantRules($rules) { $matches = "undefined"; // Needs to be truthy, but not true foreach ($rules as $k => $v) { switch (strtolower($k)) { case 'classexists': $matches = $matches && ClassInfo::exists($v); break; case 'moduleexists': $matches = $matches && $this->moduleExists($v); break; default: // NOP } if ($matches === false) { return $matches; } } return $matches; }
/** * 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) { $cacheKey = $this->class . '_' . $includerelations; if (!isset(self::$_cache_field_labels[$cacheKey])) { $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::class) { break; } $types = array('db' => (array) Config::inst()->get($ancestorClass, 'db', Config::UNINHERITED)); if ($includerelations) { $types['has_one'] = (array) Config::inst()->get($ancestorClass, 'has_one', Config::UNINHERITED); $types['has_many'] = (array) Config::inst()->get($ancestorClass, 'has_many', Config::UNINHERITED); $types['many_many'] = (array) Config::inst()->get($ancestorClass, 'many_many', Config::UNINHERITED); $types['belongs_many_many'] = (array) Config::inst()->get($ancestorClass, 'belongs_many_many', Config::UNINHERITED); } 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); self::$_cache_field_labels[$cacheKey] = $labels; } return self::$_cache_field_labels[$cacheKey]; }
/** * 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. * * @param $pattern * @param bool $shiftOnSuccess * @return array|bool */ public 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); } // Filter out any "empty" matching parts - either from an initial / or a trailing / $patternParts = array_values(array_filter($patternParts)); $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; } /** @skipUpgrade */ $key = "Controller"; $arguments[$varName] = isset($this->dirParts[$i]) ? $this->dirParts[$i] : null; if ($part == '$Controller' && (!ClassInfo::exists($arguments[$key]) || !is_subclass_of($arguments[$key], 'SilverStripe\\Control\\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; }
/** * @return array Array of associative arrays for each task (Keys: 'class', 'title', 'description') */ protected function getTasks() { $availableTasks = array(); $taskClasses = ClassInfo::subclassesFor('SilverStripe\\Dev\\BuildTask'); // remove the base class array_shift($taskClasses); foreach ($taskClasses as $class) { if (!$this->taskEnabled($class)) { continue; } $singleton = BuildTask::singleton($class); $desc = Director::is_cli() ? Convert::html2raw($singleton->getDescription()) : $singleton->getDescription(); $availableTasks[] = array('class' => $class, 'title' => $singleton->getTitle(), 'segment' => $singleton->config()->segment ?: str_replace('\\', '-', $class), 'description' => $desc); } return $availableTasks; }
/** * Returns a link to this controller. Overload with your own Link rules if they exist. * * @param string $action Optional action * @return string */ public function Link($action = null) { return Controller::join_links(ClassInfo::shortName($this), $action, '/'); }
/** * A utility funciton to retrieve subclasses of a given class that * are instantiable (ie, not abstract) and have a valid menu title. * * Sorted by url_priority config. * * @todo A variation of this function could probably be moved to {@link ClassInfo} * @param string $root The root class to begin finding subclasses * @param boolean $recursive Look for subclasses recursively? * @param string $sort Name of config on which to sort. Can be 'menu_priority' or 'url_priority' * @return array Valid, unique subclasses */ public static function get_cms_classes($root = null, $recursive = true, $sort = self::MENU_PRIORITY) { if (!$root) { $root = 'SilverStripe\\Admin\\LeftAndMain'; } /** @todo Make these actual abstract classes */ $abstractClasses = ['SilverStripe\\Admin\\LeftAndMain', 'SilverStripe\\CMS\\Controllers\\CMSMain']; $subClasses = array_values(ClassInfo::subclassesFor($root)); foreach ($subClasses as $className) { if ($recursive && $className != $root) { $subClasses = array_merge($subClasses, array_values(ClassInfo::subclassesFor($className))); } } $subClasses = array_unique($subClasses); foreach ($subClasses as $key => $className) { // Remove abstract classes and LeftAndMain if (in_array($className, $abstractClasses) || ClassInfo::classImplements($className, 'SilverStripe\\Dev\\TestOnly')) { unset($subClasses[$key]); } else { // Separate conditional to avoid autoloading the class $classReflection = new ReflectionClass($className); if (!$classReflection->isInstantiable()) { unset($subClasses[$key]); } } } // Sort by specified sorting config usort($subClasses, function ($a, $b) use($sort) { $priorityA = Config::inst()->get($a, $sort); $priorityB = Config::inst()->get($b, $sort); return $priorityB - $priorityA; }); return $subClasses; }
/** * @return string name of {@see GridFieldDetailForm_ItemRequest} subclass */ public function getItemRequestClass() { if ($this->itemRequestClass) { return $this->itemRequestClass; } else { if (ClassInfo::exists(get_class($this) . "_ItemRequest")) { return get_class($this) . "_ItemRequest"; } else { return __CLASS__ . '_ItemRequest'; } } }
/** * Determine if a class is supporting the Versioned extensions (e.g. * $table_Versions does exists). * * @param string $class Class name * @return boolean */ public function canBeVersioned($class) { return ClassInfo::exists($class) && is_subclass_of($class, DataObject::class) && DataObject::getSchema()->classHasTable($class); }
/** * Get list of classnames that should be selectable * * @return array */ public function getEnum() { $classNames = ClassInfo::subclassesFor($this->getBaseClass()); unset($classNames[DataObject::class]); return $classNames; }
/** * Get a list of all available permission codes, both defined through the * {@link PermissionProvider} interface, and all not explicitly defined codes existing * as a {@link Permission} database record. By default, the results are * grouped as denoted by {@link Permission_Group}. * * @param bool $grouped Group results into an array of permission groups. * @return array Returns an array of all available permission codes. The * array indicies are the permission codes as used in * {@link Permission::check()}. The value is a description * suitable for using in an interface. */ public static function get_codes($grouped = true) { $classes = ClassInfo::implementorsOf('SilverStripe\\Security\\PermissionProvider'); $allCodes = array(); $adminCategory = _t('Permission.AdminGroup', 'Administrator'); $allCodes[$adminCategory]['ADMIN'] = array('name' => _t('Permission.FULLADMINRIGHTS', 'Full administrative rights'), 'help' => _t('Permission.FULLADMINRIGHTS_HELP', 'Implies and overrules all other assigned permissions.'), 'sort' => 100000); if ($classes) { foreach ($classes as $class) { $SNG = singleton($class); if ($SNG instanceof TestOnly) { continue; } $someCodes = $SNG->providePermissions(); if ($someCodes) { foreach ($someCodes as $k => $v) { if (is_array($v)) { // There must be a category and name key. if (!isset($v['category'])) { user_error("The permission {$k} must have a category key", E_USER_WARNING); } if (!isset($v['name'])) { user_error("The permission {$k} must have a name key", E_USER_WARNING); } if (!isset($allCodes[$v['category']])) { $allCodes[$v['category']] = array(); } $allCodes[$v['category']][$k] = array('name' => $v['name'], 'help' => isset($v['help']) ? $v['help'] : null, 'sort' => isset($v['sort']) ? $v['sort'] : 0); } else { $allCodes['Other'][$k] = array('name' => $v, 'help' => null, 'sort' => 0); } } } } } $flatCodeArray = array(); foreach ($allCodes as $category) { foreach ($category as $code => $permission) { $flatCodeArray[] = $code; } } $otherPerms = DB::query("SELECT DISTINCT \"Code\" From \"Permission\" WHERE \"Code\" != ''")->column(); if ($otherPerms) { foreach ($otherPerms as $otherPerm) { if (!in_array($otherPerm, $flatCodeArray)) { $allCodes['Other'][$otherPerm] = array('name' => $otherPerm, 'help' => null, 'sort' => 0); } } } // Don't let people hijack ADMIN rights if (!Permission::check("ADMIN")) { unset($allCodes['ADMIN']); } ksort($allCodes); $returnCodes = array(); foreach ($allCodes as $category => $permissions) { if ($grouped) { uasort($permissions, array(__CLASS__, 'sort_permissions')); $returnCodes[$category] = $permissions; } else { $returnCodes = array_merge($returnCodes, $permissions); } } return $returnCodes; }
/** * Checks the database is in a state to perform security checks. * See {@link DatabaseAdmin->init()} for more information. * * @return bool */ public static function database_is_ready() { // Used for unit tests if (self::$force_database_is_ready !== null) { return self::$force_database_is_ready; } if (self::$database_is_ready) { return self::$database_is_ready; } $requiredClasses = ClassInfo::dataClassesFor(Member::class); $requiredClasses[] = Group::class; $requiredClasses[] = Permission::class; $schema = DataObject::getSchema(); foreach ($requiredClasses as $class) { // Skip test classes, as not all test classes are scaffolded at once if (is_a($class, TestOnly::class, true)) { continue; } // if any of the tables aren't created in the database $table = $schema->tableName($class); if (!ClassInfo::hasTable($table)) { return false; } // HACK: DataExtensions aren't applied until a class is instantiated for // the first time, so create an instance here. singleton($class); // if any of the tables don't have all fields mapped as table columns $dbFields = DB::field_list($table); if (!$dbFields) { return false; } $objFields = $schema->databaseFields($class, false); $missingFields = array_diff_key($objFields, $dbFields); if ($missingFields) { return false; } } self::$database_is_ready = true; return true; }
/** * Reset the testing database's schema. * @param bool $includeExtraDataObjects If true, the extraDataObjects tables will also be included */ public function resetDBSchema($includeExtraDataObjects = false) { if (self::using_temp_db()) { DataObject::reset(); // clear singletons, they're caching old extension info which is used in DatabaseAdmin->doBuild() Injector::inst()->unregisterAllObjects(); $dataClasses = ClassInfo::subclassesFor('SilverStripe\\ORM\\DataObject'); array_shift($dataClasses); DB::quiet(); $schema = DB::get_schema(); $extraDataObjects = $includeExtraDataObjects ? $this->extraDataObjects : null; $schema->schemaUpdate(function () use($dataClasses, $extraDataObjects) { foreach ($dataClasses as $dataClass) { // Check if class exists before trying to instantiate - this sidesteps any manifest weirdness if (class_exists($dataClass)) { $SNG = singleton($dataClass); if (!$SNG instanceof TestOnly) { $SNG->requireTable(); } } } // If we have additional dataobjects which need schema, do so here: if ($extraDataObjects) { foreach ($extraDataObjects as $dataClass) { $SNG = singleton($dataClass); if (singleton($dataClass) instanceof DataObject) { $SNG->requireTable(); } } } }); ClassInfo::reset_db_cache(); singleton('SilverStripe\\ORM\\DataObject')->flushCache(); } }
/** * Safely get a DataObject from a client-supplied ID and ClassName, checking: argument * validity; existence; and canView permissions. * * @param int $id The ID of the DataObject * @param string $class The Class of the DataObject * @return DataObject The referenced DataObject * @throws HTTPResponse_Exception */ protected function getObject($id, $class) { $id = (int) $id; $class = ClassInfo::class_name($class); if (!$class || !is_subclass_of($class, 'SilverStripe\\ORM\\DataObject') || !Object::has_extension($class, 'SilverStripe\\ORM\\Versioning\\Versioned')) { $this->controller->httpError(400, _t('AddToCampaign.ErrorGeneral', 'We apologise, but there was an error')); return null; } $object = DataObject::get($class)->byID($id); if (!$object) { $this->controller->httpError(404, _t('AddToCampaign.ErrorNotFound', 'That {Type} couldn\'t be found', '', ['Type' => $class])); return null; } if (!$object->canView()) { $this->controller->httpError(403, _t('AddToCampaign.ErrorItemPermissionDenied', 'It seems you don\'t have the necessary permissions to add {ObjectTitle} to a campaign', '', ['ObjectTitle' => $object->Title])); return null; } return $object; }
/** * Remove invalid records from tables - that is, records that don't have * corresponding records in their parent class tables. */ public function cleanup() { $baseClasses = []; foreach (ClassInfo::subclassesFor(DataObject::class) as $class) { if (get_parent_class($class) == DataObject::class) { $baseClasses[] = $class; } } $schema = DataObject::getSchema(); foreach ($baseClasses as $baseClass) { // Get data classes $baseTable = $schema->baseDataTable($baseClass); $subclasses = ClassInfo::subclassesFor($baseClass); unset($subclasses[0]); foreach ($subclasses as $k => $subclass) { if (!DataObject::getSchema()->classHasTable($subclass)) { unset($subclasses[$k]); } } if ($subclasses) { $records = DB::query("SELECT * FROM \"{$baseTable}\""); foreach ($subclasses as $subclass) { $subclassTable = $schema->tableName($subclass); $recordExists[$subclass] = DB::query("SELECT \"ID\" FROM \"{$subclassTable}\"")->keyedColumn(); } foreach ($records as $record) { foreach ($subclasses as $subclass) { $subclassTable = $schema->tableName($subclass); $id = $record['ID']; if ($record['ClassName'] != $subclass && !is_subclass_of($record['ClassName'], $subclass) && isset($recordExists[$subclass][$id])) { $sql = "DELETE FROM \"{$subclassTable}\" WHERE \"ID\" = ?"; echo "<li>{$sql} [{$id}]</li>"; DB::prepared_query($sql, [$id]); } } } } } }
public static function get_extra_config_sources($class = null) { if ($class === null) { $class = get_called_class(); } // If this class is unextendable, NOP if (in_array($class, self::$unextendable_classes)) { return null; } // Variable to hold sources in $sources = null; // Get a list of extensions $extensions = Config::inst()->get($class, 'extensions', Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES); if (!$extensions) { return null; } // Build a list of all sources; $sources = array(); foreach ($extensions as $extension) { list($extensionClass, $extensionArgs) = Object::parse_class_spec($extension); $sources[] = $extensionClass; if (!class_exists($extensionClass)) { throw new InvalidArgumentException("{$class} references nonexistent {$extensionClass} in \$extensions"); } call_user_func(array($extensionClass, 'add_to_class'), $class, $extensionClass, $extensionArgs); foreach (array_reverse(ClassInfo::ancestry($extensionClass)) as $extensionClassParent) { if (ClassInfo::has_method_from($extensionClassParent, 'get_extra_config', $extensionClassParent)) { $extras = $extensionClassParent::get_extra_config($class, $extensionClass, $extensionArgs); if ($extras) { $sources[] = $extras; } } } } return $sources; }
public function providePermissions() { $perms = array("CMS_ACCESS_LeftAndMain" => array('name' => _t('CMSMain.ACCESSALLINTERFACES', 'Access to all CMS sections'), 'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access'), 'help' => _t('CMSMain.ACCESSALLINTERFACESHELP', 'Overrules more specific access settings.'), 'sort' => -100)); // Add any custom ModelAdmin subclasses. Can't put this on ModelAdmin itself // since its marked abstract, and needs to be singleton instanciated. foreach (ClassInfo::subclassesFor('SilverStripe\\Admin\\ModelAdmin') as $i => $class) { if ($class == 'SilverStripe\\Admin\\ModelAdmin') { continue; } if (ClassInfo::classImplements($class, 'SilverStripe\\Dev\\TestOnly')) { continue; } // Check if modeladmin has explicit required_permission_codes option. // If a modeladmin is namespaced you can apply this config to override // the default permission generation based on fully qualified class name. $code = $this->getRequiredPermissions(); if (!$code) { continue; } // Get first permission if multiple specified if (is_array($code)) { $code = reset($code); } $title = LeftAndMain::menu_title($class); $perms[$code] = array('name' => _t('CMSMain.ACCESS', "Access to '{title}' section", "Item in permission selection identifying the admin section. Example: Access to 'Files & Images'", array('title' => $title)), 'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')); } return $perms; }
/** * Remove an item from this relation. * Doesn't actually remove the item, it just clears the foreign key value. * * @param DataObject $item The DataObject to be removed * @todo Maybe we should delete the object instead? */ public function remove($item) { if (!$item instanceof $this->dataClass) { throw new InvalidArgumentException("HasManyList::remove() expecting a {$this->dataClass} object, or ID", E_USER_ERROR); } // Don't remove item with unrelated class key $foreignClass = $this->getForeignClass(); $classNames = ClassInfo::subclassesFor($foreignClass); $classForeignKey = $this->classForeignKey; if (!in_array($item->{$classForeignKey}, $classNames)) { return; } // Don't remove item which doesn't belong to this list $foreignID = $this->getForeignID(); $foreignKey = $this->foreignKey; if (empty($foreignID) || is_array($foreignID) && in_array($item->{$foreignKey}, $foreignID) || $foreignID == $item->{$foreignKey}) { $item->{$foreignKey} = null; $item->{$classForeignKey} = null; $item->write(); } }
/** * 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 */ public function CSSClasses($stopAtClass = 'SilverStripe\\View\\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; } // optionally add template identifier if (isset($this->template) && !in_array($this->template, $classes)) { $classes[] = $this->template; } // Strip out namespaces $classes = preg_replace('#.*\\\\#', '', $classes); return Convert::raw2att(implode(' ', $classes)); }
/** * Ensures that the latest version of a record is the expected value * * @param DataObject $record * @param int $version */ protected function assertRecordHasLatestVersion($record, $version) { foreach (ClassInfo::ancestry(get_class($record), true) as $table) { $versionForClass = DB::prepared_query($sql = "SELECT MAX(\"Version\") FROM \"{$table}_Versions\" WHERE \"RecordID\" = ?", array($record->ID))->value(); $this->assertEquals($version, $versionForClass, "That the table {$table} has the latest version {$version}"); } }
/** * Return a disabled version of this field. * * Tries to find a class of the class name of this field suffixed with "_Disabled", failing * that, finds a method {@link setDisabled()}. * * @return FormField */ public function performDisabledTransformation() { $disabledClassName = $this->class . '_Disabled'; if (ClassInfo::exists($disabledClassName)) { $clone = $this->castedCopy($disabledClassName); } else { $clone = clone $this; } $clone->setDisabled(true); return $clone; }
/** * Returns true if a class or interface name exists in the manifest. * * @param string $class * @return bool */ public function classExists($class) { Deprecation::notice('4.0', 'Use ClassInfo::exists.'); return ClassInfo::exists($class); }
/** * Traverses the given the given class context looking for candidate template names * which match each item in the class hierarchy. The resulting list of template candidates * may or may not exist, but you can invoke {@see SSViewer::chooseTemplate} on any list * to determine the best candidate based on the current themes. * * @param string|object $classOrObject Valid class name, or object * @param string $suffix * @param string $baseClass Class to halt ancestry search at * @return array */ public static function get_templates_by_class($classOrObject, $suffix = '', $baseClass = null) { // Figure out the class name from the supplied context. if (!is_object($classOrObject) && !(is_string($classOrObject) && class_exists($classOrObject))) { throw new InvalidArgumentException('SSViewer::get_templates_by_class() expects a valid class name as its first parameter.'); } $templates = array(); $classes = array_reverse(ClassInfo::ancestry($classOrObject)); foreach ($classes as $class) { $template = $class . $suffix; $templates[] = $template; $templates[] = ['type' => 'Includes', $template]; // If the class is "Page_Controller", look for Page.ss if (stripos($class, '_controller') !== false) { $templates[] = str_ireplace('_controller', '', $class) . $suffix; } if ($baseClass && $class == $baseClass) { break; } } return $templates; }
public function testEveryFieldTransformsDisabledAsClone() { $fieldClasses = ClassInfo::subclassesFor('SilverStripe\\Forms\\FormField'); foreach ($fieldClasses as $fieldClass) { $reflectionClass = new ReflectionClass($fieldClass); if (!$reflectionClass->isInstantiable()) { continue; } $constructor = $reflectionClass->getMethod('__construct'); if ($constructor->getNumberOfRequiredParameters() > 1) { continue; } if (is_a($fieldClass, 'SilverStripe\\Forms\\CompositeField', true)) { continue; } $fieldName = $reflectionClass->getShortName() . '_instance'; if ($fieldClass = 'SilverStripe\\Forms\\NullableField') { $instance = new $fieldClass(new TextField($fieldName)); } else { $instance = new $fieldClass($fieldName); } $isDisabledBefore = $instance->isDisabled(); $disabledInstance = $instance->performDisabledTransformation(); $this->assertEquals($isDisabledBefore, $instance->isDisabled(), "FormField class {$fieldClass} retains its disabled state after calling performDisabledTransformation()"); $this->assertTrue($disabledInstance->isDisabled(), "FormField class {$fieldClass} returns a valid disabled representation as of isDisabled()"); $this->assertNotSame($disabledInstance, $instance, "FormField class {$fieldClass} returns a valid cloned disabled representation"); } }