Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/components/pdotools/docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html)

- PHPUnit 9.6 harness (Unit + Integration) and GitHub Actions on PHP 8.1, 8.2, 8.3, 8.4.
- Codecov PR comments in informational mode, same as MODX Revolution.
- [pdoTools3#11] [pdoMenu/pdoPage/pdoCrumbs] Template rows expose isFirst, isLast, isActive, hasChildren/hasChilds (menu also isHere, isStart, isCategory, isInner; page isSkip; crumbs isHome). Specialized tpl* chunks still work.
- [#306] [pdoMenu] Added the "children" placeholder to tplInner.
- [#355] [Fenom] Added type cast modifiers: boolval, doubleval, floatval, intval, strval.
- [#356] [Fenom] Enabled the array_merge modifier by default.
Expand Down
25 changes: 23 additions & 2 deletions core/components/pdotools/elements/snippets/snippet.pdocrumbs.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use ModxPro\PdoTools\Fetch;
use ModxPro\PdoTools\Support\TemplateFlags;
use MODX\Revolution\modResource;
use MODX\Revolution\modWebLink;

Expand Down Expand Up @@ -158,6 +159,7 @@
$rows = array_reverse($rows);
}

$prepared = [];
foreach ($rows as $row) {
if (!empty($useWeblinkUrl) && $row['class_key'] === modWebLink::class) {
$row['link'] = is_numeric(trim($row['content'], '[]~ '))
Expand All @@ -177,12 +179,31 @@
}

if (isset($return) && $return === 'data') {
$output[] = $row;
$prepared[] = $row;
continue;
}
if ($row['id'] == $resource->id && empty($showCurrent)) {
continue;
} elseif ($row['id'] == $resource->id && !empty($tplCurrent)) {
}
$prepared[] = $row;
}

$total = count($prepared);
foreach ($prepared as $index => $row) {
$row = array_merge(
$row,
TemplateFlags::toPlaceholders([
'isFirst' => $index === 0,
'isLast' => $total > 0 && $index === $total - 1,
'isActive' => (int)$row['id'] === (int)$resource->id,
'isHome' => (int)$row['id'] === (int)$siteStart,
])
);
if (isset($return) && $return === 'data') {
$output[] = $row;
continue;
}
if ($row['id'] == $resource->id && !empty($tplCurrent)) {
$tpl = $tplCurrent;
} elseif ($row['id'] == $siteStart && !empty($tplHome)) {
$tpl = $tplHome;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,19 +160,19 @@
if (!empty($pageCount) && $pageCount > 1) {
$pagination = [
'first' => $page > 1 && !empty($tplPageFirst)
? $paginator->makePageLink($url, 1, $tplPageFirst)
? $paginator->makePageLink($url, 1, $tplPageFirst, $page, $pageCount)
: '',
'prev' => $page > 1 && !empty($tplPagePrev)
? $paginator->makePageLink($url, $page - 1, $tplPagePrev)
? $paginator->makePageLink($url, $page - 1, $tplPagePrev, $page, $pageCount)
: '',
'pages' => $pageLimit >= 7 && empty($disableModernPagination)
? $paginator->buildModernPagination($page, $pageCount, $url)
: $paginator->buildClassicPagination($page, $pageCount, $url),
'next' => $page < $pageCount && !empty($tplPageNext)
? $paginator->makePageLink($url, $page + 1, $tplPageNext)
? $paginator->makePageLink($url, $page + 1, $tplPageNext, $page, $pageCount)
: '',
'last' => $page < $pageCount && !empty($tplPageLast)
? $paginator->makePageLink($url, $pageCount, $tplPageLast)
? $paginator->makePageLink($url, $pageCount, $tplPageLast, $page, $pageCount)
: '',
];

Expand Down
89 changes: 31 additions & 58 deletions core/components/pdotools/src/Support/MenuBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ public function templateBranch($row = [])
$row['menutitle'] = $row['pagetitle'];
}

$classes = $this->getClasses($row);
$state = $this->itemState($row);
$classes = $state->classes();
if (!empty($classes)) {
$row['classNames'] = $row['classnames'] = $classes;
$row['classes'] = ' class="' . $classes . '"';
Expand All @@ -201,7 +202,8 @@ public function templateBranch($row = [])
? $row[$this->pdoTools->config('titleOfLinks')]
: $row['pagetitle'];

$tpl = $this->getTpl($row);
$row = array_merge($row, $state->placeholders());
$tpl = $this->tplFromState($state, $row);
$row = $this->addWayFinderPlaceholders($row);

return $this->pdoTools->getChunk($tpl, $row, $this->pdoTools->config('fastMode'));
Expand All @@ -221,6 +223,18 @@ public function isHere($id = 0)
}


/**
* @param array $row
* @return MenuItemState
*/
protected function itemState(array $row = [])
{
$config = $this->pdoTools->config();
$rowId = MenuItemState::resolveRowId($row, $config);

return MenuItemState::fromRow($row, $config, $this->isHere($rowId));
}

/**
* Determine style class for current item being processed
*
Expand All @@ -230,76 +244,35 @@ public function isHere($id = 0)
*/
public function getClasses($row = [])
{
$classes = [];

if (!empty($this->pdoTools->config('rowClass'))) {
$classes[] = $this->pdoTools->config('rowClass');
}
if ($row['idx'] == 1 && !empty($this->pdoTools->config('firstClass'))) {
$classes[] = $this->pdoTools->config('firstClass');
} elseif (!empty($row['last']) && !empty($this->pdoTools->config('lastClass'))) {
$classes[] = $this->pdoTools->config('lastClass');
}
if (!empty($this->pdoTools->config('levelClass'))) {
$classes[] = $this->pdoTools->config('levelClass') . $row['level'];
}
if ($row['children'] && !empty($this->pdoTools->config('parentClass')) && ($row['level'] < $this->pdoTools->config('level') || empty($this->pdoTools->config('level')))) {
$classes[] = $this->pdoTools->config('parentClass');
}
$row_id = !empty($this->pdoTools->config('useWeblinkUrl')) && !empty($row['content']) && !empty($row['class_key']) && is_numeric(trim($row['content'], '[]~ ')) && $row['class_key'] == modWebLink::class
? (int)trim($row['content'], '[]~ ')
: $row['id'];
if ($this->isHere($row_id) && !empty($this->pdoTools->config('hereClass'))) {
$classes[] = $this->pdoTools->config('hereClass');
}
if ($row_id == $this->pdoTools->config('hereId') && !empty($this->pdoTools->config('selfClass'))) {
$classes[] = $this->pdoTools->config('selfClass');
}
if (!empty($row['class_key']) && $row['class_key'] === modWebLink::class && !empty($this->pdoTools->config('webLinkClass'))) {
$classes[] = $this->pdoTools->config('webLinkClass');
}

return implode(' ', $classes);
return $this->itemState($row)->classes();
}


/**
* Determine style class for current item being processed
* Chunk name for the current menu row.
*
* @param array $row
*
* @return mixed
*/
public function getTpl($row = [])
{
$row_id = !empty($this->pdoTools->config('useWeblinkUrl')) && !empty($row['class_key']) && !empty($row['content']) && $row['class_key'] === modWebLink::class && is_numeric(trim($row['content'], '[]~ '))
? (int)trim($row['content'], '[]~ ')
: $row['id'];
if ($row['level'] === 1 && !empty($this->pdoTools->config('tplStart')) && !empty($this->pdoTools->config('displayStart'))) {
$tpl = 'tplStart';
} elseif ($row['children'] && $row_id == $this->pdoTools->config('hereId') && !empty($this->pdoTools->config('tplParentRowHere'))) {
$tpl = 'tplParentRowHere';
} elseif ($row['level'] > 1 && $row_id == $this->pdoTools->config('hereId') && !empty($this->pdoTools->config('tplInnerHere'))) {
$tpl = 'tplInnerHere';
} elseif ($row_id == $this->pdoTools->config('hereId') && !empty($this->pdoTools->config('tplHere'))) {
$tpl = 'tplHere';
} elseif ($row['children'] && $this->isHere($row_id) && !empty($this->pdoTools->config('tplParentRowActive'))) {
$tpl = 'tplParentRowActive';
} elseif ($row['children'] && (empty($row['template']) || strpos($row['link_attributes'], 'category') != false) && !empty($this->pdoTools->config('tplCategoryFolder'))) {
$tpl = 'tplCategoryFolder';
} // It's a typo, but it is left for backward compatibility
elseif ($row['children'] && (empty($row['template']) || strpos($row['link_attributes'], 'category') != false) && !empty($this->pdoTools->config('tplCategoryFolders'))) {
$tpl = 'tplCategoryFolders';
} // ---
elseif ($row['children'] && !empty($this->pdoTools->config('tplParentRow'))) {
$tpl = 'tplParentRow';
} elseif ($row['level'] > 1 && !empty($this->pdoTools->config('tplInnerRow'))) {
$tpl = 'tplInnerRow';
} else {
return $this->tplFromState($this->itemState($row), $row);
}

/**
* @param MenuItemState $state
* @param array $row
* @return mixed
*/
protected function tplFromState(MenuItemState $state, array $row)
{
$key = $state->tplKey();
if ($key === null) {
return $this->pdoTools->defineChunk($row);
}

return $this->pdoTools->config($tpl);
return $this->pdoTools->config($key);
}


Expand Down
184 changes: 184 additions & 0 deletions core/components/pdotools/src/Support/MenuItemState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<?php

namespace ModxPro\PdoTools\Support;

use MODX\Revolution\modWebLink;

/**
* One pdoMenu row: flags, CSS classes, and which tpl* key to use.
*/
class MenuItemState
{
/** @var int */
public $rowId;
/** @var bool */
public $isFirst;
/** @var bool */
public $isLast;
/** @var bool */
public $hasChildren;
/** @var bool */
public $isActive;
/** @var bool */
public $isHere;
/** @var bool */
public $isStart;
/** @var bool */
public $isCategory;
/** @var bool */
public $isInner;
/** @var bool */
public $isWebLink;
/** @var int */
public $level;
/** @var array */
private $config;

/**
* @param array $row
* @param array $config
* @param bool $isHere Whether this rowId is on the path to the current resource
* @return self
*/
public static function fromRow(array $row, array $config, $isHere)
{
$state = new self();
$state->config = $config;
$state->level = (int)($row['level'] ?? 1);
$state->rowId = self::resolveRowId($row, $config);
$state->isFirst = isset($row['idx']) && (int)$row['idx'] === 1;
$state->isLast = !empty($row['last']);
$state->hasChildren = !empty($row['children']);
$state->isActive = $state->rowId == ($config['hereId'] ?? 0);
$state->isHere = (bool)$isHere;
$state->isStart = $state->level === 1 && !empty($config['displayStart']);
$state->isInner = $state->level > 1;
$state->isWebLink = !empty($row['class_key']) && $row['class_key'] === modWebLink::class;
$attrs = isset($row['link_attributes']) ? (string)$row['link_attributes'] : '';
$state->isCategory = $state->hasChildren && (
empty($row['template'])
|| ($attrs !== '' && strpos($attrs, 'category') !== false)
);

return $state;
}

/**
* @param array $row
* @param array $config
* @return int
*/
public static function resolveRowId(array $row, array $config)
{
if (
!empty($config['useWeblinkUrl'])
&& !empty($row['class_key'])
&& !empty($row['content'])
&& $row['class_key'] === modWebLink::class
&& is_numeric(trim($row['content'], '[]~ '))
) {
return (int)trim($row['content'], '[]~ ');
}

return (int)($row['id'] ?? 0);
}

/**
* @return array<string, int>
*/
public function placeholders()
{
$out = TemplateFlags::toPlaceholders([
'isFirst' => $this->isFirst,
'isLast' => $this->isLast,
'isActive' => $this->isActive,
'hasChildren' => $this->hasChildren,
'isHere' => $this->isHere,
'isStart' => $this->isStart,
'isCategory' => $this->isCategory,
'isInner' => $this->isInner,
]);
$out['hasChilds'] = $out['hasChildren'];

return $out;
}

/**
* @return string
*/
public function classes()
{
$config = $this->config;
$classes = [];

if (!empty($config['rowClass'])) {
$classes[] = $config['rowClass'];
}
if ($this->isFirst && !empty($config['firstClass'])) {
$classes[] = $config['firstClass'];
} elseif ($this->isLast && !empty($config['lastClass'])) {
$classes[] = $config['lastClass'];
}
if (!empty($config['levelClass'])) {
$classes[] = $config['levelClass'] . $this->level;
}
if (
$this->hasChildren
&& !empty($config['parentClass'])
&& ($this->level < ($config['level'] ?? 0) || empty($config['level']))
) {
$classes[] = $config['parentClass'];
}
if ($this->isHere && !empty($config['hereClass'])) {
$classes[] = $config['hereClass'];
}
if ($this->isActive && !empty($config['selfClass'])) {
$classes[] = $config['selfClass'];
}
if ($this->isWebLink && !empty($config['webLinkClass'])) {
$classes[] = $config['webLinkClass'];
}

return implode(' ', $classes);
}

/**
* Config key for a specialized tpl*, or null to fall back to defineChunk().
*
* @return string|null
*/
public function tplKey()
{
$config = $this->config;
if ($this->isStart && !empty($config['tplStart'])) {
return 'tplStart';
}
if ($this->hasChildren && $this->isActive && !empty($config['tplParentRowHere'])) {
return 'tplParentRowHere';
}
if ($this->isInner && $this->isActive && !empty($config['tplInnerHere'])) {
return 'tplInnerHere';
}
if ($this->isActive && !empty($config['tplHere'])) {
return 'tplHere';
}
if ($this->hasChildren && $this->isHere && !empty($config['tplParentRowActive'])) {
return 'tplParentRowActive';
}
if ($this->isCategory && !empty($config['tplCategoryFolder'])) {
return 'tplCategoryFolder';
}
// Typo kept for backward compatibility
if ($this->isCategory && !empty($config['tplCategoryFolders'])) {
return 'tplCategoryFolders';
}
if ($this->hasChildren && !empty($config['tplParentRow'])) {
return 'tplParentRow';
}
if ($this->isInner && !empty($config['tplInnerRow'])) {
return 'tplInnerRow';
}

return null;
}
}
Loading
Loading