Browse Source

Version 1.0.1

Sebastian 8 years ago
parent
commit
8ef4b30818

+ 5 - 2
.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
+typemill.zip
+typemill-1.0.1.zip

+ 2 - 0
.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

+ 5 - 0
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. 

+ 0 - 0
content/2_for-writers/02-mardown.md → content/2_for-writers/05-mardown.md


+ 0 - 0
content/2_for-writers/30-folder-structure.md → content/2_for-writers/15-folder-structure.md


+ 3 - 0
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).

+ 6 - 0
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:
 

+ 1 - 1
settings/settings.yaml.example

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

+ 50 - 25
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;
+			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);
+					}
+				}
+			}
 		}
-		else
+		catch (Exception $e)
 		{
-			$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 ]);
-			}
+			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;
-	}	
+	}
 }
 
 ?>

+ 0 - 81
system/Models/Cache.php

@@ -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);
-	}
-}
-
-?>

+ 12 - 10
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++;

+ 33 - 0
system/Models/VersionCheck.php

@@ -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 - 0
system/Models/Write.php

@@ -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;
+	}
+}
+
+?>

+ 85 - 0
system/Models/WriteCache.php

@@ -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()
+	{
+	}
+}
+
+?>

+ 47 - 0
system/Models/WriteSitemap.php

@@ -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;
+	}
+}
+
+?>

+ 35 - 0
system/Models/WriteYaml.php

@@ -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);
+	}
+}
+
+?>

+ 0 - 4
system/Routes/api.php

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

+ 2 - 2
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'
     ],
 ];
 

+ 1 - 1
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';
 
 ?>

+ 1 - 1
themes/typemill/partials/footer.twig

@@ -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>