Merge pull request #2976 from maxime-rainville/pulls/5.2/list-view-breadcrumb

BUG Don't double up the breadcrumb on list views
This commit is contained in:
Guy Sartorelli 2024-07-25 09:41:06 +12:00 committed by GitHub
commit 6218b130d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 169 additions and 78 deletions

View File

@ -1040,49 +1040,33 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
public function Breadcrumbs($unlinked = false)
{
$items = new ArrayList();
$items = ArrayList::create();
if ($this->TreeIsFiltered()) {
$items->push(new ArrayData([
'Title' => CMSPagesController::menu_title(),
'Link' => ($unlinked) ? false : $this->LinkPages()
]));
$items->push(new ArrayData([
'Title' => _t('SilverStripe\\CMS\\Controllers\\CMSMain.SEARCHRESULTS', 'Search results'),
'Link' => ($unlinked) ? false : $this->LinkPages()
]));
if (($this->getAction() !== 'index') && ($record = $this->currentPage())) {
// The page is being edited
$this->buildEditFormBreadcrumb($items, $record, $unlinked);
} else {
// Ensure we always have the "Pages" crumb first
$this->pushCrumb(
$items,
CMSPagesController::menu_title(),
$unlinked ? false : $this->LinkPages()
);
$this->extend('updateBreadcrumbs', $items);
return $items;
}
// Check if we are editing a page
/** @var SiteTree $record */
$record = $this->currentPage();
if (!$record) {
$items->push(new ArrayData([
'Title' => CMSPagesController::menu_title(),
'Link' => ($unlinked) ? false : $this->LinkPages()
]));
$this->extend('updateBreadcrumbs', $items);
return $items;
}
// Add all ancestors
$ancestors = $record->getAncestors();
$ancestors = new ArrayList(array_reverse($ancestors->toArray() ?? []));
$ancestors->push($record);
/** @var SiteTree $ancestor */
foreach ($ancestors as $ancestor) {
$items->push(new ArrayData([
'Title' => $ancestor->getMenuTitle(),
'Link' => ($unlinked)
? false
: $ancestor->CMSEditLink()
]));
if ($this->TreeIsFiltered()) {
// Showing search results
$this->pushCrumb(
$items,
_t(CMSMain::class . '.SEARCHRESULTS', 'Search results'),
($unlinked) ? false : $this->LinkPages()
);
} elseif ($parentID = $this->getRequest()->getVar('ParentID')) {
// We're navigating the listview. ParentID is the page whose
// children are currently displayed.
if ($page = SiteTree::get()->byID($parentID)) {
$this->buildListViewBreadcrumb($items, $page);
}
}
}
$this->extend('updateBreadcrumbs', $items);
@ -1090,6 +1074,61 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return $items;
}
/**
* Push the provided an extra breadcrumb crumb at the end of the provided List
*/
private function pushCrumb(ArrayList $items, string $title, string|false $link): void
{
$items->push(ArrayData::create([
'Title' => $title,
'Link' => $link
]));
}
/**
* Build Breadcrumb for the Edit page form. Each crumb links back to its own edit form.
*/
private function buildEditFormBreadcrumb(ArrayList $items, SiteTree $page, bool $unlinked): void
{
// Find all ancestors of the provided page
$ancestors = $page->getAncestors(true);
$ancestors = array_reverse($ancestors->toArray() ?? []);
foreach ($ancestors as $ancestor) {
// Link to the ancestor's edit form
$this->pushCrumb(
$items,
$ancestor->getMenuTitle(),
$unlinked ? false : $ancestor->CMSEditLink()
);
}
}
/**
* Build Breadcrumb for the List view. Each crumb links to the list view for that page.
*/
private function buildListViewBreadcrumb(ArrayList $items, SiteTree $page): void
{
// Find all ancestors of the provided page
$ancestors = $page->getAncestors(true);
$ancestors = array_reverse($ancestors->toArray() ?? []);
//turns the title and link of the breadcrumbs into template-friendly variables
$params = array_filter([
'view' => $this->getRequest()->getVar('view'),
'q' => $this->getRequest()->getVar('q')
]);
foreach ($ancestors as $ancestor) {
// Link back to the list view for the current ancestor
$params['ParentID'] = $ancestor->ID;
$this->pushCrumb(
$items,
$ancestor->getMenuTitle(),
Controller::join_links($this->Link(), '?' . http_build_query($params ?? []))
);
}
}
/**
* Create serialized JSON string with site tree hints data to be injected into
* 'data-hints' attribute of root node of jsTree.

View File

@ -31,35 +31,4 @@ class CMSPagesController extends CMSMain
{
return false;
}
public function Breadcrumbs($unlinked = false)
{
$this->beforeExtending('updateBreadcrumbs', function (ArrayList $items) {
//special case for building the breadcrumbs when calling the listchildren Pages ListView action
if ($parentID = $this->getRequest()->getVar('ParentID')) {
$page = SiteTree::get()->byID($parentID);
//build a reversed list of the parent tree
$pages = [];
while ($page) {
array_unshift($pages, $page); //add to start of array so that array is in reverse order
$page = $page->Parent;
}
//turns the title and link of the breadcrumbs into template-friendly variables
$params = array_filter([
'view' => $this->getRequest()->getVar('view'),
'q' => $this->getRequest()->getVar('q')
]);
foreach ($pages as $page) {
$params['ParentID'] = $page->ID;
$item = new stdClass();
$item->Title = $page->Title;
$item->Link = Controller::join_links($this->Link(), '?' . http_build_query($params ?? []));
$items->push(new ArrayData($item));
}
}
});
return parent::Breadcrumbs($unlinked);
}
}

View File

@ -357,20 +357,99 @@ class CMSMainTest extends FunctionalTest
public function testBreadcrumbs()
{
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page31 = $this->objFromFixture(SiteTree::class, 'page31');
$this->logInAs('admin');
$response = $this->get('admin/pages/edit/show/' . $page31->ID);
$parser = new CSSContentParser($response->getBody());
$this->assertCrumbs(
['Page 3', 'Page 3.1'],
$response,
'Edit breadcrumb includes all pages up to the one being edited without a tob level Page'
);
}
public function testBreadcrumbsListView()
{
$page311 = $this->objFromFixture(SiteTree::class, 'page311');
$this->logInAs('admin');
$response = $this->get('admin/pages?ParentID=' . $page311->ID);
$this->assertCrumbs(
['Pages', 'Page 3', 'Page 3.1', 'Page 3.1.1'],
$response,
'List view breadcrumb includes all pages and a Page link back to the root level'
);
}
public function testBreadcrumbsListViewTopLevel()
{
$page311 = $this->objFromFixture(SiteTree::class, 'page311');
$this->logInAs('admin');
$response = $this->get('admin/pages');
$this->assertCrumbs(
['Pages'],
$response,
'Top level of list view includes only a Page crumb'
);
}
public function testBreadcrumbsListViewWithPjax()
{
$page311 = $this->objFromFixture(SiteTree::class, 'page311');
$this->logInAs('admin');
$response = $this->get('admin/pages?ParentID=' . $page311->ID);
$this->assertCrumbs(
['Pages', 'Page 3', 'Page 3.1', 'Page 3.1.1'],
$response,
'List view breadcrumb includes all pages and a Page link back to the root level'
);
}
public function testBreadcrumbsSearchView()
{
$page311 = $this->objFromFixture(SiteTree::class, 'page311');
$this->logInAs('admin');
$response = $this->get(
'admin/pages?ParentID=' . $page311->ID,
null,
[
'X-Pjax' => 'ListViewForm,Breadcrumbs',
'X-Requested-With' => 'XMLHttpRequest'
]
);
$jsonStr = $response->getBody();
$data = json_decode($jsonStr, true);
$parser = new CSSContentParser($data['Breadcrumbs']);
$crumbs = $parser->getBySelector('.breadcrumbs-wrapper .crumb');
$this->assertNotNull($crumbs);
$this->assertEquals(2, count($crumbs ?? []));
$this->assertEquals('Page 3', (string)$crumbs[0]);
$this->assertEquals('Page 3.1', (string)$crumbs[1]);
$crumbs = array_map(function ($crumb) {
return (string)$crumb;
}, $crumbs);
Security::setCurrentUser(null);
$this->assertNotNull($crumbs, 'Should have found some crumbs');
$this->assertEquals(
['Pages', 'Page 3', 'Page 3.1', 'Page 3.1.1'],
$crumbs,
'List view breadcrumb includes all pages and a Page link back to the root level when access wia PJAX'
);
}
private function assertCrumbs(array $expectedCrumbs, $response, string $message): void
{
$parser = new CSSContentParser($response->getBody());
$crumbs = $parser->getBySelector('.breadcrumbs-wrapper .crumb');
$crumbs = array_map(function ($crumb) {
return (string)$crumb;
}, $crumbs);
$this->assertNotNull($crumbs, $message);
$this->assertEquals($expectedCrumbs, $crumbs, $message);
}
public function testGetNewItem()

View File

@ -12,6 +12,10 @@ SilverStripe\CMS\Model\SiteTree:
Title: Page 3.1
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
Sort: 1
page311:
Title: Page 3.1.1
Parent: =>SilverStripe\CMS\Model\SiteTree.page31
Sort: 1
page32:
Title: Page 3.2
Parent: =>SilverStripe\CMS\Model\SiteTree.page3