daux.io/libs/DauxHelper.php

393 lines
12 KiB
PHP

<?php namespace Todaymade\Daux;
use Todaymade\Daux\Exception\LinkNotFoundException;
use Todaymade\Daux\Tree\Builder;
use Todaymade\Daux\Tree\Directory;
use Todaymade\Daux\Tree\Entry;
class DauxHelper
{
/**
* Set a new base_url for the configuration.
*
* @param string $base_url
*/
public static function rebaseConfiguration(Config $config, $base_url)
{
// Avoid changing the url if it is already correct
if ($config->getBaseUrl() == $base_url && !empty($config->getTheme())) {
return;
}
// Change base url for all links on the pages
$config['base_url'] = $base_url;
$config['theme'] = static::getTheme($config, $base_url);
$config['image'] = str_replace('<base_url>', $base_url, $config->getImage());
}
/**
* @param string $current_url
*
* @return array
*/
protected static function getTheme(Config $config, $current_url)
{
$htmlTheme = $config->getHTML()->getTheme();
$theme_folder = $config->getThemesPath() . DIRECTORY_SEPARATOR . $htmlTheme;
$theme_url = $config->getBaseUrl() . 'themes/' . $htmlTheme . '/';
$theme = [];
if (is_file($theme_folder . DIRECTORY_SEPARATOR . 'config.json')) {
$theme = json_decode(file_get_contents($theme_folder . DIRECTORY_SEPARATOR . 'config.json'), true);
if (!$theme) {
$theme = [];
}
}
//Default parameters for theme
$theme += [
'name' => $htmlTheme,
'css' => [],
'js' => [],
'fonts' => [],
'favicon' => '<base_url>themes/daux/img/favicon.png',
'templates' => $theme_folder . DIRECTORY_SEPARATOR . 'templates',
'variants' => [],
];
if ($config->getHTML()->hasThemeVariant()) {
$variant = $config->getHTML()->getThemeVariant();
if (!array_key_exists($variant, $theme['variants'])) {
throw new Exception("Variant '$variant' not found for theme '$theme[name]'");
}
// These will be replaced
foreach (['templates', 'favicon'] as $element) {
if (array_key_exists($element, $theme['variants'][$variant])) {
$theme[$element] = $theme['variants'][$variant][$element];
}
}
// These will be merged
foreach (['css', 'js', 'fonts'] as $element) {
if (array_key_exists($element, $theme['variants'][$variant])) {
$theme[$element] = array_merge($theme[$element], $theme['variants'][$variant][$element]);
}
}
}
$substitutions = [
'<local_base>' => $config->getLocalBase(),
'<base_url>' => $current_url,
'<theme_url>' => $theme_url,
];
// Substitute some placeholders
$theme['templates'] = strtr($theme['templates'], $substitutions);
$theme['favicon'] = utf8_encode(strtr($theme['favicon'], $substitutions));
foreach (['css', 'js', 'fonts'] as $element) {
foreach ($theme[$element] as $key => $value) {
$theme[$element][$key] = utf8_encode(strtr($value, $substitutions));
}
}
return $theme;
}
/**
* Remove all '/./' and '/../' in a path, without actually checking the path.
*
* @param string $path
*
* @return string
*/
public static function getCleanPath($path)
{
$path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$absolutes = [];
foreach ($parts as $part) {
if ('.' == $part) {
continue;
}
if ('..' == $part) {
array_pop($absolutes);
} else {
$absolutes[] = $part;
}
}
return implode(DIRECTORY_SEPARATOR, $absolutes);
}
/**
* Get the possible output file names for a source file.
*
* @param string $part
*
* @return string[]
*/
public static function getFilenames(Config $config, $part)
{
$extensions = implode('|', array_map('preg_quote', $config->getValidContentExtensions())) . '|html';
$raw = preg_replace('/(.*)?\\.(' . $extensions . ')$/', '$1', $part);
$raw = Builder::removeSortingInformations($raw);
return ["$raw.html", $raw];
}
/**
* Locate a file in the tree. Returns the file if found or false.
*
* @param Directory $tree
* @param string $request
*
* @return false|Tree\Content|Tree\Raw
*/
public static function getFile($tree, $request)
{
$request = explode('/', $request);
foreach ($request as $node) {
// If the element we're in currently is not a
// directory, we failed to find the requested file
if (!$tree instanceof Directory) {
return false;
}
// Some relative paths may start with ./
if ($node == '.') {
continue;
}
if ($node == '..') {
$tree = $tree->getParent();
continue;
}
// if the node exists in the current request tree,
// change the $tree variable to reference the new
// node and proceed to the next url part
if (isset($tree->getEntries()[$node])) {
$tree = $tree->getEntries()[$node];
continue;
}
// We try a second time by decoding the url
$node = DauxHelper::slug(urldecode($node));
if (isset($tree->getEntries()[$node])) {
$tree = $tree->getEntries()[$node];
continue;
}
// if the node doesn't exist, we can try
// two variants of the requested file:
// with and w/o the .html extension
foreach (static::getFilenames($tree->getConfig(), $node) as $filename) {
if (isset($tree->getEntries()[$filename])) {
$tree = $tree->getEntries()[$filename];
continue 2;
}
}
// At this stage, we're in a directory, but no
// sub-item matches, so the current node must
// be an index page or we failed
if ($node !== 'index' && $node !== 'index.html') {
return false;
}
return $tree->getIndexPage();
}
// If the entry we found is not a directory, we're done
if (!$tree instanceof Directory) {
return $tree;
}
if ($index = $tree->getIndexPage()) {
return $index;
}
return false;
}
/**
* Generate a URL friendly "slug" from a given string.
*
* Taken from Stringy
*
* @param string $title
*
* @return string
*/
public static function slug($title)
{
// Convert to ASCII
if (function_exists('transliterator_transliterate')) {
$title = transliterator_transliterate('Any-Latin; NFD; [:Nonspacing Mark:] Remove; NFC;', $title);
}
$title = iconv('utf-8', 'ASCII//TRANSLIT//IGNORE', $title);
// Remove unsupported characters
$title = preg_replace('/[^\x20-\x7E]/u', '', $title);
$separator = '_';
// Convert all dashes into underscores
$title = preg_replace('![' . preg_quote('-') . ']+!u', $separator, $title);
// Remove all characters that are not valid in a URL:
// $-_.+!*'(), separator, letters, numbers, or whitespace.
$title = preg_replace('![^-' . preg_quote($separator) . '\!\'\(\),\.\+\*\$\pL\pN\s]+!u', '', $title);
// Replace all separator characters and whitespace by a single separator
$title = preg_replace('![' . preg_quote($separator) . '\s]+!u', $separator, $title);
return trim($title, $separator);
}
/**
* @param string $from
* @param string $to
*
* @return string
*/
public static function getRelativePath($from, $to)
{
// some compatibility fixes for Windows paths
$from = is_dir($from) ? rtrim($from, '\/') . '/' : $from;
$to = is_dir($to) ? rtrim($to, '\/') . '/' : $to;
$from = str_replace('\\', '/', $from);
$to = str_replace('\\', '/', $to);
$from = explode('/', $from);
$to = explode('/', $to);
$relPath = $to;
foreach ($from as $depth => $dir) {
// find first non-matching dir
if ($dir === $to[$depth]) {
// ignore this directory
array_shift($relPath);
} else {
// get number of remaining dirs to $from
$remaining = count($from) - $depth;
if ($remaining > 1) {
// add traversals up to first matching dir
$padLength = (count($relPath) + $remaining - 1) * -1;
$relPath = array_pad($relPath, $padLength, '..');
break;
}
//$relPath[0] = './' . $relPath[0];
}
}
return implode('/', $relPath);
}
public static function isAbsolutePath($path)
{
if (!is_string($path)) {
$mess = sprintf('String expected but was given %s', gettype($path));
throw new \InvalidArgumentException($mess);
}
if (!ctype_print($path)) {
$mess = 'Path can NOT have non-printable characters or be empty';
throw new \DomainException($mess);
}
// Optional wrapper(s).
$regExp = '%^(?<wrappers>(?:[[:print:]]{2,}://)*)';
// Optional root prefix.
$regExp .= '(?<root>(?:[[:alpha:]]:/|/)?)';
// Actual path.
$regExp .= '(?<path>(?:[[:print:]]*))$%';
$parts = [];
if (!preg_match($regExp, $path, $parts)) {
$mess = sprintf('Path is NOT valid, was given %s', $path);
throw new \DomainException($mess);
}
return '' !== $parts['root'];
}
public static function getAbsolutePath($path)
{
if (DauxHelper::isAbsolutePath($path)) {
return $path;
}
return getcwd() . '/' . $path;
}
public static function is($path, $type)
{
return ($type == 'dir') ? is_dir($path) : file_exists($path);
}
/**
* @param Config $config
* @param string $url
*
* @throws LinkNotFoundException
*
* @return Entry
*/
public static function resolveInternalFile($config, $url)
{
$triedAbsolute = false;
// Legacy absolute paths could start with
// "!" In this case we will try to find
// the file starting at the root
if ($url[0] == '!' || $url[0] == '/') {
$url = ltrim($url, '!/');
if ($file = DauxHelper::getFile($config->getTree(), $url)) {
return $file;
}
$triedAbsolute = true;
}
// Seems it's not an absolute path or not found,
// so we'll continue with the current folder
if ($file = DauxHelper::getFile($config->getCurrentPage()->getParent(), $url)) {
return $file;
}
// If we didn't already try it, we'll
// do a pass starting at the root
if (!$triedAbsolute && $file = DauxHelper::getFile($config->getTree(), $url)) {
return $file;
}
throw new LinkNotFoundException("Could not locate file '$url'");
}
public static function isValidUrl($url)
{
return !empty($url) && $url[0] != '#';
}
public static function isExternalUrl($url)
{
return preg_match('#^(?:[a-z]+:)?//|^mailto:#', $url);
}
}