Browse Source

Version 1.3.2: Rename and Hide navigation

trendschau 5 years ago
parent
commit
53f17927fd

+ 0 - 1
.gitignore

@@ -1,5 +1,4 @@
 cache
-cache/lastCache.txt
 content/index.yaml
 content/00-Welcome/index.yaml
 content/00-Welcome/00-Setup.yaml

+ 0 - 1
cache/lastCache.txt

@@ -1 +0,0 @@
-1578580890

+ 0 - 0
content/00-Welcome/00-Setup.md → content/00-welcome/00-setup.md


+ 0 - 0
content/00-Welcome/01-Write-Content.md → content/00-welcome/01-write-content.md


+ 0 - 0
content/00-Welcome/02-Get-Help.md → content/00-welcome/02-get-help.md


+ 0 - 0
content/00-Welcome/03-Markdown-Test.md → content/00-welcome/03-markdown-test.md


+ 0 - 0
content/00-Welcome/index.md → content/00-welcome/index.md


+ 12 - 9
system/Controllers/ContentApiController.php

@@ -246,9 +246,12 @@ class ContentApiController extends ContentController
 			# update the live structure
 			$this->setStructure($draft = false, $cache = false);
 
-			#update the backend structure
+			# update the backend structure
 			$this->setStructure($draft = true, $cache = false);
 
+			# check if page is in extended structure and delete it
+			$this->deleteFromExtended();
+
 			# dispatch event
 			$this->c->dispatcher->dispatch('onPageDeleted', new OnPageDeleted($this->item));
 			
@@ -324,12 +327,6 @@ class ContentApiController extends ContentController
 		$parentKeyFrom	= explode('.', $this->params['parent_id_from']);
 		$parentKeyTo	= explode('.', $this->params['parent_id_to']);
 		
-/*
-		echo '<pre>';
-		print_r(array($itemKeyPath 0,$parentKeyFrom navi,$parentKeyTo 2));
-		die();
-*/
-
 		# get the item from structure
 		$item 			= Folder::getItemWithKeyPath($this->structure, $itemKeyPath);
 
@@ -362,10 +359,16 @@ class ContentApiController extends ContentController
 			# delete item from folderContent
 			unset($folderContent[$itemKey]);
 		}
-		elseif($this->params['active'] == 'active')
+		else
 		{
+			# rename links in extended file
+			$this->renameExtended($item, $newFolder);
+
 			# an active file has been moved to another folder, so send new url with response
-			$url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $newFolder->urlRelWoF . '/' . $item->slug;
+			if($this->params['active'] == 'active')
+			{
+				$url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $newFolder->urlRelWoF . '/' . $item->slug;
+			}
 		}
 		
 		# add item to newFolder

+ 67 - 4
system/Controllers/ContentController.php

@@ -9,6 +9,7 @@ use Typemill\Models\Validation;
 use Typemill\Models\Folder;
 use Typemill\Models\Write;
 use Typemill\Models\WriteCache;
+use Typemill\Models\WriteYaml;
 
 abstract class ContentController
 {
@@ -182,7 +183,7 @@ abstract class ContentController
 		
 		# set variables and objects
 		$this->write = new writeCache();
-				
+
 		# check, if cached structure is still valid 
 		if($cache && $this->write->validate('cache', 'lastCache.txt', 600))
 		{
@@ -199,18 +200,76 @@ abstract class ContentController
 			# if there is content, then get the content details
 			if(count($structure) > 0)
 			{
-				# create an array of object with the whole content of the folder
-				$structure = Folder::getFolderContentDetails($structure, $this->uri->getBaseUrl(), $this->uri->getBasePath());
+				# get the extended structure files with changes like navigation title or hidden pages
+				$yaml = new writeYaml();
+				$extended = $yaml->getYaml('cache', 'structure-extended.yaml');
+
+				# create an array of object with the whole content of the folder and changes from extended file
+				$structure = Folder::getFolderContentDetails($structure, $extended, $this->uri->getBaseUrl(), $this->uri->getBasePath());
 			}
 			
 			# cache navigation
 			$this->write->updateCache('cache', $filename, 'lastCache.txt', $structure);
 		}
-				
+		
 		$this->structure = $structure;
 		return true;
 	}
 
