diff --git a/core/model/Versioned.php b/core/model/Versioned.php
index d3267ee10..701d092ab 100755
--- a/core/model/Versioned.php
+++ b/core/model/Versioned.php
@@ -70,7 +70,8 @@ class Versioned extends DataObjectDecorator {
AND `_Archive$baseTable`.Version = `{$baseTable}_versions`.Version";
// Get a specific stage
- } else if(Versioned::$reading_stage && Versioned::$reading_stage != $this->defaultStage) {
+ } else if(Versioned::$reading_stage && Versioned::$reading_stage != $this->defaultStage
+ && array_search(Versioned::$reading_stage,$this->stages) !== false) {
foreach($query->from as $table => $dummy) {
$query->renameTable($table, $table . '_' . Versioned::$reading_stage);
}
@@ -104,14 +105,57 @@ class Versioned extends DataObjectDecorator {
GROUP BY RecordID");
}
}
+ /**
+ * An array of DataObject extensions that may require versioning for extra tables
+ * The array value is a set of suffixes to form these table names, assuming a preceding '_'
+ *
+ * e.g. if Extension1 creates a new table 'Class_suffix1'
+ * and Extension2 the tables 'Class_suffix2' and 'Class_suffix3':
+ *
+ * $versionableExtensions = array(
+ * 'Extension1' => 'suffix1',
+ * 'Extension2' => array('suffix2', 'suffix3'),
+ * );
+ *
+ * @var array
+ */
+ protected static $versionableExtensions = array('Translatable' => 'lang');
function augmentDatabase() {
- $table = $this->owner->class;
+ $classTable = $this->owner->class;
- if($fields = $this->owner->databaseFields()) {
+ // Build a list of suffixes whose tables need versioning
+ $allSuffixes = array();
+ foreach (Versioned::$versionableExtensions as $versionableExtension => $suffixes) {
+ if ($this->owner->hasExtension($versionableExtension)) {
+ $allSuffixes = array_merge($allSuffixes, (array)$suffixes);
+ foreach ((array)$suffixes as $suffix) {
+ $allSuffixes[$suffix] = $versionableExtension;
+ }
+ }
+ }
+
+ // Add the zero-suffix table to the list (table name = class name)
+ array_push($allSuffixes,'');
+
+ foreach ($allSuffixes as $key => $suffix) {
+ // check that this is a valid suffix
+ if (!is_int($key)) continue;
+
+ if ($suffix) $table = "{$classTable}_$suffix";
+ else $table = $classTable;
+
+ $tableList = DB::tableList();
+ if(($fields = $this->owner->databaseFields()) && isset($tableList[strtolower($table)])) {
$indexes = $this->owner->databaseIndexes();
if($this->owner->parentClass() == "DataObject") {
$rootTable = true;
+ }
+
+ if ($suffix) {
+ $fields = $this->owner->getExtension($allSuffixes[$suffix])->fieldsInExtraTables($suffix);
+ $indexes = $fields['indexes'];
+ $fields = $fields['db'];
}
// Create tables for other stages
@@ -171,6 +215,7 @@ class Versioned extends DataObjectDecorator {
foreach($this->stages as $stage) {
if($stage != $this->defaultStage) DB::dontrequireTable("{$table}_$stage");
}
+ }
}
}
@@ -180,18 +225,19 @@ class Versioned extends DataObjectDecorator {
*/
function augmentWrite(&$manipulation) {
$tables = array_keys($manipulation);
+ $version_table = array();
foreach($tables as $table) {
- $dbFields = singleton($table)->databaseFields();
// Make sure that the augmented write is being applied to a table that can be versioned
- if( !ClassInfo::exists( $table ) || !is_subclass_of( $table, 'DataObject' ) || empty( $dbFields ) ) {
+ if( !$this->canBeVersioned($table) ) {
// Debug::message( "$table doesn't exist or has no database fields" );
unset($manipulation[$table]);
continue;
}
+ $id = $manipulation[$table]['id'] ? $manipulation[$table]['id'] : $manipulation[$table]['fields']['ID'];//echo 'id' .$id.' from '.$manipulation[$table]['id'].' and '.$manipulation[$table]['fields']['ID']."\n\n
";
+ if(!$id) user_error("Couldn't find ID in " . var_export($manipulation[$table], true), E_USER_ERROR);
- $id = $manipulation[$table]['id'] ? $manipulation[$table]['id'] : $manipulation[$table]['fields']['ID'];
- if(!$id) user_error("Couldn't find ID in " . var_expo * rt($manipulation[$table], true), E_USER_ERROR);
+ $rid = isset($manipulation[$table]['RecordID']) ? $manipulation[$table]['RecordID'] : $id;
$newManipulation = array(
"command" => "insert",
@@ -213,11 +259,13 @@ class Versioned extends DataObjectDecorator {
}
// Set up a new entry in (table)_versions
- $newManipulation['fields']['RecordID'] = $id;
+ $newManipulation['fields']['RecordID'] = $rid;
unset($newManipulation['fields']['ID']);
// Create a new version #
- if($id && !isset($nextVersion)) $nextVersion = DB::query("SELECT MAX(Version) + 1 FROM {$table}_versions WHERE RecordID = $id")->value();
+ if (isset($version_table[$table])) $nextVersion = $version_table[$table];
+ else unset($nextVersion);
+ if($rid && !isset($nextVersion)) $nextVersion = DB::query("SELECT MAX(Version) + 1 FROM {$table}_versions WHERE RecordID = $rid")->value();
$newManipulation['fields']['Version'] = $nextVersion ? $nextVersion : 1;
$newManipulation['fields']['AuthorID'] = Member::currentUserID() ? Member::currentUserID() : 0;
@@ -227,12 +275,14 @@ class Versioned extends DataObjectDecorator {
// Add the version number to this data
$manipulation[$table]['fields']['Version'] = $newManipulation['fields']['Version'];
+ $version_table[$table] = $nextVersion;
}
// Putting a Version of -1 is a signal to leave the version table alone, despite their being no version
if($manipulation[$table]['fields']['Version'] < 0) unset($manipulation[$table]['fields']['Version']);
- if(get_parent_class($table) != "DataObject") unset($manipulation[$table]['fields']['Version']);
+ // TODO : better check (canbeversioned?)
+ //if(get_parent_class($table) != "DataObject") unset($manipulation[$table]['fields']['Version']);
// Grab a version number - it should be the same across all tables.
if(isset($manipulation[$table]['fields']['Version'])) $thisVersion = $manipulation[$table]['fields']['Version'];
@@ -246,7 +296,35 @@ class Versioned extends DataObjectDecorator {
}
// Add the new version # back into the data object, for accessing after this write
- if($thisVersion) $this->owner->Version = str_replace("'","",$thisVersion);
+ if(isset($thisVersion)) $this->owner->Version = str_replace("'","",$thisVersion);
+ }
+
+ function canBeVersioned($table) {
+
+ $tableParts = explode('_',$table);
+ $dbFields = singleton($tableParts[0])->databaseFields();
+ if (!ClassInfo::exists( $tableParts[0] ) || !is_subclass_of( $tableParts[0], 'DataObject' ) || empty( $dbFields )){
+ return false;
+ } else if (count($tableParts)>1) {
+ foreach (Versioned::$versionableExtensions as $versionableExtension => $suffixes) {
+ if ($this->owner->hasExtension($versionableExtension)) {
+ foreach ((array)$suffixes as $suffix) {
+ if ($part = array_search($suffix,$tableParts)) unset($tableParts[$part]);
+ }
+ }
+ }
+ if (count($tableParts)>1) return false;
+ }
+ return true;
+ }
+
+ function extendWithSuffix($table) {
+ foreach (Versioned::$versionableExtensions as $versionableExtension => $suffixes) {
+ if ($this->owner->hasExtension($versionableExtension)) {
+ $table = $this->owner->getExtension($versionableExtension)->extendWithSuffix($table);
+ }
+ }
+ return $table;
}
//-----------------------------------------------------------------------------------------------//
@@ -274,6 +352,7 @@ class Versioned extends DataObjectDecorator {
function publish($fromStage, $toStage, $createNewVersion = false) {
$baseClass = $this->owner->class;
while( ($p = get_parent_class($baseClass)) != "DataObject") $baseClass = $p;
+ $extTable = $this->extendWithSuffix($baseClass);//die($extTable);
if(is_numeric($fromStage)) {
$from = Versioned::get_version($this->owner->class, $this->owner->ID, $fromStage);
@@ -288,7 +367,7 @@ class Versioned extends DataObjectDecorator {
if(!$createNewVersion) $from->migrateVersion($from->Version);
// Mark this version as having been published at some stage
- DB::query("UPDATE `{$baseClass}_versions` SET WasPublished = 1, PublisherID = $publisherID WHERE RecordID = $from->ID AND Version = $from->Version");
+ DB::query("UPDATE `{$extTable}_versions` SET WasPublished = 1, PublisherID = $publisherID WHERE RecordID = $from->ID AND Version = $from->Version");
$oldStage = Versioned::$reading_stage;
Versioned::$reading_stage = $toStage;
@@ -333,11 +412,11 @@ class Versioned extends DataObjectDecorator {
* @param string $filter
*/
function allVersions($filter = "") {
- $query = $this->owner->buildSQL($filter,"");
+ $query = $this->owner->extendedSQL($filter,"");
foreach($query->from as $table => $join) {
if($join[0] == '`') $baseTable = str_replace('`','',$join);
- else $query->from[$table] = "LEFT JOIN `$table` ON `$table`.RecordID = `{$baseTable}_versions`.RecordID AND `$table`.Version = `{$baseTable}_versions`.Version";
+ else if (substr($join,0,5) != 'INNER') $query->from[$table] = "LEFT JOIN `$table` ON `$table`.RecordID = `{$baseTable}_versions`.RecordID AND `$table`.Version = `{$baseTable}_versions`.Version";
$query->renameTable($table, $table . '_versions');
}
$query->select[] = "`{$baseTable}_versions`.AuthorID, `{$baseTable}_versions`.Version, `{$baseTable}_versions`.RecordID";
@@ -501,28 +580,24 @@ class Versioned extends DataObjectDecorator {
* This function is similar in style to {@link DataObject::buildSQL}
*/
function buildVersionSQL($filter = "", $sort = "") {
- $query = $this->owner->buildSQL("","");
+ $query = $this->owner->extendedSQL($filter,$sort);
foreach($query->from as $table => $join) {
if($join[0] == '`') $baseTable = str_replace('`','',$join);
else $query->from[$table] = "LEFT JOIN `$table` ON `$table`.RecordID = `{$baseTable}_versions`.RecordID AND `$table`.Version = `{$baseTable}_versions`.Version";
$query->renameTable($table, $table . '_versions');
}
$query->select[] = "`{$baseTable}_versions`.AuthorID, `{$baseTable}_versions`.Version, `{$baseTable}_versions`.RecordID AS ID";
- if($filter) $query->where[] = $filter;
- if($sort) $query->orderby = $sort;
return $query;
}
static function build_version_sql($className, $filter = "", $sort = "") {
- $query = singleton($className)->buildSQL("","");
+ $query = singleton($className)->extendedSQL($filter,$sort);
foreach($query->from as $table => $join) {
if($join[0] == '`') $baseTable = str_replace('`','',$join);
else $query->from[$table] = "LEFT JOIN `$table` ON `$table`.RecordID = `{$baseTable}_versions`.RecordID AND `$table`.Version = `{$baseTable}_versions`.Version";
$query->renameTable($table, $table . '_versions');
}
$query->select[] = "`{$baseTable}_versions`.AuthorID, `{$baseTable}_versions`.Version, `{$baseTable}_versions`.RecordID AS ID";
- if($filter) $query->where[] = $filter;
- if($sort) $query->orderby = $sort;
return $query;
}
@@ -531,7 +606,7 @@ class Versioned extends DataObjectDecorator {
*/
static function get_latest_version($class, $id) {
$baseTable = ClassInfo::baseDataClass($class);
- $query = singleton($class)->buildVersionSQL("`{$baseTable}_versions`.RecordID = $id", "`{$baseTable}_versions`.Version DESC");
+ $query = singleton($class)->buildVersionSQL("`{$baseTable}`.RecordID = $id", "`{$baseTable}`.Version DESC");
$query->limit = 1;
$record = $query->execute()->record();
$className = $record['ClassName'];
@@ -545,7 +620,7 @@ class Versioned extends DataObjectDecorator {
static function get_version($class, $id, $version) {
$baseTable = ClassInfo::baseDataClass($class);
- $query = singleton($class)->buildVersionSQL("`{$baseTable}_versions`.RecordID = $id AND `{$baseTable}_versions`.Version = $version");
+ $query = singleton($class)->buildVersionSQL("`{$baseTable}`.RecordID = $id AND `{$baseTable}`.Version = $version");
$record = $query->execute()->record();
$className = $record['ClassName'];
if(!$className) {
@@ -558,7 +633,7 @@ class Versioned extends DataObjectDecorator {
static function get_all_versions($class, $id, $version) {
$baseTable = ClassInfo::baseDataClass($class);
- $query = singleton($class)->buildVersionSQL("`{$baseTable}_versions`.RecordID = $id AND `{$baseTable}_versions`.Version = $version");
+ $query = singleton($class)->buildVersionSQL("`{$baseTable}`.RecordID = $id AND `{$baseTable}`.Version = $version");
$record = $query->execute()->record();
$className = $record[ClassName];
if(!$className) {