예제 #1
0
 /**
  * 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('SilverStripe\\Security\\Member');
     $requiredClasses[] = 'SilverStripe\\Security\\Group';
     $requiredClasses[] = 'SilverStripe\\Security\\Permission';
     foreach ($requiredClasses as $class) {
         // Skip test classes, as not all test classes are scaffolded at once
         if (is_subclass_of($class, 'TestOnly')) {
             continue;
         }
         // if any of the tables aren't created in the database
         $table = DataObject::getSchema()->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 = DataObject::database_fields($class);
         $missingFields = array_diff_key($objFields, $dbFields);
         if ($missingFields) {
             return false;
         }
     }
     self::$database_is_ready = true;
     return true;
 }
 /**
  * Update the SELECT clause of the query with the columns from the given table
  *
  * @param SQLSelect $query
  * @param string $tableClass Class to select from
  * @param array $columns
  */
 protected function selectColumnsFromTable(SQLSelect &$query, $tableClass, $columns = null)
 {
     // Add SQL for multi-value fields
     $databaseFields = DataObject::database_fields($tableClass);
     $compositeFields = DataObject::composite_fields($tableClass, false);
     unset($databaseFields['ID']);
     foreach ($databaseFields as $k => $v) {
         if ((is_null($columns) || in_array($k, $columns)) && !isset($compositeFields[$k])) {
             // Update $collidingFields if necessary
             $expressionForField = $query->expressionForField($k);
             $quotedField = DataObject::getSchema()->sqlColumnForField($tableClass, $k);
             if ($expressionForField) {
                 if (!isset($this->collidingFields[$k])) {
                     $this->collidingFields[$k] = array($expressionForField);
                 }
                 $this->collidingFields[$k][] = $quotedField;
             } else {
                 $query->selectField($quotedField, $k);
             }
         }
     }
     foreach ($compositeFields as $k => $v) {
         if ((is_null($columns) || in_array($k, $columns)) && $v) {
             $tableName = DataObject::getSchema()->tableName($tableClass);
             $dbO = Object::create_from_string($v, $k);
             $dbO->setTable($tableName);
             $dbO->addToQuery($query);
         }
     }
 }
 /**
  * Generates a ($table)_version DB manipulation and injects it into the current $manipulation
  *
  * @param array $manipulation Source manipulation data
  * @param string $class Class
  * @param string $table Table Table for this class
  * @param int $recordID ID of record to version
  */
 protected function augmentWriteVersioned(&$manipulation, $class, $table, $recordID)
 {
     $baseDataClass = DataObject::getSchema()->baseDataClass($class);
     $baseDataTable = DataObject::getSchema()->tableName($baseDataClass);
     // Set up a new entry in (table)_versions
     $newManipulation = array("command" => "insert", "fields" => isset($manipulation[$table]['fields']) ? $manipulation[$table]['fields'] : [], "class" => $class);
     // Add any extra, unchanged fields to the version record.
     $data = DB::prepared_query("SELECT * FROM \"{$table}\" WHERE \"ID\" = ?", array($recordID))->record();
     if ($data) {
         $fields = DataObject::database_fields($class);
         if (is_array($fields)) {
             $data = array_intersect_key($data, $fields);
             foreach ($data as $k => $v) {
                 // If the value is not set at all in the manipulation currently, use the existing value from the database
                 if (!array_key_exists($k, $newManipulation['fields'])) {
                     $newManipulation['fields'][$k] = $v;
                 }
             }
         }
     }
     // Ensure that the ID is instead written to the RecordID field
     $newManipulation['fields']['RecordID'] = $recordID;
     unset($newManipulation['fields']['ID']);
     // Generate next version ID to use
     $nextVersion = 0;
     if ($recordID) {
         $nextVersion = DB::prepared_query("SELECT MAX(\"Version\") + 1\n\t\t\t\tFROM \"{$baseDataTable}_versions\" WHERE \"RecordID\" = ?", array($recordID))->value();
     }
     $nextVersion = $nextVersion ?: 1;
     if ($class === $baseDataClass) {
         // Write AuthorID for baseclass
         $userID = Member::currentUser() ? Member::currentUser()->ID : 0;
         $newManipulation['fields']['AuthorID'] = $userID;
         // Update main table version if not previously known
         $manipulation[$table]['fields']['Version'] = $nextVersion;
     }
     // Update _versions table manipulation
     $newManipulation['fields']['Version'] = $nextVersion;
     $manipulation["{$table}_versions"] = $newManipulation;
 }
 /**
  * Tests the generation of the ClassName spec and ensure it's not unnecessarily influenced
  * by the order of classnames of existing records
  */
 public function testClassNameSpecGeneration()
 {
     // Test with blank entries
     DBClassName::clear_classname_cache();
     $do1 = new DataObjectSchemaGenerationTest_DO();
     $fields = DataObject::database_fields('DataObjectSchemaGenerationTest_DO');
     /** @skipUpgrade */
     $this->assertEquals("DBClassName", $fields['ClassName']);
     $this->assertEquals(array('DataObjectSchemaGenerationTest_DO' => 'DataObjectSchemaGenerationTest_DO', 'DataObjectSchemaGenerationTest_IndexDO' => 'DataObjectSchemaGenerationTest_IndexDO'), $do1->dbObject('ClassName')->getEnum());
     // Test with instance of subclass
     $item1 = new DataObjectSchemaGenerationTest_IndexDO();
     $item1->write();
     DBClassName::clear_classname_cache();
     $fields = DataObject::database_fields('DataObjectSchemaGenerationTest_DO');
     /** @skipUpgrade */
     $this->assertEquals("DBClassName", $fields['ClassName']);
     $this->assertEquals(array('DataObjectSchemaGenerationTest_DO' => 'DataObjectSchemaGenerationTest_DO', 'DataObjectSchemaGenerationTest_IndexDO' => 'DataObjectSchemaGenerationTest_IndexDO'), $item1->dbObject('ClassName')->getEnum());
     $item1->delete();
     // Test with instance of main class
     $item2 = new DataObjectSchemaGenerationTest_DO();
     $item2->write();
     DBClassName::clear_classname_cache();
     $fields = DataObject::database_fields('DataObjectSchemaGenerationTest_DO');
     /** @skipUpgrade */
     $this->assertEquals("DBClassName", $fields['ClassName']);
     $this->assertEquals(array('DataObjectSchemaGenerationTest_DO' => 'DataObjectSchemaGenerationTest_DO', 'DataObjectSchemaGenerationTest_IndexDO' => 'DataObjectSchemaGenerationTest_IndexDO'), $item2->dbObject('ClassName')->getEnum());
     $item2->delete();
     // Test with instances of both classes
     $item1 = new DataObjectSchemaGenerationTest_IndexDO();
     $item1->write();
     $item2 = new DataObjectSchemaGenerationTest_DO();
     $item2->write();
     DBClassName::clear_classname_cache();
     $fields = DataObject::database_fields('DataObjectSchemaGenerationTest_DO');
     /** @skipUpgrade */
     $this->assertEquals("DBClassName", $fields['ClassName']);
     $this->assertEquals(array('DataObjectSchemaGenerationTest_DO' => 'DataObjectSchemaGenerationTest_DO', 'DataObjectSchemaGenerationTest_IndexDO' => 'DataObjectSchemaGenerationTest_IndexDO'), $item1->dbObject('ClassName')->getEnum());
     $item1->delete();
     $item2->delete();
 }
 /**
  * @todo Re-enable all test cases for field inheritance aggregation after behaviour has been fixed
  */
 public function testFieldInheritance()
 {
     $teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1');
     $subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
     $this->assertEquals(array('ID', 'ClassName', 'LastEdited', 'Created', 'Title', 'DatabaseField', 'ExtendedDatabaseField', 'CaptainID', 'FounderID', 'HasOneRelationshipID', 'ExtendedHasOneRelationshipID'), array_keys($teamInstance->db()), 'inheritedDatabaseFields() contains all fields defined on instance: base, extended and foreign keys');
     $this->assertEquals(array('ID', 'ClassName', 'LastEdited', 'Created', 'Title', 'DatabaseField', 'ExtendedDatabaseField', 'CaptainID', 'FounderID', 'HasOneRelationshipID', 'ExtendedHasOneRelationshipID'), array_keys(DataObject::database_fields('DataObjectTest_Team', false)), 'databaseFields() contains only fields defined on instance, including base, extended and foreign keys');
     $this->assertEquals(array('ID', 'ClassName', 'LastEdited', 'Created', 'Title', 'DatabaseField', 'ExtendedDatabaseField', 'CaptainID', 'FounderID', 'HasOneRelationshipID', 'ExtendedHasOneRelationshipID', 'SubclassDatabaseField', 'ParentTeamID'), array_keys($subteamInstance->db()), 'inheritedDatabaseFields() on subclass contains all fields, including base, extended  and foreign keys');
     $this->assertEquals(array('ID', 'SubclassDatabaseField', 'ParentTeamID'), array_keys(DataObject::database_fields('DataObjectTest_SubTeam')), 'databaseFields() on subclass contains only fields defined on instance');
 }