+	protected function renameExtended($item, $newFolder)
+	{
+		# get the extended structure files with changes like navigation title or hidden pages
+		$yaml = new writeYaml();
+		$extended = $yaml->getYaml('cache', 'structure-extended.yaml');
+
+		if(isset($extended[$item->urlRelWoF]))
+		{
+			$newUrl = $newFolder->urlRelWoF . '/' . $item->slug;
+
+			$entry = $extended[$item->urlRelWoF];
+			
+			unset($extended[$item->urlRelWoF]);
+			
+			$extended[$newUrl] = $entry;
+			$yaml->updateYaml('cache', 'structure-extended.yaml', $extended);
+		}
+
+		return true;
+	}
+
+	protected function deleteFromExtended()
+	{
+		# get the extended structure files with changes like navigation title or hidden pages
+		$yaml = new writeYaml();
+		$extended = $yaml->getYaml('cache', 'structure-extended.yaml');
+
+		if($this->item->elementType == "file" && isset($extended[$this->item->urlRelWoF]))
+		{
+			unset($extended[$this->item->urlRelWoF]);
+			$yaml->updateYaml('cache', 'structure-extended.yaml', $extended);
+		}
+
+		if($this->item->elementType == "folder")
+		{
+			$changed = false;
+
+			# delete all entries with that folder url
+			foreach($extended as $url => $entries)
+			{
+				if( strpos($url, $this->item->urlRelWoF) !== false )
+				{
+					$changed = true;
+					unset($extended[$url]);
+				}
+			}
+
+			if($changed)
+			{
+				$yaml->updateYaml('cache', 'structure-extended.yaml', $extended);
+			}
+		}
+	}
+
 	protected function setHomepage()
 	{
 		$contentFolder = Folder::scanFolderFlat($this->settings['rootPath'] . $this->settings['contentFolder']);
@@ -251,6 +310,7 @@ abstract class ContentController
 		}
 
 		$this->errors = ['errors' => ['message' => 'requested page-url not found']];
+
 		return false;
 	}
 	
@@ -334,6 +394,9 @@ abstract class ContentController
 				}
 				return rmdir($path);
 			}
+
+			# delete all files from the extended file
+			$this->deleteFromExtended();
 		}
 		return false;
 	}

+ 72 - 12
system/Controllers/MetaApiController.php

@@ -5,6 +5,7 @@ namespace Typemill\Controllers;
 use Slim\Http\Request;
 use Slim\Http\Response;
 use Typemill\Models\WriteYaml;
+use Typemill\Models\Folder;
 
 class MetaApiController extends ContentController
 {
@@ -23,10 +24,6 @@ class MetaApiController extends ContentController
 
 		$metatabs = $writeYaml->getYaml('system' . DIRECTORY_SEPARATOR . 'author', 'metatabs.yaml');
 
-		# load cached metadefinitions
-		# check if valid
-		# if not, refresh cache
-
 		# loop through all plugins
 		foreach($this->settings['plugins'] as $name => $plugin)
 		{
@@ -39,6 +36,14 @@ class MetaApiController extends ContentController
 				}
 			}
 		}
+		
+		# add the meta from theme settings here
+		$themeSettings = \Typemill\Settings::getObjectSettings('themes', $this->settings['theme']);
+		
+		if($themeSettings && isset($themeSettings['metatabs']))
+		{
+			$metatabs = array_merge_recursive($metatabs, $themeSettings['metatabs']);
+		}
 
 		return $metatabs;
 	}
@@ -77,32 +82,37 @@ class MetaApiController extends ContentController
 		$metadefinitions = $this->aggregateMetaDefinitions();
 		
 		$metadata = [];
+		$metascheme = [];
 
 		foreach($metadefinitions as $tabname => $tab )
 		{
-			$metadata[$tabname] = [];
+			$metadata[$tabname] 	= [];
 
 			foreach($tab['fields'] as $fieldname => $fielddefinitions)
 			{
+				$metascheme[$tabname][$fieldname] = true;
 				$metadata[$tabname][$fieldname] = isset($pagemeta[$tabname][$fieldname]) ? $pagemeta[$tabname][$fieldname] : null;
 			}
 		}
 
