2015-04-22 18:24:10 +02:00
|
|
|
<?php namespace Todaymade\Daux\Tree;
|
|
|
|
|
2016-07-04 20:33:44 +02:00
|
|
|
use ArrayIterator;
|
|
|
|
use RuntimeException;
|
2019-11-28 23:32:33 +01:00
|
|
|
use Todaymade\Daux\Config;
|
2015-08-16 22:24:49 +02:00
|
|
|
|
2016-07-04 20:33:44 +02:00
|
|
|
class Directory extends Entry implements \ArrayAccess, \IteratorAggregate
|
2015-04-23 00:32:30 +02:00
|
|
|
{
|
2015-07-18 23:13:02 +02:00
|
|
|
/** @var Entry[] */
|
2015-07-18 14:37:18 +02:00
|
|
|
protected $children = [];
|
2015-04-22 18:24:10 +02:00
|
|
|
|
2015-07-18 23:13:02 +02:00
|
|
|
/** @var Content */
|
|
|
|
protected $first_page;
|
|
|
|
|
2015-04-23 00:32:30 +02:00
|
|
|
public function sort()
|
|
|
|
{
|
2015-10-28 00:01:41 +01:00
|
|
|
// Separate the values into buckets to sort them separately
|
|
|
|
$buckets = [
|
2017-11-08 21:49:30 +01:00
|
|
|
'up_numeric' => [],
|
|
|
|
'up' => [],
|
2015-10-28 00:18:29 +01:00
|
|
|
'index' => [],
|
2015-10-28 00:01:41 +01:00
|
|
|
'numeric' => [],
|
|
|
|
'normal' => [],
|
|
|
|
'down_numeric' => [],
|
|
|
|
'down' => [],
|
|
|
|
];
|
|
|
|
|
|
|
|
foreach ($this->children as $key => $entry) {
|
2020-01-08 08:43:02 +01:00
|
|
|
// In case of generated pages, the name might be empty.
|
|
|
|
// Thus we are falling back to other solutions, otherwise the page would disappear from the tree.
|
2015-10-28 00:01:41 +01:00
|
|
|
$name = $entry->getName();
|
|
|
|
|
2020-01-08 08:43:02 +01:00
|
|
|
if (!$name) {
|
|
|
|
$name = $entry->getTitle();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$name) {
|
|
|
|
$name = $key;
|
2015-10-28 00:18:29 +01:00
|
|
|
}
|
|
|
|
|
2018-05-05 18:16:24 +02:00
|
|
|
if (!$name) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-03-21 09:11:53 +01:00
|
|
|
|
2020-01-08 08:43:02 +01:00
|
|
|
if ($name == 'index' || $name == '_index') {
|
|
|
|
$buckets['index'][$key] = $entry;
|
2020-04-22 22:24:52 +02:00
|
|
|
|
2020-01-08 08:43:02 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-07-27 21:32:51 +02:00
|
|
|
if ($name[0] == '-') {
|
2015-10-28 00:01:41 +01:00
|
|
|
if (is_numeric($name[1])) {
|
2016-07-27 21:32:51 +02:00
|
|
|
$exploded = explode('_', $name);
|
2015-10-28 00:01:41 +01:00
|
|
|
$buckets['down_numeric'][abs(substr($exploded[0], 1))][$key] = $entry;
|
2020-04-22 22:24:52 +02:00
|
|
|
|
2015-10-28 00:01:41 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$buckets['down'][$key] = $entry;
|
2020-04-22 22:24:52 +02:00
|
|
|
|
2015-10-28 00:01:41 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-11-08 21:49:30 +01:00
|
|
|
if ($name[0] == '+') {
|
|
|
|
if (is_numeric($name[1])) {
|
|
|
|
$exploded = explode('_', $name);
|
|
|
|
$buckets['up_numeric'][abs(substr($exploded[0], 1))][$key] = $entry;
|
2020-04-22 22:24:52 +02:00
|
|
|
|
2017-11-08 21:49:30 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$buckets['up'][$key] = $entry;
|
2020-04-22 22:24:52 +02:00
|
|
|
|
2017-11-08 21:49:30 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-10-28 00:01:41 +01:00
|
|
|
if (is_numeric($name[0])) {
|
2016-07-27 21:32:51 +02:00
|
|
|
$exploded = explode('_', $name);
|
2015-10-28 00:01:41 +01:00
|
|
|
$buckets['numeric'][abs($exploded[0])][$key] = $entry;
|
2020-04-22 22:24:52 +02:00
|
|
|
|
2015-10-28 00:01:41 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$buckets['normal'][$key] = $entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
$final = [];
|
|
|
|
foreach ($buckets as $name => $bucket) {
|
2017-11-08 21:49:30 +01:00
|
|
|
if (substr($name, -7) == 'numeric') {
|
2015-10-28 00:01:41 +01:00
|
|
|
ksort($bucket);
|
|
|
|
foreach ($bucket as $sub_bucket) {
|
|
|
|
$final = $this->sortBucket($sub_bucket, $final);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$final = $this->sortBucket($bucket, $final);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->children = $final;
|
|
|
|
}
|
|
|
|
|
2015-11-06 22:44:34 +01:00
|
|
|
private function sortBucket($bucket, $final)
|
|
|
|
{
|
2020-04-22 21:55:53 +02:00
|
|
|
uasort($bucket, function (Entry $a, Entry $b) {
|
2015-10-28 00:01:41 +01:00
|
|
|
return strcasecmp($a->getName(), $b->getName());
|
|
|
|
});
|
|
|
|
|
|
|
|
foreach ($bucket as $key => $value) {
|
|
|
|
$final[$key] = $value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $final;
|
2015-07-18 14:37:18 +02:00
|
|
|
}
|
|
|
|
|
2015-07-18 21:45:38 +02:00
|
|
|
/**
|
|
|
|
* @return Entry[]
|
|
|
|
*/
|
2015-07-18 14:37:18 +02:00
|
|
|
public function getEntries()
|
|
|
|
{
|
|
|
|
return $this->children;
|
|
|
|
}
|
|
|
|
|
2019-11-28 23:32:33 +01:00
|
|
|
public function addChild(Entry $entry): void
|
2015-07-18 14:37:18 +02:00
|
|
|
{
|
|
|
|
$this->children[$entry->getUri()] = $entry;
|
|
|
|
}
|
|
|
|
|
2019-11-28 23:32:33 +01:00
|
|
|
public function removeChild(Entry $entry): void
|
2015-07-18 14:37:18 +02:00
|
|
|
{
|
|
|
|
unset($this->children[$entry->getUri()]);
|
2015-04-22 18:24:10 +02:00
|
|
|
}
|
|
|
|
|
2019-11-28 23:32:33 +01:00
|
|
|
public function getConfig(): Config
|
2015-07-18 23:13:02 +02:00
|
|
|
{
|
|
|
|
if (!$this->parent) {
|
2016-07-27 21:32:51 +02:00
|
|
|
throw new \RuntimeException('Could not retrieve configuration. Are you sure that your tree has a Root ?');
|
2015-07-18 23:13:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $this->parent->getConfig();
|
|
|
|
}
|
|
|
|
|
2020-04-22 21:55:53 +02:00
|
|
|
public function getLocalIndexPage()
|
|
|
|
{
|
2019-12-05 21:25:58 +01:00
|
|
|
$index_key = $this->getConfig()->getIndexKey();
|
2018-06-05 22:29:31 +02:00
|
|
|
|
|
|
|
if (isset($this->children[$index_key])) {
|
|
|
|
return $this->children[$index_key];
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-11-28 23:32:33 +01:00
|
|
|
public function getIndexPage(): ?Content
|
2015-07-18 23:13:02 +02:00
|
|
|
{
|
2020-01-16 16:00:51 +01:00
|
|
|
$indexPage = $this->getLocalIndexPage();
|
|
|
|
|
|
|
|
if ($indexPage instanceof Content) {
|
|
|
|
return $indexPage;
|
2015-07-18 23:13:02 +02:00
|
|
|
}
|
|
|
|
|
2016-08-02 23:39:57 +02:00
|
|
|
if ($this->getConfig()->shouldInheritIndex() && $first_page = $this->seekFirstPage()) {
|
2015-08-16 22:24:49 +02:00
|
|
|
return $first_page;
|
2015-07-21 20:34:21 +02:00
|
|
|
}
|
|
|
|
|
2015-07-18 23:13:02 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2015-07-21 20:34:21 +02:00
|
|
|
/**
|
2020-04-22 22:24:52 +02:00
|
|
|
* Seek the first available page from descendants.
|
2015-07-21 20:34:21 +02:00
|
|
|
*/
|
2019-11-28 23:32:33 +01:00
|
|
|
public function seekFirstPage(): ?Content
|
2015-08-16 22:26:00 +02:00
|
|
|
{
|
2016-07-27 21:32:51 +02:00
|
|
|
if ($this instanceof self) {
|
2019-12-05 21:25:58 +01:00
|
|
|
$index_key = $this->getConfig()->getIndexKey();
|
2020-01-16 16:00:51 +01:00
|
|
|
if (isset($this->children[$index_key]) && $this->children[$index_key] instanceof Content) {
|
2015-08-16 22:26:00 +02:00
|
|
|
return $this->children[$index_key];
|
|
|
|
}
|
|
|
|
foreach ($this->children as $node_key => $node) {
|
|
|
|
if ($node instanceof Content) {
|
|
|
|
return $node;
|
|
|
|
}
|
2016-07-27 21:32:51 +02:00
|
|
|
if ($node instanceof self
|
2015-08-16 22:26:00 +02:00
|
|
|
&& strpos($node->getUri(), '.') !== 0
|
2016-07-27 21:32:51 +02:00
|
|
|
&& $childNode = $node->seekFirstPage()) {
|
2015-08-16 22:26:00 +02:00
|
|
|
return $childNode;
|
|
|
|
}
|
|
|
|
}
|
2015-07-21 20:34:21 +02:00
|
|
|
}
|
2016-07-27 21:32:51 +02:00
|
|
|
|
2015-08-16 22:26:00 +02:00
|
|
|
return null;
|
2015-07-21 20:34:21 +02:00
|
|
|
}
|
|
|
|
|
2019-11-28 23:32:33 +01:00
|
|
|
public function getFirstPage(): ?Content
|
2015-07-18 23:13:02 +02:00
|
|
|
{
|
|
|
|
if ($this->first_page) {
|
|
|
|
return $this->first_page;
|
|
|
|
}
|
|
|
|
|
|
|
|
// First we try to find a real page
|
|
|
|
foreach ($this->getEntries() as $node) {
|
|
|
|
if ($node instanceof Content) {
|
2015-07-19 01:16:04 +02:00
|
|
|
if ($this instanceof Root && $this->getIndexPage() == $node) {
|
|
|
|
// The homepage should not count as first page
|
2015-07-18 23:13:02 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->setFirstPage($node);
|
2016-07-27 21:32:51 +02:00
|
|
|
|
2015-07-18 23:13:02 +02:00
|
|
|
return $node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we can't find one we check in the sub-directories
|
|
|
|
foreach ($this->getEntries() as $node) {
|
2016-07-27 21:32:51 +02:00
|
|
|
if ($node instanceof self && $page = $node->getFirstPage()) {
|
2015-07-18 23:13:02 +02:00
|
|
|
$this->setFirstPage($page);
|
2016-07-27 21:32:51 +02:00
|
|
|
|
2015-07-18 23:13:02 +02:00
|
|
|
return $page;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-20 15:59:52 +02:00
|
|
|
return null;
|
2015-07-18 23:13:02 +02:00
|
|
|
}
|
|
|
|
|
2019-11-28 23:32:33 +01:00
|
|
|
public function setFirstPage(Content $first_page)
|
2015-07-18 23:13:02 +02:00
|
|
|
{
|
|
|
|
$this->first_page = $first_page;
|
|
|
|
}
|
|
|
|
|
2015-07-29 08:30:41 +02:00
|
|
|
/**
|
|
|
|
* Used when creating the navigation.
|
2020-04-22 22:24:52 +02:00
|
|
|
* Hides folders without showable content.
|
2015-07-29 08:30:41 +02:00
|
|
|
*/
|
2019-11-28 23:32:33 +01:00
|
|
|
public function hasContent(): bool
|
2015-07-29 08:30:41 +02:00
|
|
|
{
|
|
|
|
foreach ($this->getEntries() as $node) {
|
|
|
|
if ($node instanceof Content) {
|
|
|
|
return true;
|
2020-04-22 22:24:52 +02:00
|
|
|
}
|
|
|
|
if ($node instanceof self) {
|
2015-07-29 08:30:41 +02:00
|
|
|
if ($node->hasContent()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-07-18 20:52:14 +02:00
|
|
|
public function dump()
|
|
|
|
{
|
|
|
|
$dump = parent::dump();
|
|
|
|
|
2015-07-18 23:13:02 +02:00
|
|
|
$dump['index'] = $this->getIndexPage() ? $this->getIndexPage()->getUrl() : '';
|
|
|
|
$dump['first'] = $this->getFirstPage() ? $this->getFirstPage()->getUrl() : '';
|
|
|
|
|
2015-07-18 20:52:14 +02:00
|
|
|
foreach ($this->getEntries() as $entry) {
|
|
|
|
$dump['children'][] = $entry->dump();
|
|
|
|
}
|
|
|
|
|
|
|
|
return $dump;
|
|
|
|
}
|
2016-07-04 20:33:44 +02:00
|
|
|
|
|
|
|
/**
|
2020-04-22 22:24:52 +02:00
|
|
|
* Whether a offset exists.
|
|
|
|
*
|
|
|
|
* @param mixed $offset an offset to check for
|
|
|
|
*
|
|
|
|
* @return bool true on success or false on failure
|
2016-07-04 20:33:44 +02:00
|
|
|
*/
|
2019-11-28 23:32:33 +01:00
|
|
|
public function offsetExists($offset): bool
|
2016-07-04 20:33:44 +02:00
|
|
|
{
|
|
|
|
return array_key_exists($offset, $this->children);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-04-22 22:24:52 +02:00
|
|
|
* Offset to retrieve.
|
|
|
|
*
|
|
|
|
* @param mixed $offset the offset to retrieve
|
|
|
|
*
|
|
|
|
* @return Entry can return all value types
|
2016-07-04 20:33:44 +02:00
|
|
|
*/
|
|
|
|
public function offsetGet($offset)
|
|
|
|
{
|
|
|
|
return $this->children[$offset];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-04-22 22:24:52 +02:00
|
|
|
* Offset to set.
|
|
|
|
*
|
|
|
|
* @param mixed $offset the offset to assign the value to
|
|
|
|
* @param Entry $value the value to set
|
2016-07-04 20:33:44 +02:00
|
|
|
*/
|
|
|
|
public function offsetSet($offset, $value)
|
|
|
|
{
|
|
|
|
if (!$value instanceof Entry) {
|
2016-07-27 21:32:51 +02:00
|
|
|
throw new RuntimeException('The value is not of type Entry');
|
2016-07-04 20:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->addChild($value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-04-22 22:24:52 +02:00
|
|
|
* Offset to unset.
|
|
|
|
*
|
2016-07-04 20:33:44 +02:00
|
|
|
* @param string $offset the offset to unset
|
|
|
|
*/
|
|
|
|
public function offsetUnset($offset)
|
|
|
|
{
|
|
|
|
unset($this->children[$offset]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getIterator()
|
|
|
|
{
|
|
|
|
return new ArrayIterator($this->children);
|
|
|
|
}
|
2015-04-22 18:24:10 +02:00
|
|
|
}
|