1
0
mirror of https://github.com/typemill/typemill.git synced 2025-08-04 13:17:29 +02:00

Version 1.0.1

This commit is contained in:
Sebastian
2017-05-01 21:44:33 +02:00
parent 50a23a135d
commit 8ef4b30818
20 changed files with 350 additions and 128 deletions

7
.gitignore vendored
View File

@@ -1,4 +1,7 @@
cache/structure.txt
cache/request.txt
cache/sitemap.txt
cache/lastCache.txt
cache/lastSitemap.txt
settings/settings.yaml
typemill.zip
typemill.zip
typemill-1.0.1.zip

View File

@@ -13,6 +13,8 @@ RewriteRule ^(system) - [F,L]
RewriteRule ^(content) - [F,L]
RewriteRule ^(.*)?\.yml$ - [F,L]
Rewriterule ^(.*)?\.yaml$ - [F,L]
RewriteRule ^(.*)?\.txt$ - [F,L]
RewriteRule ^(.*)?\.example$ - [F,L]
RewriteRule ^(.*/)?\.git+ - [F,L]
# Use this to redirect www to non-wwww on apache servers

View File

@@ -0,0 +1,5 @@
# Update
If your version of TYPEMILL is not up to date, you will find an update notice in your footer.
To update your TYPEMILL version, simply download the latest version of TYPEMILL on ![the TYPEMILL website](http://typemill.net). Then delete the old `system` folder on your server and upload the new system folder. Sometimes it is a good idea to delete the content in the cache folder too.

View File

@@ -0,0 +1,3 @@
# Google Sitemap
As of version 1.0.1, TYPEMILL creates a google sitemap in the cache folder. You can reach the sitemap with the url `http:yourwebsite.net/cache/sitemap.xml` and add the sitemap to the google search console. The sitemap will update once a day. You can also trigger a manual update with a refresh of your browser (F5).

View File

@@ -2,6 +2,12 @@
This is the version history with some release notes.
## Version 1.0.1 (01.05.2017)
- Bugfix: Index file in the content folder won't break the building of the navigation tree anymore.
- New Feature: Added a google sitemap.xml in the cache folder.
- New Feature: Added a version check, an update message can be displayed in theme now.
## Version 1.0.0 (13.04.2017)
The first alpha version of typemill with all basic features for a simple website:

View File

@@ -2,5 +2,5 @@ title: MyWebsite
author: 'Your Name'
copyright: ©
year: '2017'
theme: robodoc
theme: typemill
startpage: true

View File

@@ -3,12 +3,15 @@
namespace System\Controllers;
use System\Models\Folder;
use System\Models\Cache;
use System\Models\WriteCache;
use System\Models\WriteSitemap;
use System\Models\WriteYaml;
use \Symfony\Component\Yaml\Yaml;
use System\Models\VersionCheck;
use System\Models\Helpers;
class PageController extends Controller
{
public function index($request, $response, $args)
{
@@ -20,30 +23,54 @@ class PageController extends Controller
$description = '';
$settings = $this->c->get('settings');
$pathToContent = $settings['rootPath'] . $settings['contentFolder'];
$cache = new Cache();
$cache = new WriteCache();
$uri = $request->getUri();
$base_url = $uri->getBaseUrl();
if($cache->validate())
try
{
$structure = $this->getCachedStructure($cache);
$cached = true;
}
else
{
$structure = $this->getFreshStructure($pathToContent, $cache, $uri);
$cached = false;
if(!$structure)
{
$content = '<h1>No Content</h1><p>Your content folder is empty.</p>';
$this->c->view->render($response, '/index.twig', [ 'content' => $content ]);
if($cache->validate('cache', 'lastCache.txt',600))
{
$structure = $this->getCachedStructure($cache);
$cached = true;
}
else
{
$structure = $this->getFreshStructure($pathToContent, $cache, $uri);
$cached = false;
if(!$structure)
{
$content = '<h1>No Content</h1><p>Your content folder is empty.</p>';
$this->c->view->render($response, '/index.twig', [ 'content' => $content ]);
}
elseif(!$cache->validate('cache', 'lastSitemap.txt', 86400))
{
/* update sitemap */
$sitemap = new WriteSitemap();
$sitemap->updateSitemap('cache', 'sitemap.xml', 'lastSitemap.txt', $structure, $uri->getBaseUrl());
$version = new VersionCheck();
$latestVersion = $version->checkVersion($uri->getBaseUrl());
if($latestVersion)
{
$yaml = new WriteYaml();
$yamlContent = $yaml->getYaml('settings', 'settings.yaml');
$yamlContent['latestVersion'] = $latestVersion;
$yaml->updateYaml('settings', 'settings.yaml', $yamlContent);
}
}
}
}
catch (Exception $e)
{
echo $e->getMessage();
exit(1);
}
/* if the user is on startpage */
if(empty($args))
{
{
/* check, if there is an index-file in the root of the content folder */
$contentMD = file_exists($pathToContent . DIRECTORY_SEPARATOR . 'index.md') ? file_get_contents($pathToContent . DIRECTORY_SEPARATOR . 'index.md') : NULL;
}
@@ -100,13 +127,11 @@ class PageController extends Controller
$this->c->view->render($response, $route, array('navigation' => $structure, 'content' => $contentHTML, 'item' => $item, 'breadcrumb' => $breadcrumb, 'settings' => $settings, 'description' => $description, 'base_url' => $base_url ));
}
protected function getCachedStructure($cache)
{
return $cache->getData('structure');
return $cache->getCache('cache', 'structure.txt');
}
protected function getFreshStructure($pathToContent, $cache, $uri)
{
@@ -120,13 +145,13 @@ class PageController extends Controller
}
/* create an array of object with the whole content of the folder */
$structure = Folder::getFolderContentDetails($structure, $uri->getBaseUrl(), $uri->getBasePath());
$structure = Folder::getFolderContentDetails($structure, $uri->getBaseUrl(), $uri->getBasePath());
/* cache navigation */
$cache->refresh($structure, 'structure');
$cache->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure);
return $structure;
}
}
}
?>