+		# store the metascheme in cache for frontend
+		$writeYaml->updateYaml('cache', 'metatabs.yaml', $metascheme);
+
 		return $response->withJson(array('metadata' => $metadata, 'metadefinitions' => $metadefinitions, 'errors' => false));
 	}
 
 	public function updateArticleMeta(Request $request, Response $response, $args)
 	{
-		/* get params from call */
+		# get params from call
 		$this->params 	= $request->getParams();
 		$this->uri 		= $request->getUri();
 
 		$tab 			= isset($this->params['tab']) ? $this->params['tab'] : false;
-		$metaData		= isset($this->params['data']) ? $this->params['data'] : false ;
+		$metaInput		= isset($this->params['data']) ? $this->params['data'] : false ;
 		$objectName		= 'meta';
 		$errors 		= false;
 
-		if(!$tab or !$metaData)
+		if(!$tab or !$metaInput)
 		{
 			return $response->withJson($this->errors, 404);
 		}
@@ -114,7 +124,7 @@ class MetaApiController extends ContentController
 		$validate 		= $this->getValidator();
 
 		# take the user input data and iterate over all fields and values
-		foreach($metaData as $fieldName => $fieldValue)
+		foreach($metaInput as $fieldName => $fieldValue)
 		{
 			# get the corresponding field definition from original plugin settings */
 			$fieldDefinition = isset($metaDefinitions[$tab]['fields'][$fieldName]) ? $metaDefinitions[$tab]['fields'][$fieldName] : false;
@@ -147,16 +157,66 @@ class MetaApiController extends ContentController
 		$writeYaml = new writeYaml();
 
 		# get existing metadata for page
-		$meta = $writeYaml->getYaml($this->settings['contentFolder'], $this->item->pathWithoutType . '.yaml');
+		$metaPage = $writeYaml->getYaml($this->settings['contentFolder'], $this->item->pathWithoutType . '.yaml');
+		
+		# get extended structure
+		$extended = $writeYaml->getYaml('cache', 'structure-extended.yaml');
+
+		# flag for changed structure
+		$structure = false;
+
+		if($tab == 'meta')
+		{
+			# normalize the meta-input
+			$metaInput['navtitle'] 	= (isset($metaInput['navtitle']) && $metaInput['navtitle'] !== null )? $metaInput['navtitle'] : '';
+			$metaInput['hide'] 		= (isset($metaInput['hide']) && $metaInput['hide'] !== null) ? $metaInput['hide'] : false;
+
+			# input values are empty but entry in structure exists
+			if(!$metaInput['hide'] && $metaInput['navtitle'] == "" && isset($extended[$this->item->urlRelWoF]))
+			{
+				# delete the entry in the structure
+				unset($extended[$this->item->urlRelWoF]);
+
+				$structure = true;
+			}
+
+			# check if navtitle or hide-value has been changed
+			elseif(
+				($metaPage['meta']['navtitle'] != $metaInput['navtitle']) 
+				OR 
+				($metaPage['meta']['hide'] != $metaInput['hide'])
+			)
+			{
+				# add new file data. Also makes sure that the value is set.
+				$extended[$this->item->urlRelWoF] = ['hide' => $metaInput['hide'], 'navtitle' => $metaInput['navtitle']];
+
+				$structure = true;
+			}
+
+			if($structure)
+			{
+				# store the file
+				$writeYaml->updateYaml('cache', 'structure-extended.yaml', $extended);
+
+				# recreate the draft structure
+				$this->setStructure($draft = true, $cache = false);
+
+				# set item in navigation active again
+				$activeItem = Folder::getItemForUrl($this->structure, $this->item->urlRel, $this->uri->getBaseUrl());
+
+				# send new structure to frontend
+				$structure = $this->structure;
+			}
+		}
 
 		# add the new/edited metadata
-		$meta[$tab] = $metaData;
+		$meta[$tab] = $metaInput;
 
 		# store the metadata
 		$writeYaml->updateYaml($this->settings['contentFolder'], $this->item->pathWithoutType . '.yaml', $meta);
 
 		# return with the new metadata
-		return $response->withJson(array('metadata' => $metaData, 'errors' => false));
+		return $response->withJson(array('metadata' => $metaInput, 'structure' => $structure, 'errors' => false));
 	}
 }
 

+ 70 - 9
system/Controllers/PageController.php

@@ -40,7 +40,7 @@ class PageController extends Controller
 		try
 		{
 			/* if the cached structure is still valid, use it */
-			if($cache->validate('cache', 'lastCache.txt',600))
+			if($cache->validate('cache', 'lastCache.txt', 600))
 			{
 				$structure	= $this->getCachedStructure($cache);
 			}
@@ -75,7 +75,15 @@ class PageController extends Controller
 			echo $e->getMessage();
 			exit(1);
 		}
