diff --git a/.gitignore b/.gitignore index fb28210..2c30434 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ cache/structure.txt -cache/request.txt +cache/sitemap.txt +cache/lastCache.txt +cache/lastSitemap.txt settings/settings.yaml -typemill.zip \ No newline at end of file +typemill.zip +typemill-1.0.1.zip \ No newline at end of file diff --git a/.htaccess b/.htaccess index 0236112..3be0354 100644 --- a/.htaccess +++ b/.htaccess @@ -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 diff --git a/content/1_getting-started/03-update.md b/content/1_getting-started/03-update.md new file mode 100644 index 0000000..97085b4 --- /dev/null +++ b/content/1_getting-started/03-update.md @@ -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. \ No newline at end of file diff --git a/content/2_for-writers/02-mardown.md b/content/2_for-writers/05-mardown.md similarity index 100% rename from content/2_for-writers/02-mardown.md rename to content/2_for-writers/05-mardown.md diff --git a/content/2_for-writers/30-folder-structure.md b/content/2_for-writers/15-folder-structure.md similarity index 100% rename from content/2_for-writers/30-folder-structure.md rename to content/2_for-writers/15-folder-structure.md diff --git a/content/2_for-writers/20-google-sitemap.md b/content/2_for-writers/20-google-sitemap.md new file mode 100644 index 0000000..ed56979 --- /dev/null +++ b/content/2_for-writers/20-google-sitemap.md @@ -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). \ No newline at end of file diff --git a/content/4_info/01-release-notes.md b/content/4_info/01-release-notes.md index e49ea6c..c5a6b9f 100644 --- a/content/4_info/01-release-notes.md +++ b/content/4_info/01-release-notes.md @@ -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: diff --git a/settings/settings.yaml.example b/settings/settings.yaml.example index 8cfedae..8e72ef5 100644 --- a/settings/settings.yaml.example +++ b/settings/settings.yaml.example @@ -2,5 +2,5 @@ title: MyWebsite author: 'Your Name' copyright: © year: '2017' -theme: robodoc +theme: typemill startpage: true \ No newline at end of file diff --git a/system/Controllers/PageController.php b/system/Controllers/PageController.php index 831442b..44ab404 100644 --- a/system/Controllers/PageController.php +++ b/system/Controllers/PageController.php @@ -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 = '

No Content

Your content folder is empty.

'; - $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 = '

No Content

Your content folder is empty.

'; + $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; - } + } } ?> \ No newline at end of file diff --git a/system/Models/Cache.php b/system/Models/Cache.php deleted file mode 100644 index 09f0276..0000000 --- a/system/Models/Cache.php +++ /dev/null @@ -1,81 +0,0 @@ -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); - } -} - -?> \ No newline at end of file diff --git a/system/Models/Folder.php b/system/Models/Folder.php index 5d2801d..c5d69b6 100644 --- a/system/Models/Folder.php +++ b/system/Models/Folder.php @@ -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++; diff --git a/system/Models/VersionCheck.php b/system/Models/VersionCheck.php new file mode 100644 index 0000000..324305f --- /dev/null +++ b/system/Models/VersionCheck.php @@ -0,0 +1,33 @@ +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; + } + } +} + + +?> \ No newline at end of file diff --git a/system/Models/Write.php b/system/Models/Write.php new file mode 100644 index 0000000..0d6425e --- /dev/null +++ b/system/Models/Write.php @@ -0,0 +1,61 @@ +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; + } +} + +?> \ No newline at end of file diff --git a/system/Models/WriteCache.php b/system/Models/WriteCache.php new file mode 100644 index 0000000..90e71a7 --- /dev/null +++ b/system/Models/WriteCache.php @@ -0,0 +1,85 @@ +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() + { + } +} + +?> \ No newline at end of file diff --git a/system/Models/WriteSitemap.php b/system/Models/WriteSitemap.php new file mode 100644 index 0000000..4b09a0d --- /dev/null +++ b/system/Models/WriteSitemap.php @@ -0,0 +1,47 @@ +' . "\n"; + $sitemap .= '' . "\n"; + $sitemap = $this->addUrlSet($sitemap, $baseUrl); + $sitemap .= $this->generateUrlSets($data); + $sitemap .= ''; + + $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 .= ' ' . "\n"; + $urlset .= ' ' . $url . '' . "\n"; + $urlset .= ' ' . "\n"; + return $urlset; + } +} + +?> \ No newline at end of file diff --git a/system/Models/WriteYaml.php b/system/Models/WriteYaml.php new file mode 100644 index 0000000..f91dc56 --- /dev/null +++ b/system/Models/WriteYaml.php @@ -0,0 +1,35 @@ +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); + } +} + +?> \ No newline at end of file diff --git a/system/Routes/api.php b/system/Routes/api.php index a16c0d4..15c5adc 100644 --- a/system/Routes/api.php +++ b/system/Routes/api.php @@ -1,7 +1,3 @@ get('/api', ApiController::class . ':index' )->setName('api.index'); -*/ ?> \ No newline at end of file diff --git a/system/settings.php b/system/settings.php index ab74fb4..43e28fd 100644 --- a/system/settings.php +++ b/system/settings.php @@ -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' ], ]; diff --git a/system/system.php b/system/system.php index 8871c9e..06c3ff5 100644 --- a/system/system.php +++ b/system/system.php @@ -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'; ?> \ No newline at end of file diff --git a/themes/typemill/partials/footer.twig b/themes/typemill/partials/footer.twig index 79f4a3a..99665e3 100644 --- a/themes/typemill/partials/footer.twig +++ b/themes/typemill/partials/footer.twig @@ -5,4 +5,4 @@ {% set copyrightYears = settings.year ~ ' - ' ~ nowYear %} {% endif %} -

{{ settings.copyright }} by {{ settings.author }}, {{ copyrightYears }}. All Rights Reserved. Built with TYPEMILL.

\ No newline at end of file +

{{ settings.copyright }} by {{ settings.author }}, {{ copyrightYears }}. All Rights Reserved. Built with TYPEMILL.{% if settings.version < settings.latestVersion %} Please Update{% endif %}

\ No newline at end of file