View File

@@ -1,81 +0,0 @@
<?php
namespace System\Models;
class Cache
{
private $cachePath;
public function __construct()
{
$cachePath = getcwd() . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
if(!is_dir($cachePath)){
mkdir($cachePath, 0774, true) or die('Please create a cache folder in your root and make it writable.');
}
is_writable($cachePath) or die('Your cache folder is not writable.');
$this->cachePath = $cachePath;
}
public function validate()
{
if(isset($_SERVER['HTTP_CACHE_CONTROL']) && $_SERVER['HTTP_CACHE_CONTROL'] == 'max-age=0')
{
return false;
}
$requestFile = $this->cachePath.'request.txt';
if(!file_exists($requestFile))
{
$this->writeFile($requestFile, time());
return false;
}
$lastRequest = file_get_contents($requestFile);
if(time() - $lastRequest > 600)
{
return false;
}
return true;
}
public function refresh($data, $name)
{
$sData = serialize($data);
$dataFile = $this->cachePath.$name.'.txt';
$requestFile = $this->cachePath.'request.txt';
$this->writeFile($dataFile, $sData);
$this->writeFile($requestFile, time());
}
public function getData($name)
{
if (file_exists($this->cachePath.$name.'.txt'))
{
$data = file_get_contents($this->cachePath.$name.'.txt');
$data = unserialize($data);
return $data;
}
return false;
}
public function clearData($name)
{
/* todo */
}
public function clearAll()
{
/* todo */
}
public function writeFile($file, $data)
{
$fp = fopen($file, "w");
fwrite($fp, $data);
fclose($fp);
}
}
?>

View File