-		
+
+		# get the cached navigation here (structure without hidden files )
+		$navigation = $cache->getCache('cache', 'navigation.txt');
+		if(!$navigation)
+		{
+			# use the structure as navigation if there is no difference
+			$navigation = $structure;
+		}
+
 		# if the user is on startpage
 		if(empty($args))
 		{
@@ -93,7 +101,7 @@ class PageController extends Controller
 			/* if there is still no item, return a 404-page */
 			if(!$item)
 			{
-				return $this->render404($response, array( 'navigation' => $structure, 'settings' => $settings,  'base_url' => $base_url )); 
+				return $this->render404($response, array( 'navigation' => $navigation, 'settings' => $settings,  'base_url' => $base_url )); 
 			}
 
 			/* get breadcrumb for page */
@@ -124,7 +132,6 @@ class PageController extends Controller
 		
 		# get meta-Information
 		$writeYaml 		= new WriteYaml();
-
 		$metatabs 		= $writeYaml->getPageMeta($settings, $item);
 
 		if(!$metatabs)
@@ -196,12 +203,19 @@ class PageController extends Controller
 		$theme = $settings['theme'];
 		$route = empty($args) && isset($settings['themes'][$theme]['cover']) ? '/cover.twig' : '/index.twig';
 
+		# check if there is a custom theme css
+		$customcss = $writeYaml->checkFile('cache', $theme . '-custom.css');
+		if($customcss)
+		{
+			$this->c->assets->addCSS($base_url . '/cache/' . $theme . '-custom.css');
+		}
+
 		return $this->render($response, $route, [
 			'home'			=> $home,
-			'navigation' 	=> $structure, 
-			'title' 		=> $title,			
+			'navigation' 	=> $navigation, 
+			'title' 		=> $title,
 			'content' 		=> $contentHTML, 
-			'item' 			=> $item, 
+			'item' 			=> $item,
 			'breadcrumb' 	=> $breadcrumb, 
 			'settings' 		=> $settings,
 			'metatabs'		=> $metatabs,
@@ -225,15 +239,62 @@ class PageController extends Controller
 			return false;
 		}
 
+		# get the extended structure files with changes like navigation title or hidden pages
+		$yaml = new writeYaml();
+		$extended = $yaml->getYaml('cache', 'structure-extended.yaml');
+
 		/* create an array of object with the whole content of the folder */
-		$structure = Folder::getFolderContentDetails($structure, $uri->getBaseUrl(), $uri->getBasePath());		
+		$structure = Folder::getFolderContentDetails($structure, $extended, $uri->getBaseUrl(), $uri->getBasePath());
 
-		/* cache navigation */
+		/* cache structure */
 		$cache->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure);
+
+		if($this->containsHiddenPages($extended))
+		{
+			# generate the navigation (delete empty pages)
+			$navigation = $this->createNavigationFromStructure($structure);
+
+			# cache navigation
+			$cache->updateCache('cache', 'navigation.txt', false, $navigation);
+		}
+		else
+		{
+			# make sure no separate navigation file is set
+			$cache->deleteFileWithPath('cache' . DIRECTORY_SEPARATOR . 'navigation.txt');
+		}
 		
 		return $structure;
 	}
 	
