typemill/system/Models/Folder.php
2020-02-26 21:34:56 +01:00

554 lines
No EOL
16 KiB
PHP

<?php
namespace Typemill\Models;
use \URLify;
class Folder
{
/*
* scans content of a folder (without recursion)
* vars: folder path as string
* returns: one-dimensional array with names of folders and files
*/
public static function scanFolderFlat($folderPath)
{
$folderItems = scandir($folderPath);
$folderContent = array();
foreach ($folderItems as $key => $item)
{
if (!in_array($item, array(".","..")))
{
$nameParts = self::getStringParts($item);
$fileType = array_pop($nameParts);
if($fileType == 'md' OR $fileType == 'txt' )
{
$folderContent[] = $item;
}
}
}
return $folderContent;
}
/*
* scans content of a folder recursively
* vars: folder path as string
* returns: multi-dimensional array with names of folders and files
*/
public static function scanFolder($folderPath, $draft = false)
{
$folderItems = scandir($folderPath);
$folderContent = array();
# if it is the live version and if it is a folder that is not published, then do not show the folder and its content.
if(!$draft && !in_array('index.md', $folderItems)){ return false; }
foreach ($folderItems as $key => $item)
{
if (!in_array($item, array(".","..")))
{
if (is_dir($folderPath . DIRECTORY_SEPARATOR . $item))
{
$subFolder = $item;
$folderPublished = file_exists($folderPath . DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . 'index.md');
# scan that folder only if it is a draft or if the folder is published (contains index.md)
if($draft OR $folderPublished)
{
$folderContent[$subFolder] = self::scanFolder($folderPath . DIRECTORY_SEPARATOR . $subFolder, $draft);
}
}
else
{
$nameParts = self::getStringParts($item);
$fileType = array_pop($nameParts);
if($fileType == 'md')
{
$folderContent[] = $item;
}
if($draft === true && $fileType == 'txt')
{
if(isset($last) && ($last == implode($nameParts)) )
{
array_pop($folderContent);
$item = $item . 'md';
}
$folderContent[] = $item;
}
/* store the name of the last file */
$last = implode($nameParts);
}
}
}
return $folderContent;
}
/*
* Transforms array of folder item into an array of item-objects with additional information for each item
* vars: multidimensional array with folder- and file-names
* returns: array of objects. Each object contains information about an item (file or folder).
*/
public static function getFolderContentDetails(array $folderContent, $extended, $baseUrl, $fullSlugWithFolder = NULL, $fullSlugWithoutFolder = NULL, $fullPath = NULL, $keyPath = NULL, $chapter = NULL)
{
$contentDetails = [];
$iteration = 0;
$chapternr = 1;
foreach($folderContent as $key => $name)
{
$item = new \stdClass();
if(is_array($name))
{
$nameParts = self::getStringParts($key);
$fileType = '';
if(in_array('index.md', $name))
{
$fileType = 'md';
$status = 'published';
}
if(in_array('index.txt', $name))
{
$fileType = 'txt';
$status = 'unpublished';
}
if(in_array('index.txtmd', $name))
{
$fileType = 'txt';
$status = 'modified';
}
$item->originalName = $key;
$item->elementType = 'folder';
$item->contains = self::getFolderContentType($name, $fullPath . DIRECTORY_SEPARATOR . $key . DIRECTORY_SEPARATOR . 'index.yaml');
$item->status = $status;
$item->fileType = $fileType;
$item->order = count($nameParts) > 1 ? array_shift($nameParts) : NULL;
$item->name = implode(" ",$nameParts);
$item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name);
$item->slug = implode("-",$nameParts);
$item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug));
$item->path = $fullPath . DIRECTORY_SEPARATOR . $key;
$item->pathWithoutType = $fullPath . DIRECTORY_SEPARATOR . $key . DIRECTORY_SEPARATOR . 'index';
$item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug;
$item->urlRel = $fullSlugWithFolder . '/' . $item->slug;
$item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug;
$item->key = $iteration;
$item->keyPath = isset($keyPath) ? $keyPath . '.' . $iteration : $iteration;
$item->keyPathArray = explode('.', $item->keyPath);
$item->chapter = $chapter ? $chapter . '.' . $chapternr : $chapternr;
$item->active = false;
$item->activeParent = false;
$item->hide = false;
# check if there are extended information
if($extended && isset($extended[$item->urlRelWoF]))
{
$item->name = ($extended[$item->urlRelWoF]['navtitle'] != '') ? $extended[$item->urlRelWoF]['navtitle'] : $item->name;
$item->hide = ($extended[$item->urlRelWoF]['hide'] === true) ? true : false;
}
# sort posts in descending order
if($item->contains == "posts")
{
rsort($name);
}
$item->folderContent = self::getFolderContentDetails($name, $extended, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter);
}
else
{
# do not use files in base folder (only folders are allowed)
# if(!isset($keyPath)) continue;
# do not use index files
if($name == 'index.md' || $name == 'index.txt' || $name == 'index.txtmd' ) continue;
$nameParts = self::getStringParts($name);
$fileType = array_pop($nameParts);
$nameWithoutType = self::getNameWithoutType($name);
if($fileType == 'md')
{
$status = 'published';
}
elseif($fileType == 'txt')
{
$status = 'unpublished';
}
else
{
$fileType = 'txt';
$status = 'modified';
}
$item->originalName = $name;
$item->elementType = 'file';
$item->status = $status;
$item->fileType = $fileType;
$item->order = count($nameParts) > 1 ? array_shift($nameParts) : NULL;
$item->name = implode(" ",$nameParts);
$item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name);
$item->slug = implode("-",$nameParts);
$item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug));
$item->path = $fullPath . DIRECTORY_SEPARATOR . $name;
$item->pathWithoutType = $fullPath . DIRECTORY_SEPARATOR . $nameWithoutType;
$item->key = $iteration;
$item->keyPath = isset($keyPath) ? $keyPath . '.' . $iteration : $iteration;
$item->keyPathArray = explode('.',$item->keyPath);
$item->chapter = $chapter . '.' . $chapternr;
$item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug;
$item->urlRel = $fullSlugWithFolder . '/' . $item->slug;
$item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug;
$item->active = false;
$item->activeParent = false;
$item->hide = false;
# check if there are extended information
if($extended && isset($extended[$item->urlRelWoF]))
{
$item->name = ($extended[$item->urlRelWoF]['navtitle'] != '') ? $extended[$item->urlRelWoF]['navtitle'] : $item->name;
$item->hide = ($extended[$item->urlRelWoF]['hide'] === true) ? true : false;
}
}
$iteration++;
$chapternr++;
$contentDetails[] = $item;
}
return $contentDetails;
}
public static function getFolderContentType($folder, $yamlpath)
{
# check if folder is empty or has only index.yaml-file. This is a rare case so make it quick and dirty
if(count($folder) == 1)
{
# check if in folder yaml file contains "posts", then return posts
$folderyamlpath = getcwd() . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . $yamlpath;
$fileContent = false;
if(file_exists($folderyamlpath))
{
$fileContent = file_get_contents($folderyamlpath);
}
if($fileContent && strpos($fileContent, 'contains: posts') !== false)
{
return 'posts';
}
return 'pages';
}
else
{
$file = $folder[0];
$nameParts = self::getStringParts($file);
$order = count($nameParts) > 1 ? array_shift($nameParts) : NULL;
$order = substr($order, 0, 7);
if(\DateTime::createFromFormat('Ymd', $order) !== FALSE)
{
return "posts";
}
else
{
return "pages";
}
}
}
public static function getItemForUrl($folderContentDetails, $url, $baseUrl, $result = NULL)
{
# if we are on the homepage
if($url == '/' OR $url == $baseUrl)
{
# return a standard item-object
$item = new \stdClass;
$item->elementType = 'folder';
$item->path = '';
$item->urlRel = '/';
$item->pathWithoutType = DIRECTORY_SEPARATOR . 'index';
return $item;
}
foreach($folderContentDetails as $key => $item)
{
# set item active, needed to move item in navigation
if($item->urlRel === $url)
{
$item->active = true;
$result = $item;
}
elseif($item->elementType === "folder")
{
$result = self::getItemForUrl($item->folderContent, $url, $baseUrl, $result);
}
}
return $result;
}
public static function getPagingForItem($content, $item)
{
$keyPos = count($item->keyPathArray)-1;
$thisChapArray = $item->keyPathArray;
$nextItemArray = $item->keyPathArray;
$prevItemArray = $item->keyPathArray;
$item->thisChapter = false;
$item->prevItem = false;
$item->nextItem = false;
/************************
* ADD THIS CHAPTER *
************************/
if($keyPos > 0)
{
array_pop($thisChapArray);
$item->thisChapter = self::getItemWithKeyPath($content, $thisChapArray);
}
/************************
* ADD NEXT ITEM *
************************/
if($item->elementType == 'folder')
{
/* get the first element in the folder */
$item->nextItem = isset($item->folderContent[0]) ? clone($item->folderContent[0]) : false;
}
if(!$item->nextItem)
{
$nextItemArray[$keyPos]++;
$item->nextItem = self::getItemWithKeyPath($content, $nextItemArray);
}
while(!$item->nextItem)
{
array_pop($nextItemArray);
if(empty($nextItemArray)) break;
$newKeyPos = count($nextItemArray)-1;
$nextItemArray[$newKeyPos]++;
$item->nextItem = self::getItemWithKeyPath($content, $nextItemArray);
}
/************************
* ADD PREVIOUS ITEM *
************************/
if($prevItemArray[$keyPos] > 0)
{
$prevItemArray[$keyPos]--;
$item->prevItem = self::getItemWithKeyPath($content, $prevItemArray);
if($item->prevItem && $item->prevItem->elementType == 'folder' && !empty($item->prevItem->folderContent))
{
/* get last item in folder */
$item->prevItem = self::getLastItemOfFolder($item->prevItem);
}
}
else
{
$item->prevItem = $item->thisChapter;
}
if($item->prevItem && $item->prevItem->elementType == 'folder'){ unset($item->prevItem->folderContent); }
if($item->nextItem && $item->nextItem->elementType == 'folder'){ unset($item->nextItem->folderContent); }
if($item->thisChapter){unset($item->thisChapter->folderContent); }
return $item;
}
/*
* Gets a copy of an item with a key
* @param array $content with the full structure of the content as multidimensional array
* @param array $searchArray with the key as a one-dimensional array like array(0,3,4)
* @return array $item
*/
public static function getItemWithKeyPath($content, array $searchArray)
{
$item = false;
foreach($searchArray as $key => $itemKey)
{
$item = isset($content[$itemKey]) ? clone($content[$itemKey]) : false;
unset($searchArray[$key]);
if(!empty($searchArray) && $item)
{
return self::getItemWithKeyPath($item->folderContent, $searchArray);
}
}
return $item;
}
# https://www.quora.com/Learning-PHP-Is-there-a-way-to-get-the-value-of-multi-dimensional-array-by-specifying-the-key-with-a-variable
# NOT IN USE
public static function getItemWithKeyPathNew($array, array $keys)
{
$item = $array;
foreach ($keys as $key)
{
$item = isset($item[$key]->folderContent) ? $item[$key]->folderContent : $item[$key];
}
return $item;
}
/*
* Extracts an item with a key https://stackoverflow.com/questions/52097092/php-delete-value-of-array-with-dynamic-key
* @param array $content with the full structure of the content as multidimensional array
* @param array $searchArray with the key as a one-dimensional array like array(0,3,4)
* @return array $item
* NOT IN USE ??
*/
public static function extractItemWithKeyPath($structure, array $keys)
{
$result = &$structure;
$last = array_pop($keys);
foreach ($keys as $key) {
if(isset($result[$key]->folderContent))
{
$result = &$result[$key]->folderContent;
}
else
{
$result = &$result[$key];
}
}
$item = $result[$last];
unset($result[$last]);
return array('structure' => $structure, 'item' => $item);
}
# NOT IN USE
public static function deleteItemWithKeyPath($structure, array $keys)
{
$result = &$structure;
$last = array_pop($keys);
foreach ($keys as $key)
{
if(isset($result[$key]->folderContent))
{
$result = &$result[$key]->folderContent;
}
else
{
$result = &$result[$key];
}
}
$item = $result[$last];
unset($result[$last]);
return $structure;
}
/* get breadcrumb as copied array, set elements active in original and mark parent element in original */
public static function getBreadcrumb($content, $searchArray, $i = NULL, $breadcrumb = NULL)
{
# if it is the first round, create an empty array
if(!$i){ $i = 0; $breadcrumb = array();}
while($i < count($searchArray))
{
$item = $content[$searchArray[$i]];
if($i == count($searchArray)-1)
{
$item->active = true;
}
else
{
$item->activeParent = true;
}
/*
$item->active = true;
if($i == count($searchArray)-2)
{
$item->activeParent = true;
}
*/
$copy = clone($item);
if($copy->elementType == 'folder')
{
unset($copy->folderContent);
$content = $item->folderContent;
}
$breadcrumb[] = $copy;
$i++;
return self::getBreadcrumb($content, $searchArray, $i++, $breadcrumb);
}
return $breadcrumb;
}
public static function getParentItem($content, $searchArray, $iteration = NULL)
{
if(!$iteration){ $iteration = 0; }
while($iteration < count($searchArray)-2)
{
$content = $content[$searchArray[$iteration]]->folderContent;
$iteration++;
return self::getParentItem($content, $searchArray, $iteration);
}
return $content[$searchArray[$iteration]];
}
private static function getLastItemOfFolder($folder)
{
$lastItem = end($folder->folderContent);
if(is_object($lastItem) && $lastItem->elementType == 'folder' && !empty($lastItem->folderContent))
{
return self::getLastItemOfFolder($lastItem);
}
return $lastItem;
}
public static function getStringParts($name)
{
return preg_split('/[\-\.\_\=\+\?\!\*\#\(\)\/ ]/',$name);
}
public static function getFileType($fileName)
{
$parts = preg_split('/\./',$fileName);
return end($parts);
}
public static function splitFileName($fileName)
{
$parts = preg_split('/\./',$fileName);
return $parts;
}
public static function getNameWithoutType($fileName)
{
$parts = preg_split('/\./',$fileName);
return $parts[0];
}
}