@@ -40,16 +40,16 @@ class Folder
* 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, $baseUrl, $fullSlug = NULL, $fullPath = NULL, $keyPath = NULL, $chapter = NULL)
{
public static function getFolderContentDetails(array $folderContent, $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);
@@ -63,21 +63,22 @@ class Folder
$item->slug = implode("-",$nameParts);
$item->slug = URLify::filter(iconv('ISO-8859-15', 'UTF-8', $item->slug));
$item->path = $fullPath . DIRECTORY_SEPARATOR . $key;
$item->urlRel = $fullSlug . '/' . $item->slug;
$item->urlAbs = $baseUrl . $fullSlug . '/' . $item->slug;
$item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug;
$item->urlRel = $fullSlugWithFolder . '/' . $item->slug;
$item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug;
$item->key = $iteration;
$item->keyPath = $keyPath ? $keyPath . '.' . $iteration : $iteration;
$item->keyPathArray = explode('.', $item->keyPath);
$item->chapter = $chapter ? $chapter . '.' . $chapternr : $chapternr;
$item->folderContent = self::getFolderContentDetails($name, $baseUrl, $item->urlRel, $item->path, $item->keyPath, $item->chapter);
$item->folderContent = self::getFolderContentDetails($name, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter);
}
else
{
$nameParts = self::getStringParts($name);
$fileType = array_pop($nameParts);
if($name == 'index.md' || $fileType !== 'md' ) break;
if($name == 'index.md' || $fileType !== 'md' ) continue;
$item->originalName = $name;
$item->elementType = 'file';
@@ -92,8 +93,9 @@ class Folder
$item->keyPath = $keyPath . '.' . $iteration;
$item->keyPathArray = explode('.',$item->keyPath);
$item->chapter = $chapter . '.' . $chapternr;
$item->urlRel = $fullSlug . '/' . $item->slug;
$item->urlAbs = $baseUrl . $fullSlug . '/' . $item->slug;
$item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug;
$item->urlRel = $fullSlugWithFolder . '/' . $item->slug;
$item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug;
}
$iteration++;
$chapternr++;

View File

@@ -0,0 +1,33 @@
<?php
namespace System\Models;
class VersionCheck
{
function checkVersion($url)
{
$opts = array(
'http'=>array(
'method' => "GET",
'header' => "Referer: $url\r\n"
)
);
$context = stream_context_create($opts);
try {
$version = file_get_contents('http://typemill.net/tma1/checkversion', false, $context);
if ($version)
{
$version = json_decode($version);
return $version->version;
}
} catch (Exception $e) {
return false;
}
}
}
?>

61
system/Models/Write.php Normal file
View File

@@ -0,0 +1,61 @@
<?php
namespace System\Models;
class Write
{
protected $basePath;
public function __construct()
{
$basePath = getcwd() . DIRECTORY_SEPARATOR;
$this->basePath = $basePath;
}
protected function checkPath($folder)
{
$folderPath = $this->basePath . $folder;
if(!is_dir($folderPath) AND !mkdir($folderPath, 0774, true))
{
throw new Exception("The folder '{$folder}' is missing and we could not create it. Please create the folder manually on your server.");
return false;
}
if(!is_writable($folderPath))
{
throw new Exception("Please make the folder '{$folder}' writable.");
return false;
}
return true;
}
protected function checkFile($folder, $file)
{
if(!file_exists($this->basePath . $folder . DIRECTORY_SEPARATOR . $file))
{
return false;
}
return true;
}
protected function writeFile($folder, $file, $data)
{
$filePath = $this->basePath . $folder . DIRECTORY_SEPARATOR . $file;
$openFile = fopen($filePath, "w");
fwrite($openFile, $data);
fclose($openFile);
}
public function getFile($folderName, $fileName)
{
if($this->checkFile($folderName, $fileName))
{
$fileContent = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName);
return $fileContent;
}
return false;
}
}
?>

View File

@@ -0,0 +1,85 @@
<?php
namespace System\Models;
class WriteCache extends Write
{
/**
* Validates, if the cache is valid or invalid and has to be refreshed
* @param int $duration how many seconds the cache is valid.
* @return boolean for an invalid cache (false) and for a valid cache (true).
*/
public function validate($folderName, $fileName, $duration)
{
if(isset($_SERVER['HTTP_CACHE_CONTROL']) && $_SERVER['HTTP_CACHE_CONTROL'] == 'max-age=0')
{
return false;
}
if(!$this->checkPath($folderName))
{
return false;
}
if(!$this->checkFile($folderName, $fileName))
{
$this->writeFile($folderName, $fileName, time());
return false;
}
$lastRefresh = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName);
if(time() - $lastRefresh > $duration)
{
return false;
}
return true;
}
/**
* Updates a cache file.
* Serializes an object and writes it to the cache file together with a file that holds the last refresh time.
* @param object $cacheData has to be an object (e.g. navigation object).
* @param string $cacheFile has to be the name of the file you want to update (in case there are more than one cache files.
*/
public function updateCache($folderName, $cacheFileName, $requestFileName, $cacheData)
{
$sCacheData = serialize($cacheData);
$this->writeFile($folderName, $cacheFileName, $sCacheData);
if($requestFileName)
{
$this->writeFile($folderName, $requestFileName, time());
}
}
/**
* Get the recent cache.
* Takes a filename, gets the file and unserializes the cache into an object.
* @param string $fileName is the name of the cache file.
*/
public function getCache($folderName, $cacheFileName)
{
$sCacheData = $this->getFile($folderName, $cacheFileName);
if($sCacheData)
{
return unserialize($sCacheData);
}
return false;
}
/**
* @todo Create a function to clear a specific cache file
*/
public function clearCache($name)
{
}
/**
* @todo Create a function to clear all cache files
*/
public function clearAllCacheFiles()
{
}
}
?>