+	protected function containsHiddenPages($extended)
+	{
+		foreach($extended as $element)
+		{
+			if(isset($element['hide']) && $element['hide'] === true)
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+
+	protected function createNavigationFromStructure($navigation)
+	{
+		foreach ($navigation as $key => $element)
+		{
+			if($element->hide === true)
+			{
+				unset($navigation[$key]);
+			}
+			elseif(isset($element->folderContent))
+			{
+				$navigation[$key]->folderContent = $this->createNavigationFromStructure($element->folderContent);
+			}
+		}
+		
+		return $navigation;
+	}
+
 	protected function updateVersion($baseUrl)
 	{
 		/* check the latest public typemill version */

+ 53 - 8
system/Controllers/SettingsController.php

@@ -3,6 +3,7 @@
 namespace Typemill\Controllers;
 
 use \Symfony\Component\Yaml\Yaml;
+use Typemill\Models\Write;
 use Typemill\Models\Fields;
 use Typemill\Models\Validation;
 use Typemill\Models\User;
@@ -91,7 +92,7 @@ class SettingsController extends Controller
 	
 	public function showThemes($request, $response, $args)
 	{
-		$userSettings 	= $this->c->get('settings');
+		$userSettings 	= $this->c->get('settings');		
 		$themes 		= $this->getThemes();
 		$themedata		= array();
 		$fieldsModel	= new Fields();
@@ -109,24 +110,40 @@ class SettingsController extends Controller
 			}
 
 			$themeSettings = \Typemill\Settings::getObjectSettings('themes', $themeName);
+
+			# add standard-textarea for custom css
+			$themeSettings['forms']['fields']['customcss'] = ['type' => 'textarea', 'label' => 'Custom CSS', 'rows' => 10, 'class' => 'codearea', 'description' => 'You can overwrite the theme-css with your own css here.'];
+
+			# load custom css-file
+			$write = new write();
+			$customcss = $write->getFile('cache', $themeName . '-custom.css');
+			$themeSettings['settings']['customcss'] = $customcss;
+
+
 			if($themeSettings)
 			{
 				/* store them as default theme data with author, year, default settings and field-definitions */
 				$themedata[$themeName] = $themeSettings;
 			}
-						
+			
 			if(isset($themeSettings['forms']['fields']))
 			{
-				$fields = $fieldsModel->getFields($userSettings, 'themes', $themeName, $themeSettings);				
-								
+				$fields = $fieldsModel->getFields($userSettings, 'themes', $themeName, $themeSettings);
+
 				/* overwrite original theme form definitions with enhanced form objects */
 				$themedata[$themeName]['forms']['fields'] = $fields;
 			}
 			
 			/* add the preview image */
-			$img = getcwd() . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $themeName . DIRECTORY_SEPARATOR . $themeName . '.jpg';
-			$img = file_exists($img) ? $img : false;
-				
+			$img = getcwd() . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $themeName . DIRECTORY_SEPARATOR . $themeName;
+			$jpg = $img . '.jpg';
+			$png = $img . '.png';
+			$img = file_exists($jpg) ? $jpg : false;
+			if(!$img)
+			{
+				$img = file_exists($png) ? $png : false;
+			}
+
 			$themedata[$themeName]['img'] = $img;
 		}
 		
@@ -134,7 +151,7 @@ class SettingsController extends Controller
 		$user		= new User();
 		$users		= $user->getUsers();
 		$route 		= $request->getAttribute('route');
-		
+
 		return $this->render($response, 'settings/themes.twig', array('settings' => $userSettings, 'themes' => $themedata, 'users' => $users, 'route' => $route->getName() ));
 	}
 	
@@ -245,6 +262,34 @@ class SettingsController extends Controller
 			/* set theme name and delete theme settings from user settings for the case, that the new theme has no settings */
 			$userSettings['theme'] = $themeName;
 
+			# extract the custom css from user input
+			$customcss = isset($userInput['customcss']) ? $userInput['customcss'] : false;
+
+			# delete custom css from userinput
+			unset($userInput['customcss']);
+
+			$write = new write();
+
+			# make sure no file is set if there is no custom css
+			if(!$customcss OR $customcss == '')
+			{
+				# delete the css file if exists
+				$write->deleteFileWithPath('cache' . DIRECTORY_SEPARATOR . $themeName . '-custom.css');
+			}
+			else
+			{
+				if ( $customcss != strip_tags($customcss) )
+				{
+					$_SESSION['errors'][$themeName]['customcss'][] = 'custom css contains html';
+				}
+				else
+				{
+					# store css
+					$write = new write();
+					$write->writeFile('cache', $themeName . '-custom.css', $customcss);
+				}
+			}
+
 			if($userInput)
 			{
 				/* validate the user-input */

+ 46 - 5
system/Models/Folder.php

@@ -16,7 +16,7 @@ class Folder
 	{
 		$folderItems 	= scandir($folderPath);
 		$folderContent 	= array();
-		
+
 		foreach ($folderItems as $key => $item)
 		{
 			if (!in_array($item, array(".","..")))
@@ -96,8 +96,8 @@ 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, $fullSlugWithFolder = NULL, $fullSlugWithoutFolder = NULL, $fullPath = NULL, $keyPath = NULL, $chapter = NULL)
-	{	
+	public static function getFolderContentDetails(array $folderContent, $extended, $baseUrl, $fullSlugWithFolder = NULL, $fullSlugWithoutFolder = NULL, $fullPath = NULL, $keyPath = NULL, $chapter = NULL)
+	{
 		$contentDetails 	= [];
 		$iteration 			= 0;
 		$chapternr 			= 1;
@@ -147,8 +147,16 @@ class Folder
 				$item->chapter			= $chapter ? $chapter . '.' . $chapternr : $chapternr;
 				$item->active			= false;
 				$item->activeParent		= false;
+				$item->hide 			= false;
 
-				$item->folderContent 	= self::getFolderContentDetails($name, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter);
+				# 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;
+				}
+
+				$item->folderContent = self::getFolderContentDetails($name, $extended, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter);
 			}
 			else
 			{
@@ -196,6 +204,14 @@ class Folder
 				$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++;
@@ -223,9 +239,9 @@ class Folder
 
 		foreach($folderContentDetails as $key => $item)
 		{
+			# set item active, needed to move item in navigation
 			if($item->urlRel === $url)
 			{
-				# set item active, needed for move item in navigation
 				$item->active = true;
 				$result = $item;
 			}
@@ -234,6 +250,7 @@ class Folder
 				$result = self::getItemForUrl($item->folderContent, $url, $baseUrl, $result);
 			}
 		}
+
 		return $result;
 	}
 
@@ -378,6 +395,30 @@ class Folder
 		
 		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)

+ 0 - 1
system/Models/Helpers.php

@@ -23,6 +23,5 @@ class Helpers{
 		}
 		$table .= '</table></body></html>';
 		echo $table;
-		exit;
 	}
 }

+ 8 - 6
system/Models/Write.php

@@ -41,7 +41,7 @@ class Write
 		return true;
 	}
 	
