mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #3309 from stojg/issue/cms-1049
Delete parent page, child pages cannot be found anymore
This commit is contained in:
commit
0957f2735a
@ -799,7 +799,12 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
|||||||
? $filter->getChildrenMethod()
|
? $filter->getChildrenMethod()
|
||||||
: 'AllChildrenIncludingDeleted';
|
: 'AllChildrenIncludingDeleted';
|
||||||
|
|
||||||
if(!$numChildrenMethod) $numChildrenMethod = 'numChildren';
|
if(!$numChildrenMethod) {
|
||||||
|
$numChildrenMethod = 'numChildren';
|
||||||
|
if($filter && $filter->getNumChildrenMethod()) {
|
||||||
|
$numChildrenMethod = $filter->getNumChildrenMethod();
|
||||||
|
}
|
||||||
|
}
|
||||||
if(!$filterFunction) $filterFunction = ($filter) ? array($filter, 'isPageIncluded') : null;
|
if(!$filterFunction) $filterFunction = ($filter) ? array($filter, 'isPageIncluded') : null;
|
||||||
|
|
||||||
// Get the tree root
|
// Get the tree root
|
||||||
@ -812,7 +817,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
|||||||
$obj->markPartialTree($nodeCountThreshold, $this, $childrenMethod, $numChildrenMethod);
|
$obj->markPartialTree($nodeCountThreshold, $this, $childrenMethod, $numChildrenMethod);
|
||||||
|
|
||||||
// Ensure current page is exposed
|
// Ensure current page is exposed
|
||||||
if($p = $this->currentPage()) $obj->markToExpose($p);
|
// This call flushes the Hierarchy::$marked cache when the current node is deleted
|
||||||
|
// @see CMSMain::getRecord()
|
||||||
|
// This will make it impossible to show children under a deleted parent page
|
||||||
|
// if($p = $this->currentPage()) $obj->markToExpose($p);
|
||||||
|
|
||||||
// NOTE: SiteTree/CMSMain coupling :-(
|
// NOTE: SiteTree/CMSMain coupling :-(
|
||||||
if(class_exists('SiteTree')) {
|
if(class_exists('SiteTree')) {
|
||||||
@ -823,9 +831,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
|||||||
// getChildrenAsUL is a flexible and complex way of traversing the tree
|
// getChildrenAsUL is a flexible and complex way of traversing the tree
|
||||||
$controller = $this;
|
$controller = $this;
|
||||||
$recordController = ($this->stat('tree_class') == 'SiteTree') ? singleton('CMSPageEditController') : $this;
|
$recordController = ($this->stat('tree_class') == 'SiteTree') ? singleton('CMSPageEditController') : $this;
|
||||||
$titleFn = function(&$child) use(&$controller, &$recordController) {
|
$titleFn = function(&$child, $numChildrenMethod) use(&$controller, &$recordController) {
|
||||||
$link = Controller::join_links($recordController->Link("show"), $child->ID);
|
$link = Controller::join_links($recordController->Link("show"), $child->ID);
|
||||||
return LeftAndMain_TreeNode::create($child, $link, $controller->isCurrentPage($child))->forTemplate();
|
$node = LeftAndMain_TreeNode::create($child, $link, $controller->isCurrentPage($child), $numChildrenMethod);
|
||||||
|
return $node->forTemplate();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Limit the amount of nodes shown for performance reasons.
|
// Limit the amount of nodes shown for performance reasons.
|
||||||
@ -1866,10 +1875,22 @@ class LeftAndMain_TreeNode extends ViewableData {
|
|||||||
*/
|
*/
|
||||||
protected $isCurrent;
|
protected $isCurrent;
|
||||||
|
|
||||||
public function __construct($obj, $link = null, $isCurrent = false) {
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $numChildrenMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $obj
|
||||||
|
* @param null $link
|
||||||
|
* @param bool $isCurrent
|
||||||
|
* @param $numChildrenMethod
|
||||||
|
*/
|
||||||
|
public function __construct($obj, $link = null, $isCurrent = false, $numChildrenMethod='numChildren') {
|
||||||
$this->obj = $obj;
|
$this->obj = $obj;
|
||||||
$this->link = $link;
|
$this->link = $link;
|
||||||
$this->isCurrent = $isCurrent;
|
$this->isCurrent = $isCurrent;
|
||||||
|
$this->numChildrenMethod = $numChildrenMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1890,7 +1911,7 @@ class LeftAndMain_TreeNode extends ViewableData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getClasses() {
|
public function getClasses() {
|
||||||
$classes = $this->obj->CMSTreeClasses();
|
$classes = $this->obj->CMSTreeClasses($this->numChildrenMethod);
|
||||||
if($this->isCurrent) $classes .= " current";
|
if($this->isCurrent) $classes .= " current";
|
||||||
$flags = $this->obj->hasMethod('getStatusFlags') ? $this->obj->getStatusFlags() : false;
|
$flags = $this->obj->hasMethod('getStatusFlags') ? $this->obj->getStatusFlags() : false;
|
||||||
if ($flags) {
|
if ($flags) {
|
||||||
|
@ -115,7 +115,8 @@ class Hierarchy extends DataExtension {
|
|||||||
if($limitToMarked && $rootCall) {
|
if($limitToMarked && $rootCall) {
|
||||||
$this->markingFinished($numChildrenMethod);
|
$this->markingFinished($numChildrenMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if($nodeCountCallback) {
|
if($nodeCountCallback) {
|
||||||
$nodeCountWarning = $nodeCountCallback($this->owner, $this->owner->$numChildrenMethod());
|
$nodeCountWarning = $nodeCountCallback($this->owner, $this->owner->$numChildrenMethod());
|
||||||
if($nodeCountWarning) return $nodeCountWarning;
|
if($nodeCountWarning) return $nodeCountWarning;
|
||||||
@ -130,6 +131,7 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($children) {
|
if($children) {
|
||||||
|
|
||||||
if($attributes) {
|
if($attributes) {
|
||||||
$attributes = " $attributes";
|
$attributes = " $attributes";
|
||||||
}
|
}
|
||||||
@ -139,10 +141,15 @@ class Hierarchy extends DataExtension {
|
|||||||
foreach($children as $child) {
|
foreach($children as $child) {
|
||||||
if(!$limitToMarked || $child->isMarked()) {
|
if(!$limitToMarked || $child->isMarked()) {
|
||||||
$foundAChild = true;
|
$foundAChild = true;
|
||||||
$output .= (is_callable($titleEval)) ? $titleEval($child) : eval("return $titleEval;");
|
if(is_callable($titleEval)) {
|
||||||
|
$output .= $titleEval($child, $numChildrenMethod);
|
||||||
|
} else {
|
||||||
|
$output .= eval("return $titleEval;");
|
||||||
|
}
|
||||||
$output .= "\n";
|
$output .= "\n";
|
||||||
|
|
||||||
$numChildren = $child->$numChildrenMethod();
|
$numChildren = $child->$numChildrenMethod();
|
||||||
|
|
||||||
if(
|
if(
|
||||||
// Always traverse into opened nodes (they might be exposed as parents of search results)
|
// Always traverse into opened nodes (they might be exposed as parents of search results)
|
||||||
$child->isExpanded()
|
$child->isExpanded()
|
||||||
@ -158,7 +165,7 @@ class Hierarchy extends DataExtension {
|
|||||||
} else {
|
} else {
|
||||||
$output .= $child->getChildrenAsUL("", $titleEval, $extraArg, $limitToMarked,
|
$output .= $child->getChildrenAsUL("", $titleEval, $extraArg, $limitToMarked,
|
||||||
$childrenMethod, $numChildrenMethod, false, $nodeCountThreshold);
|
$childrenMethod, $numChildrenMethod, false, $nodeCountThreshold);
|
||||||
}
|
}
|
||||||
} elseif($child->isTreeOpened()) {
|
} elseif($child->isTreeOpened()) {
|
||||||
// Since we're not loading children, don't mark it as open either
|
// Since we're not loading children, don't mark it as open either
|
||||||
$child->markClosed();
|
$child->markClosed();
|
||||||
@ -256,7 +263,7 @@ class Hierarchy extends DataExtension {
|
|||||||
return call_user_func($func, $node);
|
return call_user_func($func, $node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@ -276,11 +283,6 @@ class Hierarchy extends DataExtension {
|
|||||||
foreach($children as $child) {
|
foreach($children as $child) {
|
||||||
$markingMatches = $this->markingFilterMatches($child);
|
$markingMatches = $this->markingFilterMatches($child);
|
||||||
if($markingMatches) {
|
if($markingMatches) {
|
||||||
// Filtered results should always show opened, since actual matches
|
|
||||||
// might be hidden by non-matching parent nodes.
|
|
||||||
if($this->markingFilter) {
|
|
||||||
$child->markOpened();
|
|
||||||
}
|
|
||||||
if($child->$numChildrenMethod()) {
|
if($child->$numChildrenMethod()) {
|
||||||
$child->markUnexpanded();
|
$child->markUnexpanded();
|
||||||
} else {
|
} else {
|
||||||
@ -315,14 +317,14 @@ class Hierarchy extends DataExtension {
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function markingClasses() {
|
public function markingClasses($numChildrenMethod="numChildren") {
|
||||||
$classes = '';
|
$classes = '';
|
||||||
if(!$this->isExpanded()) {
|
if(!$this->isExpanded()) {
|
||||||
$classes .= " unexpanded";
|
$classes .= " unexpanded";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set jstree open state, or mark it as a leaf (closed) if there are no children
|
// Set jstree open state, or mark it as a leaf (closed) if there are no children
|
||||||
if(!$this->numChildren()) {
|
if(!$this->$numChildrenMethod()) {
|
||||||
$classes .= " jstree-leaf closed";
|
$classes .= " jstree-leaf closed";
|
||||||
} elseif($this->isTreeOpened()) {
|
} elseif($this->isTreeOpened()) {
|
||||||
$classes .= " jstree-open";
|
$classes .= " jstree-open";
|
||||||
|
@ -391,6 +391,97 @@ class HierarchyTest extends SapphireTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test checks that deleted ('archived') child pages don't set a css class on the parent
|
||||||
|
* node that makes it look like it has children
|
||||||
|
*/
|
||||||
|
public function testGetChildrenAsULNodeDeletedOnLive() {
|
||||||
|
$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
|
||||||
|
$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
|
||||||
|
$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
|
||||||
|
$obj2ab = $this->objFromFixture('HierarchyTest_Object', 'obj2b');
|
||||||
|
|
||||||
|
// delete all children under obj2
|
||||||
|
$obj2a->delete();
|
||||||
|
$obj2aa->delete();
|
||||||
|
$obj2ab->delete();
|
||||||
|
// Don't pre-load all children
|
||||||
|
$nodeCountThreshold = 1;
|
||||||
|
|
||||||
|
$childrenMethod = 'AllChildren';
|
||||||
|
$numChildrenMethod = 'numChildren';
|
||||||
|
|
||||||
|
$root = new HierarchyTest_Object();
|
||||||
|
$root->markPartialTree($nodeCountThreshold, null, $childrenMethod, $numChildrenMethod);
|
||||||
|
|
||||||
|
// As in LeftAndMain::getSiteTreeFor() but simpler and more to the point for testing purposes
|
||||||
|
$titleFn = function(&$child, $numChildrenMethod="") {
|
||||||
|
return '<li class="' . $child->markingClasses($numChildrenMethod).
|
||||||
|
'" id="' . $child->ID . '">"' . $child->Title;
|
||||||
|
};
|
||||||
|
|
||||||
|
$html = $root->getChildrenAsUL(
|
||||||
|
"",
|
||||||
|
$titleFn,
|
||||||
|
null,
|
||||||
|
true, // limit to marked
|
||||||
|
$childrenMethod,
|
||||||
|
$numChildrenMethod,
|
||||||
|
true,
|
||||||
|
$nodeCountThreshold
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the class attribute from the $obj2 node in the sitetree, class 'jstree-leaf' means it's a leaf node
|
||||||
|
$nodeClass = $this->getNodeClassFromTree($html, $obj2);
|
||||||
|
$this->assertEquals('jstree-leaf closed', $nodeClass, 'object2 should not have children in the sitetree');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test checks that deleted ('archived') child pages _do_ set a css class on the parent
|
||||||
|
* node that makes it look like it has children when getting all children including deleted
|
||||||
|
*/
|
||||||
|
public function testGetChildrenAsULNodeDeletedOnStage() {
|
||||||
|
$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
|
||||||
|
$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
|
||||||
|
$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
|
||||||
|
$obj2ab = $this->objFromFixture('HierarchyTest_Object', 'obj2b');
|
||||||
|
|
||||||
|
// delete all children under obj2
|
||||||
|
$obj2a->delete();
|
||||||
|
$obj2aa->delete();
|
||||||
|
$obj2ab->delete();
|
||||||
|
// Don't pre-load all children
|
||||||
|
$nodeCountThreshold = 1;
|
||||||
|
|
||||||
|
$childrenMethod = 'AllChildrenIncludingDeleted';
|
||||||
|
$numChildrenMethod = 'numHistoricalChildren';
|
||||||
|
|
||||||
|
$root = new HierarchyTest_Object();
|
||||||
|
$root->markPartialTree($nodeCountThreshold, null, $childrenMethod, $numChildrenMethod);
|
||||||
|
|
||||||
|
// As in LeftAndMain::getSiteTreeFor() but simpler and more to the point for testing purposes
|
||||||
|
$titleFn = function(&$child, $numChildrenMethod="") {
|
||||||
|
return '<li class="' . $child->markingClasses($numChildrenMethod).
|
||||||
|
'" id="' . $child->ID . '">"' . $child->Title;
|
||||||
|
};
|
||||||
|
|
||||||
|
$html = $root->getChildrenAsUL(
|
||||||
|
"",
|
||||||
|
$titleFn,
|
||||||
|
null,
|
||||||
|
true, // limit to marked
|
||||||
|
$childrenMethod,
|
||||||
|
$numChildrenMethod,
|
||||||
|
true,
|
||||||
|
$nodeCountThreshold
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the class attribute from the $obj2 node in the sitetree
|
||||||
|
$nodeClass = $this->getNodeClassFromTree($html, $obj2);
|
||||||
|
// Object2 can now be expanded
|
||||||
|
$this->assertEquals('unexpanded jstree-closed closed', $nodeClass, 'obj2 should have children in the sitetree');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param String $html [description]
|
* @param String $html [description]
|
||||||
* @param array $nodes Breadcrumb path as array
|
* @param array $nodes Breadcrumb path as array
|
||||||
@ -417,6 +508,25 @@ class HierarchyTest extends SapphireTest {
|
|||||||
self::assertThat((bool)$match, self::isFalse(), $message);
|
self::assertThat((bool)$match, self::isFalse(), $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the HTML class attribute from a node in the sitetree
|
||||||
|
*
|
||||||
|
* @param $html
|
||||||
|
* @param $node
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getNodeClassFromTree($html, $node) {
|
||||||
|
$parser = new CSSContentParser($html);
|
||||||
|
$xpath = '//ul/li[@id="' . $node->ID . '"]';
|
||||||
|
$object = $parser->getByXpath($xpath);
|
||||||
|
|
||||||
|
foreach($object[0]->attributes() as $key => $attr) {
|
||||||
|
if($key == 'class') {
|
||||||
|
return (string)$attr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class HierarchyTest_Object extends DataObject implements TestOnly {
|
class HierarchyTest_Object extends DataObject implements TestOnly {
|
||||||
@ -428,4 +538,8 @@ class HierarchyTest_Object extends DataObject implements TestOnly {
|
|||||||
'Hierarchy',
|
'Hierarchy',
|
||||||
"Versioned('Stage', 'Live')",
|
"Versioned('Stage', 'Live')",
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
public function cmstreeclasses() {
|
||||||
|
return $this->markingClasses();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user