PHPDoc improvements for Hierarchy and Versioned

- Added missing @param and @method documentation
- Fixed incorrect PHPDoc @params and @returns
- Fixed wrapping width on many docblocks
- Added and/or improved method documentation when missing or confusing
- Fixed typos as found
- Removed references to 'Page' in Versioned documentation
This commit is contained in:
Nik Rolls 2014-12-02 01:20:09 +13:00 committed by Sam Minnee
parent 8ebdedf330
commit 9791426be6
2 changed files with 314 additions and 278 deletions

View File

@ -1,9 +1,14 @@
<?php <?php
/** /**
* DataObjects that use the Hierarchy extension can be be organised as a hierarchy, with children and parents. * DataObjects that use the Hierarchy extension can be be organised as a hierarchy, with children and parents. The most
* The most obvious example of this is SiteTree. * obvious example of this is SiteTree.
*
* @package framework * @package framework
* @subpackage model * @subpackage model
*
* @property int ParentID
* @property DataObject owner
* @method DataObject Parent
*/ */
class Hierarchy extends DataExtension { class Hierarchy extends DataExtension {
@ -11,30 +16,28 @@ class Hierarchy extends DataExtension {
protected $markingFilter; protected $markingFilter;
/** /** @var int */
* @var Int
*/
protected $_cache_numChildren; protected $_cache_numChildren;
/** /**
* The lower bounds for the amount of nodes to mark. If set, the logic will expand nodes until it reaches at least
* this number, and then stops. Root nodes will always show regardless of this settting. Further nodes can be
* lazy-loaded via ajax. This isn't a hard limit. Example: On a value of 10, with 20 root nodes, each having 30
* children, the actual node count will be 50 (all root nodes plus first expanded child).
*
* @config * @config
* @var integer The lower bounds for the amount of nodes to mark. If set, the logic will expand * @var int
* nodes until it reaches at least this number, and then stops. Root nodes will always
* show regardless of this settting. Further nodes can be lazy-loaded via ajax.
* This isn't a hard limit. Example: On a value of 10, with 20 root nodes, each having
* 30 children, the actual node count will be 50 (all root nodes plus first expanded child).
*/ */
private static $node_threshold_total = 50; private static $node_threshold_total = 50;
/** /**
* Limit on the maximum children a specific node can display. Serves as a hard limit to avoid exceeding available
* server resources in generating the tree, and browser resources in rendering it. Nodes with children exceeding
* this value typically won't display any children, although this is configurable through the $nodeCountCallback
* parameter in {@link getChildrenAsUL()}. "Root" nodes will always show all children, regardless of this setting.
*
* @config * @config
* @var integer Limit on the maximum children a specific node can display. * @var int
* Serves as a hard limit to avoid exceeding available server resources
* in generating the tree, and browser resources in rendering it.
* Nodes with children exceeding this value typically won't display
* any children, although this is configurable through the $nodeCountCallback
* parameter in {@link getChildrenAsUL()}. "Root" nodes will always show
* all children, regardless of this setting.
*/ */
private static $node_threshold_leaf = 250; private static $node_threshold_leaf = 250;
@ -46,6 +49,8 @@ class Hierarchy extends DataExtension {
/** /**
* Validate the owner object - check for existence of infinite loops. * Validate the owner object - check for existence of infinite loops.
*
* @param ValidationResult $validationResult
*/ */
public function validate(ValidationResult $validationResult) { public function validate(ValidationResult $validationResult) {
// The object is new, won't be looping. // The object is new, won't be looping.
@ -78,19 +83,21 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Returns the children of this DataObject as an XHTML UL. This will be called recursively on each child, * Returns the children of this DataObject as an XHTML UL. This will be called recursively on each child, so if they
* so if they have children they will be displayed as a UL inside a LI. * have children they will be displayed as a UL inside a LI.
* @param string $attributes Attributes to add to the UL. *
* @param string|callable $titleEval PHP code to evaluate to start each child - this should include '<li>' * @param string $attributes Attributes to add to the UL
* @param string $extraArg Extra arguments that will be passed on to children, for if they overload this function. * @param string|callable $titleEval PHP code to evaluate to start each child - this should include '<li>'
* @param boolean $limitToMarked Display only marked children. * @param string $extraArg Extra arguments that will be passed on to children, for if they
* @param string $childrenMethod The name of the method used to get children from each object * overload this function
* @param boolean $rootCall Set to true for this first call, and then to false for calls inside the recursion. You * @param bool $limitToMarked Display only marked children
* should not change this. * @param string $childrenMethod The name of the method used to get children from each object
* @param int $nodeCountThreshold See {@link self::$node_threshold_total} * @param bool $rootCall Set to true for this first call, and then to false for calls inside
* @param callable $nodeCountCallback Called with the node count, which gives the callback an opportunity * the recursion. You should not change this.
* to intercept the query. Useful e.g. to avoid excessive children listings * @param int $nodeCountThreshold See {@link self::$node_threshold_total}
* (Arguments: $parent, $numChildren) * @param callable $nodeCountCallback Called with the node count, which gives the callback an opportunity to
* intercept the query. Useful e.g. to avoid excessive children listings
* (Arguments: $parent, $numChildren)
* *
* @return string * @return string
*/ */
@ -175,11 +182,12 @@ class Hierarchy extends DataExtension {
/** /**
* Mark a segment of the tree, by calling mark(). * Mark a segment of the tree, by calling mark().
* The method performs a breadth-first traversal until the number of nodes is more than minCount.
* This is used to get a limited number of tree nodes to show in the CMS initially.
* *
* This method returns the number of nodes marked. After this method is called other methods * The method performs a breadth-first traversal until the number of nodes is more than minCount. This is used to
* can check isExpanded() and isMarked() on individual nodes. * get a limited number of tree nodes to show in the CMS initially.
*
* This method returns the number of nodes marked. After this method is called other methods can check
* {@link isExpanded()} and {@link isMarked()} on individual nodes.
* *
* @param int $nodeCountThreshold See {@link getChildrenAsUL()} * @param int $nodeCountThreshold See {@link getChildrenAsUL()}
* @return int The actual number of nodes marked. * @return int The actual number of nodes marked.
@ -205,9 +213,10 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Filter the marking to only those object with $node->$parameterName = $parameterValue * Filter the marking to only those object with $node->$parameterName == $parameterValue
* @param string $parameterName The parameter on each node to check when marking. *
* @param mixed $parameterValue The value the parameter must be to be marked. * @param string $parameterName The parameter on each node to check when marking.
* @param mixed $parameterValue The value the parameter must be to be marked.
*/ */
public function setMarkingFilter($parameterName, $parameterValue) { public function setMarkingFilter($parameterName, $parameterValue) {
$this->markingFilter = array( $this->markingFilter = array(
@ -217,9 +226,10 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Filter the marking to only those where the function returns true. * Filter the marking to only those where the function returns true. The node in question will be passed to the
* The node in question will be passed to the function. * function.
* @param string $funcName The function name. *
* @param string $funcName The name of the function to call
*/ */
public function setMarkingFilterFunction($funcName) { public function setMarkingFilterFunction($funcName) {
$this->markingFilter = array( $this->markingFilter = array(
@ -229,8 +239,9 @@ class Hierarchy extends DataExtension {
/** /**
* Returns true if the marking filter matches on the given node. * Returns true if the marking filter matches on the given node.
* @param DataObject $node Node to check. *
* @return boolean * @param DataObject $node Node to check
* @return bool
*/ */
public function markingFilterMatches($node) { public function markingFilterMatches($node) {
if(!$this->markingFilter) { if(!$this->markingFilter) {
@ -257,7 +268,11 @@ class Hierarchy extends DataExtension {
/** /**
* Mark all children of the given node that match the marking filter. * Mark all children of the given node that match the marking filter.
* @param DataObject $node Parent node. *
* @param DataObject $node Parent node
* @param mixed $context
* @param string $childrenMethod The name of the instance method to call to get the object's list of children
* @param string $numChildrenMethod The name of the instance method to call to count the object's children
* @return DataList * @return DataList
*/ */
public function markChildren($node, $context = null, $childrenMethod = "AllChildrenIncludingDeleted", public function markChildren($node, $context = null, $childrenMethod = "AllChildrenIncludingDeleted",
@ -288,8 +303,10 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Ensure marked nodes that have children are also marked expanded. * Ensure marked nodes that have children are also marked expanded. Call this after marking but before iterating
* Call this after marking but before iterating over the tree. * over the tree.
*
* @param string $numChildrenMethod The name of the instance method to call to count the object's children
*/ */
protected function markingFinished($numChildrenMethod = "numChildren") { protected function markingFinished($numChildrenMethod = "numChildren") {
// Mark childless nodes as expanded. // Mark childless nodes as expanded.
@ -303,9 +320,10 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Return CSS classes of 'unexpanded', 'closed', both, or neither, as well as a * Return CSS classes of 'unexpanded', 'closed', both, or neither, as well as a 'jstree-*' state depending on the
* 'jstree-*' state depending on the marking of this DataObject. * marking of this DataObject.
* *
* @param string $numChildrenMethod The name of the instance method to call to count the object's children
* @return string * @return string
*/ */
public function markingClasses($numChildrenMethod="numChildren") { public function markingClasses($numChildrenMethod="numChildren") {
@ -327,8 +345,10 @@ class Hierarchy extends DataExtension {
/** /**
* Mark the children of the DataObject with the given ID. * Mark the children of the DataObject with the given ID.
* @param int $id ID of parent node. *
* @param boolean $open If this is true, mark the parent node as opened. * @param int $id ID of parent node
* @param bool $open If this is true, mark the parent node as opened
* @return bool
*/ */
public function markById($id, $open = false) { public function markById($id, $open = false) {
if(isset($this->markedNodes[$id])) { if(isset($this->markedNodes[$id])) {
@ -344,6 +364,7 @@ class Hierarchy extends DataExtension {
/** /**
* Expose the given object in the tree, by marking this page and all it ancestors. * Expose the given object in the tree, by marking this page and all it ancestors.
*
* @param DataObject $childObj * @param DataObject $childObj
*/ */
public function markToExpose($childObj) { public function markToExpose($childObj) {
@ -356,7 +377,9 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Return the IDs of all the marked nodes * Return the IDs of all the marked nodes.
*
* @return array
*/ */
public function markedNodeIDs() { public function markedNodeIDs() {
return array_keys($this->markedNodes); return array_keys($this->markedNodes);
@ -364,7 +387,8 @@ class Hierarchy extends DataExtension {
/** /**
* Return an array of this page and its ancestors, ordered item -> root. * Return an array of this page and its ancestors, ordered item -> root.
* @return array *
* @return SiteTree[]
*/ */
public function parentStack() { public function parentStack() {
$p = $this->owner; $p = $this->owner;
@ -378,20 +402,20 @@ class Hierarchy extends DataExtension {
} }
/** /**
* True if this DataObject is marked. * Cache of DataObjects' marked statuses: [ClassName][ID] = bool
* @var boolean * @var array
*/ */
protected static $marked = array(); protected static $marked = array();
/** /**
* True if this DataObject is expanded. * Cache of DataObjects' expanded statuses: [ClassName][ID] = bool
* @var boolean * @var array
*/ */
protected static $expanded = array(); protected static $expanded = array();
/** /**
* True if this DataObject is opened. * Cache of DataObjects' opened statuses: [ClassName][ID] = bool
* @var boolean * @var array
*/ */
protected static $treeOpened = array(); protected static $treeOpened = array();
@ -430,7 +454,8 @@ class Hierarchy extends DataExtension {
/** /**
* Check if this DataObject is marked. * Check if this DataObject is marked.
* @return boolean *
* @return bool
*/ */
public function isMarked() { public function isMarked() {
$baseClass = ClassInfo::baseDataClass($this->owner->class); $baseClass = ClassInfo::baseDataClass($this->owner->class);
@ -440,7 +465,8 @@ class Hierarchy extends DataExtension {
/** /**
* Check if this DataObject is expanded. * Check if this DataObject is expanded.
* @return boolean *
* @return bool
*/ */
public function isExpanded() { public function isExpanded() {
$baseClass = ClassInfo::baseDataClass($this->owner->class); $baseClass = ClassInfo::baseDataClass($this->owner->class);
@ -450,6 +476,8 @@ class Hierarchy extends DataExtension {
/** /**
* Check if this DataObject's tree is opened. * Check if this DataObject's tree is opened.
*
* @return bool
*/ */
public function isTreeOpened() { public function isTreeOpened() {
$baseClass = ClassInfo::baseDataClass($this->owner->class); $baseClass = ClassInfo::baseDataClass($this->owner->class);
@ -459,7 +487,8 @@ class Hierarchy extends DataExtension {
/** /**
* Get a list of this DataObject's and all it's descendants IDs. * Get a list of this DataObject's and all it's descendants IDs.
* @return int *
* @return int[]
*/ */
public function getDescendantIDList() { public function getDescendantIDList() {
$idList = array(); $idList = array();
@ -468,8 +497,9 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Get a list of this DataObject's and all it's descendants ID, and put it in $idList. * Get a list of this DataObject's and all it's descendants ID, and put them in $idList.
* @var array $idList Array to put results in. *
* @param array $idList Array to put results in.
*/ */
public function loadDescendantIDListInto(&$idList) { public function loadDescendantIDListInto(&$idList) {
if($children = $this->AllChildren()) { if($children = $this->AllChildren()) {
@ -488,7 +518,8 @@ class Hierarchy extends DataExtension {
/** /**
* Get the children for this DataObject. * Get the children for this DataObject.
* @return ArrayList *
* @return DataList
*/ */
public function Children() { public function Children() {
if(!(isset($this->_cache_children) && $this->_cache_children)) { if(!(isset($this->_cache_children) && $this->_cache_children)) {
@ -506,7 +537,8 @@ class Hierarchy extends DataExtension {
/** /**
* Return all children, including those 'not in menus'. * Return all children, including those 'not in menus'.
* @return SS_List *
* @return DataList
*/ */
public function AllChildren() { public function AllChildren() {
return $this->owner->stageChildren(true); return $this->owner->stageChildren(true);
@ -514,11 +546,13 @@ class Hierarchy extends DataExtension {
/** /**
* Return all children, including those that have been deleted but are still in live. * Return all children, including those that have been deleted but are still in live.
* Deleted children will be marked as "DeletedFromStage" * - Deleted children will be marked as "DeletedFromStage"
* Added children will be marked as "AddedToStage" * - Added children will be marked as "AddedToStage"
* Modified children will be marked as "ModifiedOnStage" * - Modified children will be marked as "ModifiedOnStage"
* Everything else has "SameOnStage" set, as an indicator that this information has been looked up. * - Everything else has "SameOnStage" set, as an indicator that this information has been looked up.
* @return SS_List *
* @param mixed $context
* @return ArrayList
*/ */
public function AllChildrenIncludingDeleted($context = null) { public function AllChildrenIncludingDeleted($context = null) {
return $this->doAllChildrenIncludingDeleted($context); return $this->doAllChildrenIncludingDeleted($context);
@ -527,8 +561,8 @@ class Hierarchy extends DataExtension {
/** /**
* @see AllChildrenIncludingDeleted * @see AllChildrenIncludingDeleted
* *
* @param unknown_type $context * @param mixed $context
* @return SS_List * @return ArrayList
*/ */
public function doAllChildrenIncludingDeleted($context = null) { public function doAllChildrenIncludingDeleted($context = null) {
if(!$this->owner) user_error('Hierarchy::doAllChildrenIncludingDeleted() called without $this->owner'); if(!$this->owner) user_error('Hierarchy::doAllChildrenIncludingDeleted() called without $this->owner');
@ -560,8 +594,10 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Return all the children that this page had, including pages that were deleted * Return all the children that this page had, including pages that were deleted from both stage & live.
* from both stage & live. *
* @return DataList
* @throws Exception
*/ */
public function AllHistoricalChildren() { public function AllHistoricalChildren() {
if(!$this->owner->hasExtension('Versioned')) { if(!$this->owner->hasExtension('Versioned')) {
@ -574,7 +610,10 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Return the number of children that this page ever had, including pages that were deleted * Return the number of children that this page ever had, including pages that were deleted.
*
* @return int
* @throws Exception
*/ */
public function numHistoricalChildren() { public function numHistoricalChildren() {
if(!$this->owner->hasExtension('Versioned')) { if(!$this->owner->hasExtension('Versioned')) {
@ -586,11 +625,10 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Return the number of direct children. * Return the number of direct children. By default, values are cached after the first invocation. Can be
* By default, values are cached after the first invocation. * augumented by {@link augmentNumChildrenCountQuery()}.
* Can be augumented by {@link augmentNumChildrenCountQuery()}.
* *
* @param Boolean $cache * @param bool $cache Whether to retrieve values from cache
* @return int * @return int
*/ */
public function numChildren($cache = true) { public function numChildren($cache = true) {
@ -606,10 +644,10 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Return children from the stage site * Return children in the stage site.
* *
* @param showAll Inlcude all of the elements, even those not shown in the menus. * @param bool $showAll Include all of the elements, even those not shown in the menus. Only applicable when
* (only applicable when extension is applied to {@link SiteTree}). * extension is applied to {@link SiteTree}.
* @return DataList * @return DataList
*/ */
public function stageChildren($showAll = false) { public function stageChildren($showAll = false) {
@ -625,12 +663,13 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Return children from the live site, if it exists. * Return children in the live site, if it exists.
* *
* @param boolean $showAll Include all of the elements, even those not shown in the menus. * @param bool $showAll Include all of the elements, even those not shown in the menus. Only
* (only applicable when extension is applied to {@link SiteTree}). * applicable when extension is applied to {@link SiteTree}.
* @param boolean $onlyDeletedFromStage Only return items that have been deleted from stage * @param bool $onlyDeletedFromStage Only return items that have been deleted from stage
* @return SS_List * @return DataList
* @throws Exception
*/ */
public function liveChildren($showAll = false, $onlyDeletedFromStage = false) { public function liveChildren($showAll = false, $onlyDeletedFromStage = false) {
if(!$this->owner->hasExtension('Versioned')) { if(!$this->owner->hasExtension('Versioned')) {
@ -652,7 +691,10 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Get the parent of this class. * Get this object's parent, optionally filtered by an SQL clause. If the clause doesn't match the parent, nothing
* is returned.
*
* @param string $filter
* @return DataObject * @return DataObject
*/ */
public function getParent($filter = null) { public function getParent($filter = null) {
@ -669,7 +711,7 @@ class Hierarchy extends DataExtension {
/** /**
* Return all the parents of this class in a set ordered from the lowest to highest parent. * Return all the parents of this class in a set ordered from the lowest to highest parent.
* *
* @return SS_List * @return ArrayList
*/ */
public function getAncestors() { public function getAncestors() {
$ancestors = new ArrayList(); $ancestors = new ArrayList();
@ -683,11 +725,10 @@ class Hierarchy extends DataExtension {
} }
/** /**
* Returns a human-readable, flattened representation of the path to the object, * Returns a human-readable, flattened representation of the path to the object, using its {@link Title} attribute.
* using its {@link Title()} attribute.
* *
* @param String * @param string $separator
* @return String * @return string
*/ */
public function getBreadcrumbs($separator = ' &raquo; ') { public function getBreadcrumbs($separator = ' &raquo; ') {
$crumbs = array(); $crumbs = array();
@ -702,22 +743,25 @@ class Hierarchy extends DataExtension {
* then search the parents. * then search the parents.
* *
* @todo Write! * @todo Write!
*
* @param string $className Class name of the node to find
* @param DataObject $afterNode Used for recursive calls to this function
* @return DataObject
*/ */
public function naturalPrev( $className, $afterNode = null ) { public function naturalPrev($className, $afterNode = null ) {
return null; return null;
} }
/** /**
* Get the next node in the tree of the type. If there is no instance of the className descended from this node, * Get the next node in the tree of the type. If there is no instance of the className descended from this node,
* then search the parents. * then search the parents.
* @param string $className Class name of the node to find. * @param string $className Class name of the node to find.
* @param string|int $root ID/ClassName of the node to limit the search to * @param string|int $root ID/ClassName of the node to limit the search to
* @param DataObject afterNode Used for recursive calls to this function * @param DataObject $afterNode Used for recursive calls to this function
* @return DataObject * @return DataObject
*/ */
public function naturalNext( $className = null, $root = 0, $afterNode = null ) { public function naturalNext($className = null, $root = 0, $afterNode = null ) {
// If this node is not the node we are searching from, then we can possibly return this // If this node is not the node we are searching from, then we can possibly return this node as a solution
// node as a solution
if($afterNode && $afterNode->ID != $this->owner->ID) { if($afterNode && $afterNode->ID != $this->owner->ID) {
if(!$className || ($className && $this->owner->class == $className)) { if(!$className || ($className && $this->owner->class == $className)) {
return $this->owner; return $this->owner;
@ -761,6 +805,14 @@ class Hierarchy extends DataExtension {
return null; return null;
} }
/**
* Flush all Hierarchy caches:
* - Children (instance)
* - NumChildren (instance)
* - Marked (global)
* - Expanded (global)
* - TreeOpened (global)
*/
public function flushCache() { public function flushCache() {
$this->_cache_children = null; $this->_cache_children = null;
$this->_cache_numChildren = null; $this->_cache_numChildren = null;
@ -769,6 +821,12 @@ class Hierarchy extends DataExtension {
self::$treeOpened = array(); self::$treeOpened = array();
} }
/**
* Reset global Hierarchy caches:
* - Marked
* - Expanded
* - TreeOpened
*/
public static function reset() { public static function reset() {
self::$marked = array(); self::$marked = array();
self::$expanded = array(); self::$expanded = array();

View File

@ -1,16 +1,24 @@
<?php <?php
/** /**
* The Versioned extension allows your DataObjects to have several versions, * The Versioned extension allows your DataObjects to have several versions, allowing you to rollback changes and view
* allowing you to rollback changes and view history. An example of this is * history. An example of this is the pages used in the CMS.
* the pages used in the CMS.
* *
* @property int $Version * @property int $Version
* *
* @package framework * @package framework
* @subpackage model * @subpackage model
*
* @property DataObject owner
*
* @property int RecordID
* @property int Version
* @property bool WasPublished
* @property int AuthorID
* @property int PublisherID
*/ */
class Versioned extends DataExtension implements TemplateGlobalProvider { class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* An array of possible stages. * An array of possible stages.
* @var array * @var array
@ -35,40 +43,33 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
const DEFAULT_MODE = 'Stage.Live'; const DEFAULT_MODE = 'Stage.Live';
/** /**
* A version that a DataObject should be when it is 'migrating', * A version that a DataObject should be when it is 'migrating', that is, when it is in the process of moving from
* that is, when it is in the process of moving from one stage to another. * one stage to another.
* @var string * @var string
*/ */
public $migratingVersion; public $migratingVersion;
/** /**
* A cache used by get_versionnumber_by_stage(). * A cache used by get_versionnumber_by_stage(). Clear through {@link flushCache()}.
* Clear through {@link flushCache()}.
*
* @var array * @var array
*/ */
protected static $cache_versionnumber; protected static $cache_versionnumber;
/** /** @var string */
* @var string
*/
protected static $reading_mode = null; protected static $reading_mode = null;
/** /**
* @var Boolean Flag which is temporarily changed during the write() process * Flag which is temporarily changed during the write() process to influence augmentWrite() behaviour. If set to
* to influence augmentWrite() behaviour. If set to TRUE, no new version will be created * true, no new version will be created for the following write. Needs to be public as other classes introspect this
* for the following write. Needs to be public as other classes introspect this state * state during the write process in order to adapt to this versioning behaviour.
* during the write process in order to adapt to this versioning behaviour. * @var bool
*/ */
public $_nextWriteWithoutVersion = false; public $_nextWriteWithoutVersion = false;
/** /**
* Additional database columns for the new * Additional database columns for the new "_versions" table. Used in {@link augmentDatabase()} and all Versioned
* "_versions" table. Used in {@link augmentDatabase()} * calls extending or creating SELECT statements.
* and all Versioned calls extending or creating * @var array
* SELECT statements.
*
* @var array $db_for_versions_table
*/ */
private static $db_for_versions_table = array( private static $db_for_versions_table = array(
"RecordID" => "Int", "RecordID" => "Int",
@ -78,33 +79,25 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
"PublisherID" => "Int" "PublisherID" => "Int"
); );
/**
* @var array
*/
private static $db = array( private static $db = array(
'Version' => 'Int' 'Version' => 'Int'
); );
/** /**
* Used to enable or disable the prepopulation of the version number cache. * Used to enable or disable the prepopulation of the version number cache. Defaults to true.
* Defaults to true. * @var bool
* */
* @var boolean
*/
private static $prepopulate_versionnumber_cache = true; private static $prepopulate_versionnumber_cache = true;
/** /**
* Keep track of the archive tables that have been created. * Keep track of the archive tables that have been created.
*
* @var array * @var array
*/ */
private static $archive_tables = array(); private static $archive_tables = array();
/** /**
* Additional database indexes for the new * Additional database indexes for the new "_versions" table. Used in {@link augmentDatabase()}.
* "_versions" table. Used in {@link augmentDatabase()}. * @var array
*
* @var array $indexes_for_versions_table
*/ */
private static $indexes_for_versions_table = array( private static $indexes_for_versions_table = array(
'RecordID_Version' => '("RecordID","Version")', 'RecordID_Version' => '("RecordID","Version")',
@ -116,10 +109,9 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* An array of DataObject extensions that may require versioning for extra tables * An array of DataObject extensions that may require versioning for extra tables. The array value is a set of
* The array value is a set of suffixes to form these table names, assuming a preceding '_'. * suffixes to form these table names, assuming a preceding '_'. E.g. if Extension1 creates a new table
* E.g. if Extension1 creates a new table 'Class_suffix1' * 'Class_suffix1' and Extension2 the tables 'Class_suffix2' and 'Class_suffix3':
* and Extension2 the tables 'Class_suffix2' and 'Class_suffix3':
* *
* $versionableExtensions = array( * $versionableExtensions = array(
* 'Extension1' => 'suffix1', * 'Extension1' => 'suffix1',
@ -142,9 +134,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
* Config::inst()->update($this->owner->class, 'versionableExtensions', * Config::inst()->update($this->owner->class, 'versionableExtensions',
* array('Extension1' => 'suffix1', 'Extension2' => array('suffix2', 'suffix3'))); * array('Extension1' => 'suffix1', 'Extension2' => array('suffix2', 'suffix3')));
* *
* * Make sure your extension has a static $enabled-property that determines if it is processed by Versioned.
* Make sure your extension has a static $enabled-property that determines if it is
* processed by Versioned.
* *
* @var array * @var array
*/ */
@ -170,9 +160,8 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* Construct a new Versioned object. * Construct a new Versioned object.
* *
* @var array $stages The different stages the versioned object can be. * @param array $stages The different stages the versioned object can be. The first stage is considered the
* The first stage is considered the 'default' stage, the last stage is * 'default' stage, the last stage is considered the 'live' stage.
* considered the 'live' stage.
*/ */
public function __construct($stages = array('Stage','Live')) { public function __construct($stages = array('Stage','Live')) {
parent::__construct(); parent::__construct();
@ -187,11 +176,10 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* Amend freshly created DataQuery objects with versioned-specific * Amend freshly created DataQuery objects with versioned-specific information.
* information.
* *
* @param SQLQuery * @param SQLQuery $query
* @param DataQuery * @param DataQuery $dataQuery
*/ */
public function augmentDataQueryCreation(SQLQuery &$query, DataQuery &$dataQuery) { public function augmentDataQueryCreation(SQLQuery &$query, DataQuery &$dataQuery) {
$parts = explode('.', Versioned::get_reading_mode()); $parts = explode('.', Versioned::get_reading_mode());
@ -206,12 +194,14 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
$dataQuery->setQueryParam('Versioned.mode', 'stage'); $dataQuery->setQueryParam('Versioned.mode', 'stage');
$dataQuery->setQueryParam('Versioned.stage', $parts[1]); $dataQuery->setQueryParam('Versioned.stage', $parts[1]);
} }
} }
/** /**
* Augment the the SQLQuery that is created by the DataQuery * Augment the the SQLQuery that is created by the DataQuery.
* @todo Should this all go into VersionedDataQuery? * @todo Should this all go into VersionedDataQuery?
*
* @param SQLQuery $query
* @param DataQuery $dataQuery
*/ */
public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) { public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) {
if(!$dataQuery || !$dataQuery->getQueryParam('Versioned.mode')) { if(!$dataQuery || !$dataQuery->getQueryParam('Versioned.mode')) {
@ -328,9 +318,8 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
$query->setOrderBy($orders); $query->setOrderBy($orders);
// latest_version has one more step // latest_version has one more step. Return latest version instances, regardless of whether they are on a
// Return latest version instances, regardless of whether they are on a particular stage // particular stage. This provides "show all, including deleted" functonality
// This provides "show all, including deleted" functonality
if($dataQuery->getQueryParam('Versioned.mode') == 'latest_versions') { if($dataQuery->getQueryParam('Versioned.mode') == 'latest_versions') {
$query->addWhere( $query->addWhere(
"\"{$alias}_versions\".\"Version\" IN "\"{$alias}_versions\".\"Version\" IN
@ -355,17 +344,16 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* For lazy loaded fields requiring extra sql manipulation, ie versioning. * For lazy loaded fields requiring extra SQL manipulation, ie versioning.
* *
* @param SQLQuery $query * @param SQLQuery $query
* @param DataQuery $dataQuery * @param DataQuery $dataQuery
* @param DataObject $dataObject * @param DataObject $dataObject
*/ */
public function augmentLoadLazyFields(SQLQuery &$query, DataQuery &$dataQuery = null, $dataObject) { public function augmentLoadLazyFields(SQLQuery &$query, DataQuery &$dataQuery = null, $dataObject) {
// The VersionedMode local variable ensures that this decorator only applies to // The VersionedMode local variable ensures that this decorator only applies to queries that have originated
// queries that have originated from the Versioned object, and have the Versioned // from the Versioned object, and have the Versioned metadata set on the query object. This prevents regular
// metadata set on the query object. This prevents regular queries from // queries from accidentally querying the *_versions tables.
// accidentally querying the *_versions tables.
$versionedMode = $dataObject->getSourceQueryParam('Versioned.mode'); $versionedMode = $dataObject->getSourceQueryParam('Versioned.mode');
$dataClass = $dataQuery->dataClass(); $dataClass = $dataQuery->dataClass();
$modesToAllowVersioning = array('all_versions', 'latest_versions', 'archive'); $modesToAllowVersioning = array('all_versions', 'latest_versions', 'archive');
@ -382,7 +370,6 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
} }
/** /**
* Called by {@link SapphireTest} when the database is reset. * Called by {@link SapphireTest} when the database is reset.
* *
@ -498,8 +485,8 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
if(DB::get_schema()->hasTable("{$table}_versions")) { if(DB::get_schema()->hasTable("{$table}_versions")) {
// Fix data that lacks the uniqueness constraint (since this was added later and // Fix data that lacks the uniqueness constraint (since this was added later and bugs meant that
// bugs meant that the constraint was validated) // the constraint was validated)
$duplications = DB::query("SELECT MIN(\"ID\") AS \"ID\", \"RecordID\", \"Version\" $duplications = DB::query("SELECT MIN(\"ID\") AS \"ID\", \"RecordID\", \"Version\"
FROM \"{$table}_versions\" GROUP BY \"RecordID\", \"Version\" FROM \"{$table}_versions\" GROUP BY \"RecordID\", \"Version\"
HAVING COUNT(*) > 1"); HAVING COUNT(*) > 1");
@ -514,8 +501,8 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
); );
} }
// Remove junk which has no data in parent classes. Only needs to run the following // Remove junk which has no data in parent classes. Only needs to run the following when versioned
// when versioned data is spread over multiple tables // data is spread over multiple tables
if(!$isRootClass && ($versionedTables = ClassInfo::dataClassesFor($table))) { if(!$isRootClass && ($versionedTables = ClassInfo::dataClassesFor($table))) {
foreach($versionedTables as $child) { foreach($versionedTables as $child) {
@ -595,9 +582,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* Generates a ($table)_version DB manipulation and injects it into the current $manipulation * Generates a ($table)_version DB manipulation and injects it into the current $manipulation
* *
* @param array $manipulation Source manipulation data * @param SQLQuery $manipulation The query to augment
* @param string $table Name of table
* @param int $recordID ID of record to version
*/ */
protected function augmentWriteVersioned(&$manipulation, $table, $recordID) { protected function augmentWriteVersioned(&$manipulation, $table, $recordID) {
$baseDataClass = ClassInfo::baseDataClass($table); $baseDataClass = ClassInfo::baseDataClass($table);
@ -740,8 +725,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
$this->migrateVersion(null); $this->migrateVersion(null);
} }
// Add the new version # back into the data object, for accessing // Add the new version # back into the data object, for accessing after this write
// after this write
if(isset($thisVersion)) { if(isset($thisVersion)) {
$this->owner->Version = str_replace("'","", $thisVersion); $this->owner->Version = str_replace("'","", $thisVersion);
} }
@ -749,9 +733,8 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* Perform a write without affecting the version table. * Perform a write without affecting the version table.
* On objects without versioning.
* *
* @return int The ID of the record * @return int The ID of the written record
*/ */
public function writeWithoutVersion() { public function writeWithoutVersion() {
$this->_nextWriteWithoutVersion = true; $this->_nextWriteWithoutVersion = true;
@ -759,18 +742,13 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
return $this->owner->write(); return $this->owner->write();
} }
/**
*
*/
public function onAfterWrite() { public function onAfterWrite() {
$this->_nextWriteWithoutVersion = false; $this->_nextWriteWithoutVersion = false;
} }
/** /**
* If a write was skipped, then we need to ensure that we don't leave a * If a write was skipped, then we need to ensure that we don't leave a migrateVersion() value lying around for the
* migrateVersion() value lying around for the next write. * next write.
*
*
*/ */
public function onAfterSkippedWrite() { public function onAfterSkippedWrite() {
$this->migrateVersion(null); $this->migrateVersion(null);
@ -867,11 +845,10 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* Determine if a table is supporting the Versioned extensions (e.g. * Determine if a table supports the Versioned extensions (e.g. $table_versions does exists).
* $table_versions does exists).
* *
* @param string $table Table name * @param string $table Table name
* @return boolean * @return bool
*/ */
public function canBeVersioned($table) { public function canBeVersioned($table) {
return ClassInfo::exists($table) return ClassInfo::exists($table)
@ -883,8 +860,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
* Check if a certain table has the 'Version' field. * Check if a certain table has the 'Version' field.
* *
* @param string $table Table name * @param string $table Table name
* * @return bool
* @return boolean Returns false if the field isn't in the table, true otherwise
*/ */
public function hasVersionField($table) { public function hasVersionField($table) {
$rPos = strrpos($table,'_'); $rPos = strrpos($table,'_');
@ -900,7 +876,6 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* @param string $table * @param string $table
*
* @return string * @return string
*/ */
public function extendWithSuffix($table) { public function extendWithSuffix($table) {
@ -921,7 +896,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* Get the latest published DataObject. * Get the latest published version of this object.
* *
* @return DataObject * @return DataObject
*/ */
@ -942,10 +917,10 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* Move a database record from one stage to the other. * Move a database record from one stage to the other.
* *
* @param int|string $fromStage Place to copy from. Can be either a stage name or a version number. * @param string $fromStage Place to copy from. Can be either a stage name or a version number.
* @param string $toStage Place to copy to. Must be a stage name. * @param string $toStage Place to copy to. Must be a stage name.
* @param bool $createNewVersion Set this to true to create a new version number. * @param bool $createNewVersion Set this to true to create a new version number. By default, the existing
* By default, the existing version number will be copied over. * version number will be copied over.
*/ */
public function publish($fromStage, $toStage, $createNewVersion = false) { public function publish($fromStage, $toStage, $createNewVersion = false) {
$this->owner->extend('onBeforeVersionedPublish', $fromStage, $toStage, $createNewVersion); $this->owner->extend('onBeforeVersionedPublish', $fromStage, $toStage, $createNewVersion);
@ -1005,19 +980,18 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* Set the migrating version. * Set the migrating version.
* *
* @param string $version The version. * @param string $version
*/ */
public function migrateVersion($version) { public function migrateVersion($version) {
$this->migratingVersion = $version; $this->migratingVersion = $version;
} }
/** /**
* Compare two stages to see if they're different. * Compare two stages to see if they're different. Only checks the version numbers, not the actual content.
* *
* Only checks the version numbers, not the actual content. * @param string $stage1 The first stage to check
* * @param string $stage2 The second stage to check
* @param string $stage1 The first stage to check. * @return bool
* @param string $stage2
*/ */
public function stagesDiffer($stage1, $stage2) { public function stagesDiffer($stage1, $stage2) {
$table1 = $this->baseTable($stage1); $table1 = $this->baseTable($stage1);
@ -1027,8 +1001,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
return true; return true;
} }
// We test for equality - if one of the versions doesn't exist, this // We test for equality - if one of the versions doesn't exist, this will be false.
// will be false.
// TODO: DB Abstraction: if statement here: // TODO: DB Abstraction: if statement here:
$stagesAreEqual = DB::prepared_query( $stagesAreEqual = DB::prepared_query(
@ -1042,24 +1015,28 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* Get a list of versions for this object, optionally with additional SQL parameters
*
* @param string $filter * @param string $filter
* @param string $sort * @param string $sort
* @param string $limit * @param string $limit
* @param string $join Deprecated, use leftJoin($table, $joinClause) instead * @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param string $having * @param string $having
* @return DataList
*/ */
public function Versions($filter = "", $sort = "", $limit = "", $join = "", $having = "") { public function Versions($filter = "", $sort = "", $limit = "", $join = "", $having = "") {
return $this->allVersions($filter, $sort, $limit, $join, $having); return $this->allVersions($filter, $sort, $limit, $join, $having);
} }
/** /**
* Return a list of all the versions available. * Get a list of versions for this object, optionally with additional SQL parameters
* *
* @param string $filter * @param string $filter
* @param string $sort * @param string $sort
* @param string $limit * @param string $limit
* @param string $join Deprecated, use leftJoin($table, $joinClause) instead * @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param string $having * @param string $having
* @return DataList
*/ */
public function allVersions($filter = "", $sort = "", $limit = "", $join = "", $having = "") { public function allVersions($filter = "", $sort = "", $limit = "", $join = "", $having = "") {
// Make sure the table names are not postfixed (e.g. _Live) // Make sure the table names are not postfixed (e.g. _Live)
@ -1106,11 +1083,10 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* Compare two version, and return the diff between them. * Compare two version, and return the differences between them.
*
* @param string $from The version to compare from.
* @param string $to The version to compare to.
* *
* @param string $from The version to compare from
* @param string $to The version to compare to
* @return DataObject * @return DataObject
*/ */
public function compareVersions($from, $to) { public function compareVersions($from, $to) {
@ -1125,6 +1101,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* Return the base table - the class that directly extends DataObject. * Return the base table - the class that directly extends DataObject.
* *
* @param string $stage Override the stage used
* @return string * @return string
*/ */
public function baseTable($stage = null) { public function baseTable($stage = null) {
@ -1162,16 +1139,12 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* Choose the stage the site is currently on. * Choose the stage the site is currently on:
* - If $_GET['stage'] is set, then it will use that stage, and store it in the session.
* - If $_GET['archiveDate'] is set, it will use that date, and store it in the session.
* - If neither of these are set, it checks the session, otherwise the stage is set to 'Live'.
* *
* If $_GET['stage'] is set, then it will use that stage, and store it in * @param Session $session Optional session within which to store the resulting stage
* the session.
*
* if $_GET['archiveDate'] is set, it will use that date, and store it in
* the session.
*
* If neither of these are set, it checks the session, otherwise the stage
* is set to 'Live'.
*/ */
public static function choose_site_stage() { public static function choose_site_stage() {
// Check any pre-existing session mode // Check any pre-existing session mode
@ -1268,7 +1241,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* Set the reading stage. * Set the reading stage.
* *
* @param string $stage New reading stage. * @param string $stage
*/ */
public static function reading_stage($stage) { public static function reading_stage($stage) {
Versioned::set_reading_mode('Stage.' . $stage); Versioned::set_reading_mode('Stage.' . $stage);
@ -1277,21 +1250,20 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* Set the reading archive date. * Set the reading archive date.
* *
* @param string $date New reading archived date. * @param string $date
*/ */
public static function reading_archived_date($date) { public static function reading_archived_date($date) {
Versioned::set_reading_mode('Archive.' . $date); Versioned::set_reading_mode('Archive.' . $date);
} }
/** /**
* Get a singleton instance of a class in the given stage. * Get a singleton instance of a class in the given stage.
* *
* @param string $class The name of the class. * @param string $class The name of the class
* @param string $stage The name of the stage. * @param string $stage The name of the stage
* @param string $filter A filter to be inserted into the WHERE clause. * @param string $filter A filter to be inserted into the WHERE clause
* @param boolean $cache Use caching. * @param bool $cache Whether to load from the cache instead of fresh from the database
* @param string $orderby A sort expression to be inserted into the ORDER BY clause. * @param string $sort A sort expression to be inserted into the ORDER BY clause.
* *
* @return DataObject * @return DataObject
*/ */
@ -1305,10 +1277,10 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* Gets the current version number of a specific record. * Gets the current version number of a specific record.
* *
* @param string $class * @param string $class The classname of the desired object
* @param string $stage * @param string $stage The name of the stage to load from
* @param int $id * @param int $id The object's ID
* @param boolean $cache * @param bool $cache Whether to load from the cache instead of fresh from the database
* *
* @return int * @return int
*/ */
@ -1321,11 +1293,8 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
return 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) // get version as performance-optimized SQL query (gets called for each object of this class in the database)
$version = DB::prepared_query( $version = DB::query("SELECT \"Version\" FROM \"$stageTable\" WHERE \"ID\" = $id")->value();
"SELECT \"Version\" FROM \"$stageTable\" WHERE \"ID\" = ?",
array($id)
)->value();
// cache value (if required) // cache value (if required)
if($cache) { if($cache) {
@ -1344,13 +1313,12 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* Pre-populate the cache for Versioned::get_versionnumber_by_stage() for * Prepopulate the cache for Versioned::get_versionnumber_by_stage() for a list of record IDs, for more efficient
* a list of record IDs, for more efficient database querying. If $idList * database querying. If $idList is null, then every object will be pre-cached.
* is null, then every page will be pre-cached.
* *
* @param string $class * @param string $class The object class to prepopulate version numbers for
* @param string $stage * @param string $stage The stage to prepopulate version numbers from
* @param array $idList * @param array $idList A whitelist of IDs to use when prepopulating
*/ */
public static function prepopulate_versionnumber_cache($class, $stage, $idList = null) { public static function prepopulate_versionnumber_cache($class, $stage, $idList = null) {
if (!Config::inst()->get('Versioned', 'prepopulate_versionnumber_cache')) { if (!Config::inst()->get('Versioned', 'prepopulate_versionnumber_cache')) {
@ -1383,13 +1351,13 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* Get a set of class instances by the given stage. * Get a set of class instances by the given stage.
* *
* @param string $class The name of the class. * @param string $class The name of the class.
* @param string $stage The name of the stage. * @param string $stage The name of the stage.
* @param string $filter A filter to be inserted into the WHERE clause. * @param string $filter A filter to be inserted into the WHERE clause.
* @param string $sort A sort expression to be inserted into the ORDER BY clause. * @param string $sort A sort expression to be inserted into the ORDER BY clause.
* @param string $join Deprecated, use leftJoin($table, $joinClause) instead * @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param int $limit A limit on the number of records returned from the database. * @param string|int $limit A limit on the number of records returned from the database.
* @param string $containerClass The container class for the result set (default is DataList) * @param string $containerClass The container class for the result set (default is DataList)
* *
* @return DataList A modified DataList designated to the specified stage * @return DataList A modified DataList designated to the specified stage
*/ */
@ -1404,9 +1372,9 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* @param string $stage * Delete this item from the specified stage.
* *
* @return int * @param string $stage
*/ */
public function deleteFromStage($stage) { public function deleteFromStage($stage) {
$oldMode = Versioned::get_reading_mode(); $oldMode = Versioned::get_reading_mode();
@ -1423,8 +1391,11 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* @param string $stage * Write this item to the specified stage.
* @param boolean $forceInsert *
* @param string $stage The stage to write this item to
* @param bool $forceInsert Whether to force an INSERT query over an UPDATE query
* @return int The ID of the item being written
*/ */
public function writeToStage($stage, $forceInsert = false) { public function writeToStage($stage, $forceInsert = false) {
$oldMode = Versioned::get_reading_mode(); $oldMode = Versioned::get_reading_mode();
@ -1438,10 +1409,11 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* Roll the draft version of this page to match the published page. * Roll the draft version of this object to match the published one.
*
* Caution: Doesn't overwrite the object properties with the rolled back version. * Caution: Doesn't overwrite the object properties with the rolled back version.
* *
* @param int $version Either the string 'Live' or a version number * @param string|int $version Either the string 'Live' or a version number
*/ */
public function doRollbackTo($version) { public function doRollbackTo($version) {
$this->owner->extend('onBeforeRollback', $version); $this->owner->extend('onBeforeRollback', $version);
@ -1453,8 +1425,10 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* Return the latest version of the given page. * Return the latest version of the given object.
* *
* @param string $class The classname of the object to lookup
* @param string $id The object of the ID to retrieve
* @return DataObject * @return DataObject
*/ */
public static function get_latest_version($class, $id) { public static function get_latest_version($class, $id) {
@ -1474,7 +1448,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
* @see get_latest_version() * @see get_latest_version()
* @see latestPublished * @see latestPublished
* *
* @return boolean * @return bool
*/ */
public function isLatestVersion() { public function isLatestVersion() {
$version = self::get_latest_version($this->owner->class, $this->owner->ID); $version = self::get_latest_version($this->owner->class, $this->owner->ID);
@ -1486,14 +1460,12 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* Return the equivalent of a DataList::create() call, querying the latest * Return the equivalent of a DataList::create() call, querying the latest version of each object stored in the
* version of each page stored in the (class)_versions tables. * (class)_versions tables. In particular, this will query deleted records as well as active ones.
* *
* In particular, this will query deleted records as well as active ones. * @param string $class The type of object to lookup
* * @param string $filter An optional SQL comparison to add to the WHERE clause
* @param string $class * @param string $sort An optional SQL statement to add to the SORT clause
* @param string $filter
* @param string $sort
*/ */
public static function get_including_deleted($class, $filter = "", $sort = "") { public static function get_including_deleted($class, $filter = "", $sort = "") {
$list = DataList::create($class) $list = DataList::create($class)
@ -1505,16 +1477,14 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
} }
/** /**
* Return the specific version of the given id. * Return the specific version of the given ID.
* *
* Caution: The record is retrieved as a DataObject, but saving back * Caution: The record is retrieved as a DataObject, but saving back modifications via write() will create a new
* modifications via write() will create a new version, rather than * version, rather than modifying the existing one.
* modifying the existing one.
*
* @param string $class
* @param int $id
* @param int $version
* *
* @param string $class The type of object to lookup
* @param int $id The ID of the object to retrieve
* @param int $version The desired version of the object
* @return DataObject * @return DataObject
*/ */
public static function get_version($class, $id, $version) { public static function get_version($class, $id, $version) {
@ -1530,8 +1500,8 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
/** /**
* Return a list of all versions for a given id. * Return a list of all versions for a given id.
* *
* @param string $class * @param string $class The type of object to lookup
* @param int $id * @param int $id The ID of the object to retrieve
* *
* @return DataList * @return DataList
*/ */
@ -1551,9 +1521,6 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
$labels['Versions'] = _t('Versioned.has_many_Versions', 'Versions', 'Past Versions of this page'); $labels['Versions'] = _t('Versioned.has_many_Versions', 'Versions', 'Past Versions of this page');
} }
/**
* @param FieldList
*/
public function updateCMSFields(FieldList $fields) { public function updateCMSFields(FieldList $fields) {
// remove the version field from the CMS as this should be left // remove the version field from the CMS as this should be left
// entirely up to the extension (not the cms user). // entirely up to the extension (not the cms user).
@ -1570,12 +1537,15 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
$this->owner->Version = 0; $this->owner->Version = 0;
} }
/**
* Clear the cached version numbers from previous queries.
*/
public function flushCache() { public function flushCache() {
self::$cache_versionnumber = array(); self::$cache_versionnumber = array();
} }
/** /**
* Return a piece of text to keep DataObject cache keys appropriately specific. * Returns a piece of text to keep DataObject cache keys appropriately specific.
* *
* @return string * @return string
*/ */
@ -1615,14 +1585,11 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
* @see Versioned * @see Versioned
*/ */
class Versioned_Version extends ViewableData { class Versioned_Version extends ViewableData {
/**
* @var array /** @var array */
*/
protected $record; protected $record;
/** /** @var DataObject */
* @var DataObject
*/
protected $object; protected $object;
public function __construct($record) { public function __construct($record) {
@ -1637,6 +1604,8 @@ class Versioned_Version extends ViewableData {
} }
/** /**
* Get a CSS classname to use representing whether this version was published or not.
*
* @return string * @return string
*/ */
public function PublishedClass() { public function PublishedClass() {
@ -1644,6 +1613,8 @@ class Versioned_Version extends ViewableData {
} }
/** /**
* Gets this version's author (the person who saved to Stage).
*
* @return Member * @return Member
*/ */
public function Author() { public function Author() {
@ -1651,6 +1622,8 @@ class Versioned_Version extends ViewableData {
} }
/** /**
* Get this version's publisher.
*
* @return Member * @return Member
*/ */
public function Publisher() { public function Publisher() {
@ -1662,7 +1635,9 @@ class Versioned_Version extends ViewableData {
} }
/** /**
* @return boolean * Determines if this version was ever published.
*
* @return bool
*/ */
public function Published() { public function Published() {
return !empty($this->record['WasPublished']); return !empty($this->record['WasPublished']);
@ -1670,6 +1645,9 @@ class Versioned_Version extends ViewableData {
/** /**
* Copied from DataObject to allow access via dot notation. * Copied from DataObject to allow access via dot notation.
*
* @param string $fieldName
* @return mixed
*/ */
public function relField($fieldName) { public function relField($fieldName) {
$component = $this; $component = $this;