-	protected function checkFile($folder, $file)
+	public function checkFile($folder, $file)
 	{
 		if(!file_exists($this->basePath . $folder . DIRECTORY_SEPARATOR . $file))
 		{
@@ -50,7 +50,7 @@ class Write
 		return true;
 	}
 
-	protected function checkFileWithPath($filepath)
+	public function checkFileWithPath($filepath)
 	{
 		if(!file_exists($this->basePath . $filepath))
 		{
@@ -62,7 +62,7 @@ class Write
 	public function writeFile($folder, $file, $data)
 	{
 		if($this->checkPath($folder))
-		{
+		{			
 			$filePath 	= $this->basePath . $folder . DIRECTORY_SEPARATOR . $file;
 			
 			$openFile 	= @fopen($filePath, "w");
@@ -70,11 +70,11 @@ class Write
 			if(!$openFile)
 			{
 				return false;
-			}
+			}			
 			
 			fwrite($openFile, $data);
 			fclose($openFile);
-			
+
 			return true;
 		}
 		return false;
@@ -118,8 +118,10 @@ class Write
 		$newOrder			= ($index < 10) ? '0' . $index : $index;
 
 		# create new path with foldername or filename but without file-type
-		$newPath 			= $this->basePath . 'content' . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . str_replace(" ", "-", $item->name);
+		# $newPath 			= $this->basePath . 'content' . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . str_replace(" ", "-", $item->name);
 		
+		$newPath 			= $this->basePath . 'content' . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . $item->slug;
+
 		if($item->elementType == 'folder')
 		{
 			$oldPath = $this->basePath . 'content' . $item->path;

+ 2 - 2
system/Models/WriteCache.php

@@ -10,7 +10,7 @@ class WriteCache extends Write
 	 * @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;
@@ -28,7 +28,7 @@ class WriteCache extends Write
 		}
 		
 		$lastRefresh = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName);
-		
+
 		if(time() - $lastRefresh > $duration)
 		{
 			return false;

+ 29 - 0
system/Models/WriteYaml.php

@@ -48,6 +48,14 @@ class WriteYaml extends Write
 			return false;
 		}
 
+		# compare with meta that are in use right now (e.g. changed theme, disabled plugin)
+		$metascheme = $this->getYaml('cache', 'metatabs.yaml');
+
+		if($metascheme)
+		{
+			$meta = $this->whitelistMeta($meta,$metascheme);
+		}
+
 		$meta = $this->addFileTimeToMeta($meta, $item, $settings);
 
 		return $meta;
@@ -117,6 +125,27 @@ class WriteYaml extends Write
 		return $meta;
 	}
 
+
+	private function whitelistMeta($meta, $metascheme)
+	{
+		# we have only 2 dimensions, so no recursive needed
+		foreach($meta as $tab => $values)
+		{
+			if(!isset($metascheme[$tab]))
+			{
+				unset($meta[$tab]);
+			}
+			foreach($values as $key => $value)
+			{
+				if(!isset($metascheme[$tab][$key]))
+				{
+					unset($meta[$tab][$key]);
+				}
+			}
+		}
+		return $meta;
+	}
+
 	private function addFileTimeToMeta($meta, $item, $settings)
 	{
 		$filePath = $settings['contentFolder'] . $item->path;

+ 14 - 14
system/Settings.php

@@ -16,11 +16,10 @@ class Settings
 			$settings 			= array_merge($defaultSettings, $userSettings);
 		}
 
-    // i18n
-    // load the strings of the set language
-    $language = $settings['language'];
-    $settings['labels'] = self::getLanguageLabels($language);
-    
+	    # i18n
+    	# load the strings of the set language
+    	$language = $settings['language'];
+    	$settings['labels'] = self::getLanguageLabels($language);
 
 		# We know the used theme now so create the theme path 
 		$settings['themePath'] = $settings['rootPath'] . $settings['themeFolder'] . DIRECTORY_SEPARATOR . $settings['theme'];
@@ -77,15 +76,16 @@ class Settings
 	}
 
 
-    // i18n
-  public static function getLanguageLabels($language)
+    # i18n
+ 	public static function getLanguageLabels($language)
 	{
-    // if not present, set the English language
-    if( empty($language) ){
-      $language = 'en';
-    }
+    	# if not present, set the English language
+    	if( empty($language) )
+    	{
+      		$language = 'en';
+    	}
 
-    // load the strings of the set language
+    	# load the strings of the set language
 		$yaml = new Models\WriteYaml();
 		$labels = $yaml->getYaml('settings/languages', $language.'.yaml');
 		
@@ -124,7 +124,7 @@ class Settings
 		if($userSettings)
 		{
 			# whitelist settings that can be stored in usersettings (values are not relevant here, only keys)			
-			$allowedUserSettings = ['displayErrorDetails' => false,
+			$allowedUserSettings = ['displayErrorDetails' => true,
 									'title' => false,
 									'copyright' => false,
 									'language' => false,
@@ -151,7 +151,7 @@ class Settings
 			# merge usersettings with new settings
 			$settings 	= array_merge($userSettings, $settings);
 
-			/* write settings to yaml */
+			# write settings to yaml
 			$yaml = new Models\WriteYaml();
 			$yaml->updateYaml('settings', 'settings.yaml', $settings);					
 		}

+ 10 - 2
system/author/css/style.css

@@ -411,7 +411,6 @@ footer{
 	margin: 0 0 20px 0;
 }
 
-
 /************************
 *  	OPEN-CLOSE BUTTON	*
 ************************/
@@ -972,6 +971,15 @@ ul.cardInfo{
 .cardField input, .cardField select, .cardField textarea{
 	background-color: #FFF;
 }
+textarea.codearea, .cardField textarea.codearea{
+    font-family: monospace;
+    background: #333;
+    color: #fff;
+}
+textarea.codearea:focus, .cardField textarea.codearea:focus{
+	border: 1px solid #333;
+}
+
 .cardFields fieldset.subfield{
 	padding: 20px;
 	border: 1px solid #ddd;
@@ -1018,7 +1026,7 @@ span.error{
 	line-height: 1em;
 }
 .cardInner span.error{
-	display: block;	
+	display: inline-block;	
 	margin-top: -8px;
 	margin-bottom: 8px;	
 }

+ 4 - 0
system/author/js/vue-meta.js

@@ -439,6 +439,10 @@ let meta = new Vue({
 	        .then(function (response) {
 	        	self.saved = true;
 	        	self.formErrors = self.formErrorsReset;
+	        	if(response.data.structure)
+	        	{
+	        		navi.items = response.data.structure;
+	        	}
 	        })
 	        .catch(function (error)
 	        {

+ 11 - 3
system/author/js/vue-navi.js

@@ -1,6 +1,6 @@
 const navcomponent = Vue.component('navigation', {
 	template: '#navigation-template',
-	props: ['homepage', 'showForm', 'name', 'newItem', 'parent', 'active', 'filetype', 'status', 'elementtype', 'element', 'folder', 'level', 'url', 'root', 'freeze'],
+	props: ['homepage', 'showForm', 'name', 'hide', 'newItem', 'parent', 'active', 'filetype', 'status', 'elementtype', 'element', 'folder', 'level', 'url', 'root', 'freeze'],
 	data: function () {
 		return {
 			showForm: false,
@@ -88,8 +88,12 @@ const navcomponent = Vue.component('navigation', {
 			level = level.split('.').length;
 			return 'level-' + level;
 		},
-		getIcon : function(elementtype, filetype)
+		getIcon : function(elementtype, filetype, hide)
 		{
+			if(hide)
+			{
+				return '#icon-eye-blocked';
+			}
 			if(elementtype == 'file')
 			{
 				return '#icon-file-text-o';
@@ -99,8 +103,12 @@ const navcomponent = Vue.component('navigation', {
 				return '#icon-folder-o';
 			}
 		},
-		getIconClass : function(elementtype, filetype)
+		getIconClass : function(elementtype, filetype, hide)
 		{
+			if(hide)
+			{
+				return 'icon-eye-blocked ' + filetype;
+			}
 			if(elementtype == 'file')
 			{
 				return 'icon-file-text-o ' + filetype;

+ 6 - 0
system/author/layouts/layoutBlox.twig

@@ -142,6 +142,12 @@
 					<title>cross</title>
 					<path d="M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"></path>
 				</symbol>
+				<symbol id="icon-eye-blocked" viewBox="0 0 32 32">
+					<title>eye-blocked</title>
+					<path d="M29.561 0.439c-0.586-0.586-1.535-0.586-2.121 0l-6.318 6.318c-1.623-0.492-3.342-0.757-5.122-0.757-6.979 0-13.028 4.064-16 10 1.285 2.566 3.145 4.782 5.407 6.472l-4.968 4.968c-0.586 0.586-0.586 1.535 0 2.121 0.293 0.293 0.677 0.439 1.061 0.439s0.768-0.146 1.061-0.439l27-27c0.586-0.586 0.586-1.536 0-2.121zM13 10c1.32 0 2.44 0.853 2.841 2.037l-3.804 3.804c-1.184-0.401-2.037-1.521-2.037-2.841 0-1.657 1.343-3 3-3zM3.441 16c1.197-1.891 2.79-3.498 4.67-4.697 0.122-0.078 0.246-0.154 0.371-0.228-0.311 0.854-0.482 1.776-0.482 2.737 0 1.715 0.54 3.304 1.459 4.607l-1.904 1.904c-1.639-1.151-3.038-2.621-4.114-4.323z"></path>
+					<path d="M24 13.813c0-0.849-0.133-1.667-0.378-2.434l-10.056 10.056c0.768 0.245 1.586 0.378 2.435 0.378 4.418 0 8-3.582 8-8z"></path>
+					<path d="M25.938 9.062l-2.168 2.168c0.040 0.025 0.079 0.049 0.118 0.074 1.88 1.199 3.473 2.805 4.67 4.697-1.197 1.891-2.79 3.498-4.67 4.697-2.362 1.507-5.090 2.303-7.889 2.303-1.208 0-2.403-0.149-3.561-0.439l-2.403 2.403c1.866 0.671 3.873 1.036 5.964 1.036 6.978 0 13.027-4.064 16-10-1.407-2.81-3.504-5.2-6.062-6.938z"></path>
+				</symbol>				
 				{{ assets.renderSvg() }}
 			</defs>
 		</svg>

+ 30 - 1
system/author/metatabs.yaml

@@ -29,4 +29,33 @@ meta:
       type: date
       label: Created at (readonly)
       readonly: readonly
-      class: medium
+      class: medium
+    navtitle:
+      type: text
+      label: Navigation Title
+      class: medium
+      maxlength: 60
+    hide:
+      type: checkbox
+      label: Hide
+      checkboxlabel: Hide page from navigation
+      class: medium
+#    roles:
+#      type: select
+#      label: Show page to
+#      class: medium
+#      options:
+#        null: null
+#        public: Public (standard)
+#        members: Members only (logged in)
+#        customers: Customers only (paying)
+#    list:
+#      type: select
+#      label: List sub-pages 
+#      class: medium
+#      options:
+#        null: null
+#        standard: Standard order
+#        revert: Reverse order
+#        asc: Date ascending
+#        desc: Date descending

+ 6 - 4
system/author/partials/editorNavi.twig

@@ -20,11 +20,12 @@
 				v-for="item in items" 
 				ref="draggit" 
 				:freeze="freeze" 
-				:name="item.name" 
+				:name="item.name"
+				:hide="item.hide"
 				:active="item.active" 
 				:parent="item.activeParent" 
-				:level="item.keyPath" 
-				:root="root" 
+				:level="item.keyPath"
+				:root="root"
 				:url="item.urlRelWoF" 
 				:id="item.keyPath" 
 				:key="item.keyPath" 
@@ -57,7 +58,7 @@
 	<template id="navigation-template">
 		<li class="navi-item" :class="elementtype">
 			<div class="status" :class="status"></div>
-			<a v-bind:href="getUrl(root, url)" :class="checkActive(active,parent)"><span class="iconwrapper"><svg class="icon" :class="getIconClass(elementtype, filetype)"><use :xlink:href="getIcon(elementtype, filetype)"></use></svg></span><span :class="getLevel(level)">{{ name }}</span><span class="movewrapper"><span class="movewrapper"><svg class="icon icon-arrows-v"><use xlink:href="#icon-arrows-v"></use></svg></span></a>
+			<a v-bind:href="getUrl(root, url)" :class="checkActive(active,parent)"><span class="iconwrapper"><svg class="icon" :class="getIconClass(elementtype, filetype, hide)"><use :xlink:href="getIcon(elementtype, filetype, hide)"></use></svg></span><span :class="getLevel(level)">{{ name }}</span><span class="movewrapper"><span class="movewrapper"><svg class="icon icon-arrows-v"><use xlink:href="#icon-arrows-v"></use></svg></span></a>
 			<draggable v-if="folder" class="navi-list" tag="ul" 
 				@start="onStart" 
 				@end="onEnd" 
@@ -71,6 +72,7 @@
 					ref="draggit" 
 					:freeze="freeze" 
 					:name="item.name" 
+					:hide="item.hide"
 					:active="item.active" 
 					:parent="item.activeParent" 
 					:level="item.keyPath" 

+ 1 - 1
themes/typemill/cover.twig

@@ -9,7 +9,7 @@
 		{{ content }}
 		
 		<div class="actionLink">
-			<a href="{{ navigation[0].urlRel }}">{{ settings.themes.typemill.start ? __(settings.themes.typemill.start) : __('Start')}}</a>
+			<a href="{{ navigation[0].urlRel }}">{{ settings.themes.typemill.start ? settings.themes.typemill.start : __('Start')}}</a>
 			
 			{% if settings.setup  %}
 				<a href="{{ base_url }}/setup">{{ __('Setup') }}</a>

+ 1 - 1
themes/typemill/typemill.yaml

@@ -1,5 +1,5 @@
 name: Typemill Theme
-version: 1.2.1
+version: 1.2.2
 description: The standard theme for Typemill. Responsive, minimal and without any dependencies. It uses the system fonts Calibri and Helvetica. No JavaScript is used. 
 author: Sebastian Schürmanns
 homepage: https://typemill.net