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");
     }
 }