diff --git a/model/Versioned.php b/model/Versioned.php index 1b56c924a..4444bd5c1 100644 --- a/model/Versioned.php +++ b/model/Versioned.php @@ -111,13 +111,14 @@ class Versioned extends DataExtension { * Amend freshly created DataQuery objects with versioned-specific information */ function augmentDataQueryCreation(SQLQuery &$query, DataQuery &$dataQuery) { - if($date = Versioned::$reading_archived_date) { + $parts = explode('.', Versioned::get_reading_mode()); + if($parts[0] == 'Archive') { $dataQuery->setQueryParam('Versioned.mode', 'archive'); - $dataQuery->setQueryParam('Versioned.date', Versioned::$reading_archived_date); + $dataQuery->setQueryParam('Versioned.date', $parts[1]); - } else if(Versioned::$reading_stage && Versioned::$reading_stage != $this->defaultStage && array_search(Versioned::$reading_stage,$this->stages) !== false) { + } else if($parts[0] == 'Stage' && $parts[1] != $this->defaultStage && array_search($parts[1],$this->stages) !== false) { $dataQuery->setQueryParam('Versioned.mode', 'stage'); - $dataQuery->setQueryParam('Versioned.stage', Versioned::$reading_stage); + $dataQuery->setQueryParam('Versioned.stage', $parts[1]); } } @@ -127,14 +128,17 @@ class Versioned extends DataExtension { * @todo Should this all go into VersionedDataQuery? */ function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery) { + $baseTable = ClassInfo::baseDataClass($dataQuery->dataClass()); + switch($dataQuery->getQueryParam('Versioned.mode')) { + // Noop + case '': + break; + // Reading a specific data from the archive case 'archive': $date = $dataQuery->getQueryParam('Versioned.date'); foreach($query->from as $table => $dummy) { - if(!isset($baseTable)) { - $baseTable = $table; - } $query->renameTable($table, $table . '_versions'); $query->replaceText("\"$table\".\"ID\"", "\"$table\".\"RecordID\""); @@ -172,6 +176,36 @@ class Versioned extends DataExtension { } } break; + + + // Return all version instances + case 'all_versions': + case 'latest_versions': + foreach($query->from as $alias => $join) { + if($alias != $baseTable) { + $query->setJoinFilter($alias, "\"$alias\".\"RecordID\" = \"{$baseTable}_versions\".\"RecordID\" AND \"$alias\".\"Version\" = \"{$baseTable}_versions\".\"Version\""); + } + $query->renameTable($alias, $alias . '_versions'); + } + + // Add all _versions columns + foreach(self::$db_for_versions_table as $name => $type) { + $query->selectMore(sprintf('"%s_versions"."%s"', $baseTable, $name)); + } + $query->selectMore(sprintf('"%s_versions"."%s" AS "ID"', $baseTable, 'RecordID')); + + // latest_version has one more step + // Return latest version instances, regardless of whether they are on a particular stage + // This provides "show all, including deleted" functonality + if($dataQuery->getQueryParam('Versioned.mode') == 'latest_versions') { + $archiveTable = self::requireArchiveTempTable($baseTable); + $query->innerJoin($archiveTable, "\"$archiveTable\".\"ID\" = \"{$baseTable}_versions\".\"RecordID\" AND \"$archiveTable\".\"Version\" = \"{$baseTable}_versions\".\"Version\""); + } + + break; + + default: + throw new InvalidArgumentException("Bad value for query parameter Versioned.mode: " . $dataQuery->getQueryParam('Versioned.mode')); } } @@ -803,17 +837,11 @@ class Versioned extends DataExtension { * @param string $orderby A sort expression to be inserted into the ORDER BY clause. * @return DataObject */ - static function get_one_by_stage($class, $stage, $filter = '', $cache = true, $orderby = '') { - $oldMode = Versioned::get_reading_mode(); - Versioned::reading_stage($stage); - - singleton($class)->flushCache(); - $result = DataObject::get_one($class, $filter, $cache, $orderby); - singleton($class)->flushCache(); - - Versioned::set_reading_mode($oldMode); - return $result; - } + static function get_one_by_stage($class, $stage, $filter = '', $cache = true, $sort = '') { + // TODO: No identity cache operating + $items = self::get_by_stage($class, $stage, $filter, $sort, null, 1); + return $items->First(); + } /** * Gets the current version number of a specific record. @@ -832,7 +860,7 @@ class Versioned extends DataExtension { if($cache && isset(self::$cache_versionnumber[$baseClass][$stage][$id])) { return self::$cache_versionnumber[$baseClass][$stage][$id]; } - + // get version as performance-optimized SQL query (gets called for each page in the sitetree) $version = DB::query("SELECT \"Version\" FROM \"$stageTable\" WHERE \"ID\" = $id")->value(); @@ -908,7 +936,7 @@ class Versioned extends DataExtension { Versioned::set_reading_mode($oldMode); return $result; } - + /** * Roll the draft version of this page to match the published page. * Caution: Doesn't overwrite the object properties with the rolled back version. @@ -919,44 +947,6 @@ class Versioned extends DataExtension { $this->publish($version, "Stage", true); $this->owner->writeWithoutVersion(); } - - /** - * Build a SQL query to get data from the _version table. - * This function is similar in style to {@link DataObject::buildSQL} - */ - function buildVersionSQL($filter = "", $sort = "") { - $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'); - } - - // Add all _versions columns - foreach(self::$db_for_versions_table as $name => $type) { - $query->select[] = sprintf('"%s_versions"."%s"', $baseTable, $name); - } - $query->select[] = sprintf('"%s_versions"."%s" AS "ID"', $baseTable, 'RecordID'); - - return $query; - } - - static function build_version_sql($className, $filter = "", $sort = "") { - $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'); - } - - // Add all _versions columns - foreach(self::$db_for_versions_table as $name => $type) { - $query->select[] = sprintf('"%s_versions"."%s"', $baseTable, $name); - } - $query->select[] = sprintf('"%s_versions"."%s" AS "ID"', $baseTable, 'RecordID'); - - return $query; - } /** * Return the latest version of the given page. @@ -964,25 +954,10 @@ class Versioned extends DataExtension { * @return DataObject */ static function get_latest_version($class, $id) { - $oldMode = Versioned::get_reading_mode(); - Versioned::set_reading_mode(''); - - $baseTable = ClassInfo::baseDataClass($class); - $query = singleton($class)->buildVersionSQL("\"{$baseTable}\".\"RecordID\" = $id", "\"{$baseTable}\".\"Version\" DESC"); - $query->limit = 1; - $record = $query->execute()->record(); - if(!$record) return; - - $className = $record['ClassName']; - if(!$className) { - Debug::show($query->sql()); - Debug::show($record); - user_error("Versioned::get_version: Couldn't get $class.$id", E_USER_ERROR); - } - - Versioned::set_reading_mode($oldMode); - - return new $className($record); + $baseClass = ClassInfo::baseDataClass($class); + $list = DataList::create($class)->filter("\"$baseClass\".\"RecordID\" = $id"); + $list->dataQuery()->setQueryParam("Versioned.mode", "latest_versions"); + return $list->First(); } /** @@ -992,61 +967,31 @@ class Versioned extends DataExtension { * In particular, this will query deleted records as well as active ones. */ static function get_including_deleted($class, $filter = "", $sort = "") { - $oldMode = Versioned::get_reading_mode(); - Versioned::set_reading_mode(''); - - $SNG = singleton($class); - - // Build query - $query = $SNG->buildVersionSQL($filter, $sort); - $baseTable = ClassInfo::baseDataClass($class); - $archiveTable = self::requireArchiveTempTable($baseTable); - $query->from[$archiveTable] = "INNER JOIN \"$archiveTable\" - ON \"$archiveTable\".\"ID\" = \"{$baseTable}_versions\".\"RecordID\" - AND \"$archiveTable\".\"Version\" = \"{$baseTable}_versions\".\"Version\""; - - // Process into a DataObjectSet - $result = $SNG->buildDataObjectSet($query->execute(), 'DataObjectSet', null, $class); - - Versioned::set_reading_mode($oldMode); - return $query; + $list = DataList::create($class)->filter($filter)->sort($sort); + $list->dataQuery()->setQueryParam("Versioned.mode", "latest_versions"); + return $list; } /** + * Return the specific version of the given id * @return DataObject */ static function get_version($class, $id, $version) { - $oldMode = Versioned::get_reading_mode(); - Versioned::set_reading_mode(''); - - $baseTable = ClassInfo::baseDataClass($class); - $query = singleton($class)->buildVersionSQL("\"{$baseTable}\".\"RecordID\" = $id AND \"{$baseTable}\".\"Version\" = $version"); - $record = $query->execute()->record(); - $className = $record['ClassName']; - if(!$className) { - Debug::show($query->sql()); - Debug::show($record); - user_error("Versioned::get_version: Couldn't get $class.$id, version $version", E_USER_ERROR); - } - - Versioned::set_reading_mode($oldMode); - return new $className($record); + $baseClass = ClassInfo::baseDataClass($class); + $list = DataList::create($class)->filter("\"$baseClass\".\"RecordID\" = $id")->filter("\"$baseClass\".\"Version\" = " . (int)$version); + $list->dataQuery()->setQueryParam('Versioned.mode', 'all_versions'); + return $list->First(); } /** - * @return DataObject + * Return a list of all versions for a given id + * @return DataList */ - static function get_all_versions($class, $id, $version) { - $baseTable = ClassInfo::baseDataClass($class); - $query = singleton($class)->buildVersionSQL("\"{$baseTable}\".\"RecordID\" = $id AND \"{$baseTable}\".\"Version\" = $version"); - $record = $query->execute()->record(); - $className = $record['ClassName']; - if(!$className) { - Debug::show($query->sql()); - Debug::show($record); - user_error("Versioned::get_version: Couldn't get $class.$id, version $version", E_USER_ERROR); - } - return new $className($record); + static function get_all_versions($class, $id) { + $baseClass = ClassInfo::baseDataClass($class); + $list = DataList::create($class)->filter("\"$baseClass\".\"RecordID\" = $id"); + $list->dataQuery()->setQueryParam('Versioned.mode', 'all_versions'); + return $list; } function contentcontrollerInit($controller) { @@ -1070,7 +1015,7 @@ class Versioned extends DataExtension { * Return a piece of text to keep DataObject cache keys appropriately specific */ function cacheKeyComponent() { - return 'stage-'.self::current_stage(); + return 'versionedmode-'.self::get_reading_mode(); } }