View File

@@ -0,0 +1,47 @@
<?php
namespace System\Models;
class WriteSitemap extends Write
{
public function updateSitemap($folderName, $sitemapFileName, $requestFileName, $data, $baseUrl)
{
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
$sitemap .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
$sitemap = $this->addUrlSet($sitemap, $baseUrl);
$sitemap .= $this->generateUrlSets($data);
$sitemap .= '</urlset>';
$this->writeFile($folderName, $sitemapFileName, $sitemap);
$this->writeFile($folderName, $requestFileName, time());
}
public function generateUrlSets($data)
{
$urlset = '';
foreach($data as $item)
{
if($item->elementType == 'folder')
{
$urlset = $this->addUrlSet($urlset, $item->urlAbs);
$urlset .= $this->generateUrlSets($item->folderContent, $urlset);
}
else
{
$urlset = $this->addUrlSet($urlset, $item->urlAbs);
}
}
return $urlset;
}
public function addUrlSet($urlset, $url)
{
$urlset .= ' <url>' . "\n";
$urlset .= ' <loc>' . $url . '</loc>' . "\n";
$urlset .= ' </url>' . "\n";
return $urlset;
}
}
?>

View File

@@ -0,0 +1,35 @@
<?php
namespace System\Models;
class WriteYaml extends Write
{
/**
* Get the a yaml file.
* @param string $fileName is the name of the Yaml Folder.
* @param string $yamlFileName is the name of the Yaml File.
*/
public function getYaml($folderName, $yamlFileName)
{
$yaml = $this->getFile($folderName, $yamlFileName);
if($yaml)
{
return \Symfony\Component\Yaml\Yaml::parse($yaml);
}
return false;
}
/**
* Writes a yaml file.
* @param string $fileName is the name of the Yaml Folder.
* @param string $yamlFileName is the name of the Yaml File.
* @param array $contentArray is the content as an array.
*/
public function updateYaml($folderName, $yamlFileName, $contentArray)
{
$yaml = \Symfony\Component\Yaml\Yaml::dump($contentArray);
$this->writeFile($folderName, $yamlFileName, $yaml);
}
}
?>

View File

@@ -1,7 +1,3 @@
<?php
/*
use App\Controllers\ApiController;
$app->get('/api', ApiController::class . ':index' )->setName('api.index');
*/
?>

View File

@@ -15,8 +15,8 @@ return [
'settingsPath' => __DIR__ . DS . '..' . DS . 'settings',
'authorPath' => __DIR__ . DS . 'author' . DS,
'contentFolder' => 'content',
'displayErrorDetails' => false,
'version' => '1.0.0'
'displayErrorDetails' => true,
'version' => '1.0.1'
],
];

View File

@@ -71,7 +71,7 @@ $container['notFoundHandler'] = function($c)
return new \System\Handlers\NotFoundHandler($c['view']);
};
require __DIR__ . '/Routes/web.php';
require __DIR__ . '/Routes/api.php';
require __DIR__ . '/Routes/web.php';
?>

View File

@@ -5,4 +5,4 @@
{% set copyrightYears = settings.year ~ ' - ' ~ nowYear %}
{% endif %}
<div class="copyrightLine"><p>{{ settings.copyright }} by {{ settings.author }}, {{ copyrightYears }}. All Rights Reserved. Built with <a href="http://typemill.net">TYPEMILL</a>.</p></div>
<div class="copyrightLine"><p>{{ settings.copyright }} by {{ settings.author }}, {{ copyrightYears }}. All Rights Reserved. Built with <a href="http://typemill.net">TYPEMILL</a>.{% if settings.version < settings.latestVersion %} Please <a href="http://typemill.com">Update</a>{% endif %}</p></div>