diff --git a/system/Controllers/ArticleApiController.php b/system/Controllers/ArticleApiController.php index 5ed5e19..034260a 100644 --- a/system/Controllers/ArticleApiController.php +++ b/system/Controllers/ArticleApiController.php @@ -7,6 +7,7 @@ use Slim\Http\Response; use Typemill\Models\Folder; use Typemill\Models\Write; use Typemill\Models\WriteYaml; +use Typemill\Models\WriteMeta; use Typemill\Extensions\ParsedownExtension; use Typemill\Events\OnPagePublished; use Typemill\Events\OnPageUnpublished; @@ -77,10 +78,15 @@ class ArticleApiController extends ContentController # update the public structure $this->setStructure($draft = false, $cache = false); - # dispatch event - $this->c->dispatcher->dispatch('onPagePublished', new OnPagePublished($this->item)); + # complete the page meta if title or description not set + $writeMeta = new WriteMeta(); + $meta = $writeMeta->completePageMeta($this->content, $this->settings, $this->item); - return $response->withJson(['success'], 200); + # dispatch event + $page = ['content' => $this->content, 'meta' => $meta, 'item' => $this->item]; + $this->c->dispatcher->dispatch('onPagePublished', new OnPagePublished($page)); + + return $response->withJson(['success' => true, 'meta' => $meta], 200); } else { @@ -260,7 +266,7 @@ class ArticleApiController extends ContentController } else { - return $response->withJson(array('data' => $this->structure, 'errors' => $this->errors), 404); + return $response->withJson(array('data' => $this->structure, 'errors' => $this->errors), 422); } } @@ -465,13 +471,12 @@ class ArticleApiController extends ContentController # create the url for the item $urlWoF = $folder->urlRelWoF . '/' . $slug; - # add the navigation name to the item htmlspecialchars needed for frensh language + # add the navigation name to the item htmlspecialchars needed for french language $extended[$urlWoF] = ['hide' => false, 'navtitle' => $name]; # store the extended structure $write->updateYaml('cache', 'structure-extended.yaml', $extended); - # update the structure for editor $this->setStructure($draft = true, $cache = false); @@ -482,7 +487,6 @@ class ArticleApiController extends ContentController return $response->withJson(array('posts' => $folder, $this->structure, 'errors' => false, 'url' => $url)); } - public function createArticle(Request $request, Response $response, $args) { @@ -546,14 +550,10 @@ class ArticleApiController extends ContentController if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); } # add prefix number to the name -# $namePath = $index > 9 ? $index . '-' . $name : '0' . $index . '-' . $name; $namePath = $index > 9 ? $index . '-' . $slug : '0' . $index . '-' . $slug; $folderPath = 'content' . $folder->path; -# $title = implode(" ", $nameParts); - # create default content -# $content = json_encode(['# ' . $title, 'Content']); $content = json_encode(['# ' . $name, 'Content']); if($this->params['type'] == 'file') @@ -576,21 +576,18 @@ class ArticleApiController extends ContentController } - # get extended structure $extended = $write->getYaml('cache', 'structure-extended.yaml'); # create the url for the item $urlWoF = $folder->urlRelWoF . '/' . $slug; - # add the navigation name to the item htmlspecialchars needed for frensh language + # add the navigation name to the item htmlspecialchars needed for french language $extended[$urlWoF] = ['hide' => false, 'navtitle' => $name]; # store the extended structure $write->updateYaml('cache', 'structure-extended.yaml', $extended); - - # update the structure for editor $this->setStructure($draft = true, $cache = false); @@ -643,7 +640,7 @@ class ArticleApiController extends ContentController # check, if the same name as new item, then return an error if($item->slug == $slug) { - return $response->withJson(array('data' => $this->structure, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404); + return $response->withJson(array('data' => $this->structure, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 422); } if(!$write->moveElement($item, '', $index)) @@ -653,7 +650,7 @@ class ArticleApiController extends ContentController $index++; } - if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); } + if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 422); } # add prefix number to the name $namePath = $index > 9 ? $index . '-' . $slug : '0' . $index . '-' . $slug; @@ -667,14 +664,14 @@ class ArticleApiController extends ContentController { if(!$write->writeFile($folderPath, $namePath . '.txt', $content)) { - return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the file. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); + return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the file. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 422); } } elseif($this->params['type'] == 'folder') { if(!$write->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath)) { - return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); + return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 422); } $write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content); @@ -695,7 +692,6 @@ class ArticleApiController extends ContentController # store the extended structure $write->updateYaml('cache', 'structure-extended.yaml', $extended); - # update the structure for editor $this->setStructure($draft = true, $cache = false); diff --git a/system/Controllers/AuthController.php b/system/Controllers/AuthController.php index b5fa700..b8c8921 100644 --- a/system/Controllers/AuthController.php +++ b/system/Controllers/AuthController.php @@ -24,7 +24,6 @@ class AuthController extends Controller } } - /** * show login form * diff --git a/system/Controllers/BlockApiController.php b/system/Controllers/BlockApiController.php index 9c77ff4..3e5a0af 100644 --- a/system/Controllers/BlockApiController.php +++ b/system/Controllers/BlockApiController.php @@ -769,28 +769,29 @@ class BlockApiController extends ContentController $imageData = @file_get_contents($videoURL0, 0, $ctx); if($imageData === false) { - return $response->withJson(array('errors' => 'could not get the video image')); + return $response->withJson(['errors' => ['message' => 'We did not find that video or could not get a preview image.']], 500); } } - $imageData64 = 'data:image/jpeg;base64,' . base64_encode($imageData); - $desiredSizes = ['live' => ['width' => 560, 'height' => 315]]; - $imageProcessor = new ProcessImage($this->settings['images']); - if(!$imageProcessor->checkFolders()) + $imageData64 = 'data:image/jpeg;base64,' . base64_encode($imageData); + $desiredSizes = $this->settings['images']; + $desiredSizes['live'] = ['width' => 560, 'height' => 315]; + $imageProcessor = new ProcessImage($desiredSizes); + + if(!$imageProcessor->checkFolders('images')) { return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500); } - - $tmpImage = $imageProcessor->createImage($imageData64, $desiredSizes); - if(!$tmpImage) + if(!$imageProcessor->createImage($imageData64, 'youtube-' . $videoID, $desiredSizes, $overwrite = true)) { - return $response->withJson(array('errors' => 'could not create temporary image')); + return $response->withJson(['errors' => ['message' => 'We could not create the image.']], 500); } - - $imageUrl = $imageProcessor->publishImage($desiredSizes, $videoID); + + $imageUrl = $imageProcessor->publishImage(); if($imageUrl) { + $this->params['markdown'] = '{#' . $videoID. ' .' . $class . '}'; $request = $request->withParsedBody($this->params); diff --git a/system/Controllers/ContentController.php b/system/Controllers/ContentController.php index 78ea990..87dc47e 100644 --- a/system/Controllers/ContentController.php +++ b/system/Controllers/ContentController.php @@ -60,6 +60,7 @@ abstract class ContentController $this->structureDraftName = 'structure-draft.txt'; } + # admin ui rendering protected function render($response, $route, $data) { if(isset($_SESSION['old'])) @@ -68,7 +69,7 @@ abstract class ContentController } $response = $response->withoutHeader('Server'); - $response = $response->withoutHeader('X-Powered-By'); + $response = $response->withoutHeader('X-Powered-By'); if($this->c->request->getUri()->getScheme() == 'https') { @@ -376,7 +377,7 @@ abstract class ContentController if(file_exists($path)) { $files = array_diff(scandir($path), array('.', '..')); - + # check if there are published pages or folders inside, then stop the operation foreach ($files as $file) { @@ -385,9 +386,9 @@ abstract class ContentController $this->errors = ['message' => 'Please delete the sub-folder first.']; } - if(substr($file, -3) == '.md' ) + if(substr($file, -3) == '.md' && $file != 'index.md') { - $this->errors = ['message' => 'Please unpublish all pages in the folder first.']; + $this->errors = ['message' => 'Please unpublish all pages in the folder first.']; } } diff --git a/system/Controllers/Controller.php b/system/Controllers/Controller.php index 57e54cf..6dcad45 100644 --- a/system/Controllers/Controller.php +++ b/system/Controllers/Controller.php @@ -18,6 +18,7 @@ abstract class Controller $this->c = $c; } + # frontend rendering protected function render($response, $route, $data) { # why commented this out?? @@ -29,8 +30,6 @@ abstract class Controller } $response = $response->withoutHeader('Server'); - $response = $response->withoutHeader('X-Powered-By'); - if($this->c->request->getUri()->getScheme() == 'https') { $response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000'); @@ -40,6 +39,8 @@ abstract class Controller $response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN'); $response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block'); $response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade'); + $response = $response->withAddedHeader('X-Powered-By', 'Typemill'); + return $this->c->view->render($response, $route, $data); } diff --git a/system/Controllers/MediaApiController.php b/system/Controllers/MediaApiController.php index a721197..5b0ec9b 100644 --- a/system/Controllers/MediaApiController.php +++ b/system/Controllers/MediaApiController.php @@ -320,19 +320,34 @@ class MediaApiController extends ContentController return $response->withJson(array('errors' => 'could not store the preview image')); } + # https://www.sitepoint.com/mime-types-complete-list/ + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types private function getAllowedMtypes() { return array( 'application/zip', 'application/gzip', + 'application/x-gzip', + 'application/x-compressed', + 'application/x-zip-compressed', 'application/vnd.rar', + 'application/x-7z-compressed', + 'application/x-visio', 'application/vnd.visio', + 'application/excel', + 'application/x-excel', + 'application/x-msexcel', 'application/vnd.ms-excel', - 'application/vnd.ms-powerpoint', - 'application/vnd.ms-word.document.macroEnabled.12', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/powerpoint', + 'application/mspowerpoint', + 'application/x-mspowerpoint', + 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/x-project', + 'application/vnd.ms-project', 'application/vnd.apple.keynote', 'application/vnd.apple.mpegurl', 'application/vnd.apple.numbers', @@ -340,17 +355,31 @@ class MediaApiController extends ContentController 'application/vnd.amazon.mobi8-ebook', 'application/epub+zip', 'application/pdf', + 'application/x-latex', 'image/png', 'image/jpeg', 'image/gif', + 'image/tiff', + 'image/x-tiff', 'image/svg+xml', + 'image/x-icon', + 'text/plain', + 'application/plain', + 'text/richtext', + 'text/vnd.rn-realtext', + 'application/rtf', + 'application/x-rtf', 'font/*', 'audio/mpeg', 'audio/mp4', 'audio/ogg', + 'audio/3gpp', + 'audio/3gpp2', 'video/mpeg', 'video/mp4', 'video/ogg', + 'video/3gpp', + 'video/3gpp2', ); } } \ No newline at end of file diff --git a/system/Controllers/MetaApiController.php b/system/Controllers/MetaApiController.php index ab997a1..30c6df9 100644 --- a/system/Controllers/MetaApiController.php +++ b/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\WriteMeta; use Typemill\Models\Folder; class MetaApiController extends ContentController @@ -24,6 +25,7 @@ class MetaApiController extends ContentController $metatabs = $writeYaml->getYaml('system' . DIRECTORY_SEPARATOR . 'author', 'metatabs.yaml'); + # add radio buttons to choose posts or pages for folder. if($folder) { $metatabs['meta']['fields']['contains'] = [ @@ -70,22 +72,22 @@ class MetaApiController extends ContentController # set item if(!$this->setItem()){ return $response->withJson($this->errors, 404); } - $writeYaml = new writeYaml(); + $writeMeta = new writeMeta(); - $pagemeta = $writeYaml->getPageMeta($this->settings, $this->item); + $pagemeta = $writeMeta->getPageMeta($this->settings, $this->item); if(!$pagemeta) { # set the status for published and drafted $this->setPublishStatus(); - + # set path $this->setItemPath($this->item->fileType); # read content from file if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - $pagemeta = $writeYaml->getPageMetaDefaults($this->content, $this->settings, $this->item); + $pagemeta = $writeMeta->getPageMetaBlank($this->content, $this->settings, $this->item); } # if item is a folder @@ -118,7 +120,7 @@ class MetaApiController extends ContentController } # store the metascheme in cache for frontend - $writeYaml->updateYaml('cache', 'metatabs.yaml', $metascheme); + $writeMeta->updateYaml('cache', 'metatabs.yaml', $metascheme); return $response->withJson(array('metadata' => $metadata, 'metadefinitions' => $metadefinitions, 'item' => $this->item, 'errors' => false)); } @@ -187,13 +189,13 @@ class MetaApiController extends ContentController # return validation errors if($errors){ return $response->withJson(array('errors' => $errors),422); } - $writeYaml = new writeYaml(); + $writeMeta = new writeMeta(); # get existing metadata for page - $metaPage = $writeYaml->getYaml($this->settings['contentFolder'], $this->item->pathWithoutType . '.yaml'); + $metaPage = $writeMeta->getYaml($this->settings['contentFolder'], $this->item->pathWithoutType . '.yaml'); # get extended structure - $extended = $writeYaml->getYaml('cache', 'structure-extended.yaml'); + $extended = $writeMeta->getYaml('cache', 'structure-extended.yaml'); # flag for changed structure $structure = false; @@ -218,7 +220,7 @@ class MetaApiController extends ContentController $pathWithoutFile = str_replace($this->item->originalName, "", $this->item->path); $newPathWithoutType = $pathWithoutFile . $datetime . '-' . $this->item->slug; - $writeYaml->renamePost($this->item->pathWithoutType, $newPathWithoutType); + $writeMeta->renamePost($this->item->pathWithoutType, $newPathWithoutType); # recreate the draft structure $this->setStructure($draft = true, $cache = false); @@ -235,11 +237,11 @@ class MetaApiController extends ContentController if($metaInput['contains'] == "posts") { - $writeYaml->transformPagesToPosts($this->item); + $writeMeta->transformPagesToPosts($this->item); } if($metaInput['contains'] == "pages") { - $writeYaml->transformPostsToPages($this->item); + $writeMeta->transformPostsToPages($this->item); } } @@ -273,12 +275,12 @@ class MetaApiController extends ContentController $meta[$tab] = $metaInput; # store the metadata - $writeYaml->updateYaml($this->settings['contentFolder'], $this->item->pathWithoutType . '.yaml', $meta); + $writeMeta->updateYaml($this->settings['contentFolder'], $this->item->pathWithoutType . '.yaml', $meta); if($structure) { # store the extended file - $writeYaml->updateYaml('cache', 'structure-extended.yaml', $extended); + $writeMeta->updateYaml('cache', 'structure-extended.yaml', $extended); # recreate the draft structure $this->setStructure($draft = true, $cache = false); @@ -309,6 +311,4 @@ class MetaApiController extends ContentController } return true; } -} - -# check models -> writeYaml for getPageMeta and getPageMetaDefaults. \ No newline at end of file +} \ No newline at end of file diff --git a/system/Controllers/PageController.php b/system/Controllers/PageController.php index b721941..a05c9e2 100644 --- a/system/Controllers/PageController.php +++ b/system/Controllers/PageController.php @@ -6,6 +6,7 @@ use Typemill\Models\Folder; use Typemill\Models\WriteCache; use Typemill\Models\WriteSitemap; use Typemill\Models\WriteYaml; +use Typemill\Models\WriteMeta; use \Symfony\Component\Yaml\Yaml; use Typemill\Models\VersionCheck; use Typemill\Models\Helpers; @@ -30,7 +31,6 @@ class PageController extends Controller $item = false; $home = false; $breadcrumb = false; - $description = ''; $settings = $this->c->get('settings'); $pathToContent = $settings['rootPath'] . $settings['contentFolder']; $cache = new WriteCache(); @@ -61,10 +61,6 @@ class PageController extends Controller /* update sitemap */ $sitemap = new WriteSitemap(); $sitemap->updateSitemap('cache', 'sitemap.xml', 'lastSitemap.txt', $structure, $uri->getBaseUrl()); - - /* check and update the typemill-version in the user settings */ - # this version check is not needed - # $this->updateVersion($uri->getBaseUrl()); } } @@ -89,7 +85,7 @@ class PageController extends Controller if(empty($args)) { $home = true; - $item = Folder::getItemForUrl($structure, $uri->getBasePath(), $uri->getBasePath()); + $item = Folder::getItemForUrl($navigation, $uri->getBasePath(), $uri->getBasePath()); } else { @@ -110,10 +106,10 @@ class PageController extends Controller $breadcrumb = $this->c->dispatcher->dispatch('onBreadcrumbLoaded', new OnBreadcrumbLoaded($breadcrumb))->getData(); # set pages active for navigation again - Folder::getBreadcrumb($navigation, $item->keyPathArray); + Folder::getBreadcrumb($structure, $item->keyPathArray); /* add the paging to the item */ - $item = Folder::getPagingForItem($structure, $item); + $item = Folder::getPagingForItem($navigation, $item); } # dispatch the item @@ -126,6 +122,9 @@ class PageController extends Controller if($item->elementType == 'folder') { $filePath = $filePath . DIRECTORY_SEPARATOR . 'index.md'; + + # use navigation instead of structure to get + $item = Folder::getItemForUrl($navigation, $urlRel, $uri->getBasePath()); } # read the content of the file @@ -135,13 +134,10 @@ class PageController extends Controller $this->c->dispatcher->dispatch('onOriginalLoaded', new OnOriginalLoaded($contentMD)); # get meta-Information - $writeYaml = new WriteYaml(); - $metatabs = $writeYaml->getPageMeta($settings, $item); + $writeMeta = new WriteMeta(); - if(!$metatabs) - { - $metatabs = $writeYaml->getPageMetaDefaults($contentMD, $settings, $item); - } + # makes sure that you always have the full meta with title, description and all the rest. + $metatabs = $writeMeta->completePageMeta($contentMD, $settings, $item); # dispatch meta $metatabs = $this->c->dispatcher->dispatch('onMetaLoaded', new OnMetaLoaded($metatabs))->getData(); @@ -149,20 +145,20 @@ class PageController extends Controller # dispatch content $contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new OnMarkdownLoaded($contentMD))->getData(); + $itemUrl = isset($item->urlRel) ? $item->urlRel : false; + /* initialize parsedown */ - $parsedown = new ParsedownExtension(); + $parsedown = new ParsedownExtension($settings['headlineanchors']); /* set safe mode to escape javascript and html in markdown */ $parsedown->setSafeMode(true); /* parse markdown-file to content-array */ - $contentArray = $parsedown->text($contentMD); + $contentArray = $parsedown->text($contentMD, $itemUrl); $contentArray = $this->c->dispatcher->dispatch('onContentArrayLoaded', new OnContentArrayLoaded($contentArray))->getData(); /* get the first image from content array */ $firstImage = $this->getFirstImage($contentArray); - - $itemUrl = isset($item->urlRel) ? $item->urlRel : false; /* parse markdown-content-array to content-string */ $contentHTML = $parsedown->markup($contentArray, $itemUrl); @@ -172,25 +168,7 @@ class PageController extends Controller $contentParts = explode("", $contentHTML); $title = isset($contentParts[0]) ? strip_tags($contentParts[0]) : $settings['title']; - $contentHTML = isset($contentParts[1]) ? $contentParts[1] : $contentHTML; - - # if there is not meta description - if(!isset($metatabs['meta']['description']) or !$metatabs['meta']['description']) - { - # create excerpt from html - $excerpt = substr($contentHTML,0,500); - - # create description from excerpt - $description = isset($excerpt) ? strip_tags($excerpt) : false; - if($description) - { - $description = trim(preg_replace('/\s+/', ' ', $description)); - $description = substr($description, 0, 300); - $lastSpace = strrpos($description, ' '); - - $metatabs['meta']['description'] = substr($description, 0, $lastSpace); - } - } + $contentHTML = isset($contentParts[1]) ? $contentParts[1] : $contentHTML; /* get url and alt-tag for first image, if exists */ if($firstImage) @@ -208,7 +186,7 @@ class PageController extends Controller $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'); + $customcss = $writeMeta->checkFile('cache', $theme . '-custom.css'); if($customcss) { $this->c->assets->addCSS($base_url . '/cache/' . $theme . '-custom.css'); @@ -262,10 +240,10 @@ class PageController extends Controller $yaml = new writeYaml(); $extended = $yaml->getYaml('cache', 'structure-extended.yaml'); - /* create an array of object with the whole content of the folder */ + # create an array of object with the whole content of the folder $structure = Folder::getFolderContentDetails($structure, $extended, $uri->getBaseUrl(), $uri->getBasePath()); - /* cache structure */ + # cache structure $cache->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure); if($extended && $this->containsHiddenPages($extended)) @@ -282,7 +260,8 @@ class PageController extends Controller $cache->deleteFileWithPath('cache' . DIRECTORY_SEPARATOR . 'navigation.txt'); } - return $structure; + # load and return the cached structure, because might be manipulated with navigation.... + return $this->getCachedStructure($cache); } protected function containsHiddenPages($extended) diff --git a/system/Controllers/SettingsController.php b/system/Controllers/SettingsController.php index bad290f..23af2e4 100644 --- a/system/Controllers/SettingsController.php +++ b/system/Controllers/SettingsController.php @@ -57,15 +57,16 @@ class SettingsController extends Controller { /* make sure only allowed fields are stored */ $newSettings = array( - 'title' => $newSettings['title'], - 'author' => $newSettings['author'], - 'copyright' => $newSettings['copyright'], - 'year' => $newSettings['year'], - 'language' => $newSettings['language'], - 'editor' => $newSettings['editor'], - 'formats' => $newSettings['formats'], + 'title' => $newSettings['title'], + 'author' => $newSettings['author'], + 'copyright' => $newSettings['copyright'], + 'year' => $newSettings['year'], + 'language' => $newSettings['language'], + 'editor' => $newSettings['editor'], + 'formats' => $newSettings['formats'], + 'headlineanchors' => isset($newSettings['headlineanchors']) ? $newSettings['headlineanchors'] : null, ); - + # https://www.slimframework.com/docs/v3/cookbook/uploading-files.html; $copyright = $this->getCopyright(); diff --git a/system/Controllers/SetupController.php b/system/Controllers/SetupController.php index e149ed1..35e10a3 100644 --- a/system/Controllers/SetupController.php +++ b/system/Controllers/SetupController.php @@ -52,7 +52,7 @@ class SetupController extends Controller $setuperrors = empty($systemcheck) ? false : 'Some system requirements for Typemill are missing.'; $systemcheck = empty($systemcheck) ? false : $systemcheck; - return $this->render($response, 'auth/setup.twig', array( 'messages' => $setuperror, 'systemcheck' => $systemcheck )); + return $this->render($response, 'auth/setup.twig', array( 'messages' => $setuperrors, 'systemcheck' => $systemcheck )); } public function create($request, $response, $args) diff --git a/system/Extensions/ParsedownExtension.php b/system/Extensions/ParsedownExtension.php index 34771a2..77d305a 100644 --- a/system/Extensions/ParsedownExtension.php +++ b/system/Extensions/ParsedownExtension.php @@ -6,10 +6,13 @@ use \URLify; class ParsedownExtension extends \ParsedownExtra { - function __construct() + function __construct($showAnchor = NULL) { parent::__construct(); + # show anchor next to headline? + $this->showAnchor = $showAnchor; + # math support $this->BlockTypes['\\'][] = 'Math'; $this->BlockTypes['$'][] = 'Math'; @@ -30,8 +33,10 @@ class ParsedownExtension extends \ParsedownExtra $this->visualMode = true; } - public function text($text) + public function text($text, $relurl = null) { + $this->relurl = $relurl ? $relurl : ''; + $Elements = $this->textElements($text); return $Elements; @@ -117,20 +122,37 @@ class ParsedownExtension extends \ParsedownExtra { return; } - + $text = trim($text, ' '); - - $Block = array( - 'element' => array( - 'name' => 'h' . min(6, $level), - 'text' => $text, - 'handler' => 'line', - 'attributes' => array( - 'id' => "$headline" - ) - ) - ); - + + $Block = array( + 'element' => array( + 'name' => 'h' . min(6, $level), + 'text' => $text, + 'handler' => 'line', + 'attributes' => array( + 'id' => "$headline" + ) + ) + ); + + if($this->showAnchor && $level > 1) + { + $Block['element']['elements'] = array( + array( + 'name' => 'a', + 'attributes' => array( + 'href' => $this->relurl . "#" . $headline, + 'class' => 'tm-heading-anchor', + ), + 'text' => '#', + ), + array( + 'text' => $text, + ) + ); + } + $this->headlines[] = array('level' => $level, 'name' => $Block['element']['name'], 'attribute' => $Block['element']['attributes']['id'], 'text' => $text); return $Block; diff --git a/system/Extensions/TwigMetaExtension.php b/system/Extensions/TwigMetaExtension.php index 82da060..0d33399 100644 --- a/system/Extensions/TwigMetaExtension.php +++ b/system/Extensions/TwigMetaExtension.php @@ -2,7 +2,7 @@ namespace Typemill\Extensions; -use Typemill\Models\WriteYaml; +use Typemill\Models\WriteMeta; class TwigMetaExtension extends \Twig_Extension { @@ -15,9 +15,30 @@ class TwigMetaExtension extends \Twig_Extension public function getMeta($settings, $item) { - $write = new WriteYaml(); + $writeMeta = new WriteMeta(); - $meta = $write->getPageMeta($settings, $item); + $meta = $writeMeta->getPageMeta($settings, $item); + + if(!$meta OR $meta['meta']['title'] == '' OR $meta['meta']['description'] == '') + { + # create path to the file + $filePath = $settings['rootPath'] . $settings['contentFolder'] . $item->path; + + # check if url is a folder and add index.md + if($item->elementType == 'folder') + { + $filePath = $filePath . DIRECTORY_SEPARATOR . 'index.md'; + } + + if(file_exists($filePath)) + { + # get content + $content = file_get_contents($filePath); + + # completes title and description or generate default meta values + $meta = $writeMeta->completePageMeta($content, $settings, $item); + } + } return $meta; } diff --git a/system/Models/ProcessAssets.php b/system/Models/ProcessAssets.php index 98bed4d..7494388 100644 --- a/system/Models/ProcessAssets.php +++ b/system/Models/ProcessAssets.php @@ -98,7 +98,7 @@ class ProcessAssets return (count(scandir($dir)) == 2); } - public function setFileName($originalname, $type, $overwrite = null) + public function setFileName($originalname, $type, $overwrite = NULL) { $pathinfo = pathinfo($originalname); @@ -135,6 +135,11 @@ class ProcessAssets return $this->filename; } + public function setExtension($extension) + { + $this->extension = $extension; + } + public function getExtension() { return $this->extension; diff --git a/system/Models/ProcessImage.php b/system/Models/ProcessImage.php index f3c4b0a..e6a1d18 100644 --- a/system/Models/ProcessImage.php +++ b/system/Models/ProcessImage.php @@ -5,7 +5,7 @@ use Typemill\Models\Helpers; class ProcessImage extends ProcessAssets { - public function createImage(string $image, string $name, array $desiredSizes) + public function createImage(string $image, string $name, array $desiredSizes, $overwrite = NULL) { # fix error from jpeg-library ini_set ('gd.jpeg_ignore_warning', 1); @@ -15,13 +15,15 @@ class ProcessImage extends ProcessAssets $this->clearTempFolder(); # set the name of the image - $this->setFileName($name, 'image'); + $this->setFileName($name, 'image', $overwrite); # decode the image from base64-string $imageDecoded = $this->decodeImage($image); $imageData = $imageDecoded["image"]; $imageType = $imageDecoded["type"]; + $this->setExtension($imageType); + # transform image-stream into image $image = imagecreatefromstring($imageData); @@ -38,7 +40,7 @@ class ProcessImage extends ProcessAssets $tmpname = fopen($this->tmpFolder . $this->getName() . '.' . $imageType . ".txt", "w"); $this->saveOriginal($this->tmpFolder, $imageData, $name = 'original', $imageType); - + # temporary store resized images foreach($resizedImages as $key => $resizedImage) { @@ -71,8 +73,8 @@ class ProcessImage extends ProcessAssets { $tmpname = str_replace('.txt', '', basename($imagename)); - # set extension and sanitize name - $this->setFileName($tmpname, 'image'); + # set extension and sanitize name. Overwrite because this has been checked before + $this->setFileName($tmpname, 'image', $overwrite = true); unlink($imagename); } @@ -80,7 +82,7 @@ class ProcessImage extends ProcessAssets $name = uniqid(); if($this->filename && $this->extension) - { + { $name = $this->filename; } @@ -110,7 +112,7 @@ class ProcessImage extends ProcessAssets if($success) { - return true; + # return true; return 'media/live/' . $name . '.' . $tmpfilename[1]; } @@ -201,7 +203,9 @@ class ProcessImage extends ProcessAssets # save resized images in temporary folder public function saveImage($folder, $image, $name, $type) - { + { + $type = strtolower($type); + if($type == "png") { $result = imagepng( $image, $folder . $name . '.png' ); @@ -210,10 +214,14 @@ class ProcessImage extends ProcessAssets { $result = imagegif( $image, $folder . $name . '.gif' ); } + elseif($type == "jpg" OR $type == "jpeg") + { + $result = imagejpeg( $image, $folder . $name . '.' . $type ); + } else { - $result = imagejpeg( $image, $folder . $name . '.jpeg' ); - $type = 'jpeg'; + # image type not supported + return false; } imagedestroy($image); @@ -339,12 +347,13 @@ class ProcessImage extends ProcessAssets { $this->setFileName($filename, 'image', $overwrite = true); - if($this->extension == 'jpeg') $this->extension = 'jpg'; + # if($this->extension == 'jpg') $this->extension = 'jpeg'; switch($this->extension) { case 'gif': $image = imagecreatefromgif($this->liveFolder . $filename); break; - case 'jpg': $image = imagecreatefromjpeg($this->liveFolder . $filename); break; + case 'jpg' : + case 'jpeg': $image = imagecreatefromjpeg($this->liveFolder . $filename); break; case 'png': $image = imagecreatefrompng($this->liveFolder . $filename); break; default: return 'image type not supported'; } @@ -367,12 +376,13 @@ class ProcessImage extends ProcessAssets { $this->setFileName($filename, 'image'); - if($this->extension == 'jpeg') $this->extension = 'jpg'; + if($this->extension == 'jpg') $this->extension = 'jpeg'; switch($this->extension) { case 'gif': $image = imagecreatefromgif($image); break; - case 'jpg': $image = imagecreatefromjpeg($image); break; + case 'jpg' : + case 'jpeg': $image = imagecreatefromjpeg($image); break; case 'png': $image = imagecreatefrompng($image); break; default: return 'image type not supported'; } @@ -383,5 +393,4 @@ class ProcessImage extends ProcessAssets return $resizedImages; } - } \ No newline at end of file diff --git a/system/Models/WriteMeta.php b/system/Models/WriteMeta.php new file mode 100644 index 0000000..f04d7af --- /dev/null +++ b/system/Models/WriteMeta.php @@ -0,0 +1,348 @@ +getYaml($settings['contentFolder'], $item->pathWithoutType . '.yaml'); + + if(!$meta) + { + 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; + } + + # cases are rare: updates from old version prior 1.3.4 or if content-files are added manually, e.g. by ftp + public function getPageMetaDefaults($content, $settings, $item) + { + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # if content is not an array, then transform it + if(!is_array($content)) + { + # turn markdown into an array of markdown-blocks + $content = $parsedown->markdownToArrayBlocks($content); + } + + $title = false; + + # delete markdown from title + if(isset($content[0])) + { + $title = trim($content[0], "# "); + } + + $description = $this->generateDescription($content, $parsedown, $item); + + $author = $settings['author']; + + if(isset($_SESSION)) + { + if(isset($_SESSION['firstname']) && $_SESSION['firstname'] !='' && isset($_SESSION['lastname']) && $_SESSION['lastname'] != '') + { + $author = $_SESSION['firstname'] . ' ' . $_SESSION['lastname']; + } + elseif(isset($_SESSION['user'])) + { + $author = $_SESSION['user']; + } + } + + # create new meta-file + $meta = [ + 'meta' => [ + 'title' => $title, + 'description' => $description, + 'author' => $author, + 'created' => date("Y-m-d"), + 'time' => date("H-i-s"), + 'navtitle' => $item->name, + ] + ]; + + $meta = $this->addFileTimeToMeta($meta, $item, $settings); + + $this->updateYaml($settings['contentFolder'], $item->pathWithoutType . '.yaml', $meta); + + return $meta; + } + + # used by MetaApiController. Do not set title or description in defaults if page is not published yet + public function getPageMetaBlank($content, $settings, $item) + { + $author = $settings['author']; + + if(isset($_SESSION)) + { + if(isset($_SESSION['firstname']) && $_SESSION['firstname'] !='' && isset($_SESSION['lastname']) && $_SESSION['lastname'] != '') + { + $author = $_SESSION['firstname'] . ' ' . $_SESSION['lastname']; + } + elseif(isset($_SESSION['user'])) + { + $author = $_SESSION['user']; + } + } + + # create new meta-file + $meta = [ + 'meta' => [ + 'title' => '', + 'description' => '', + 'author' => $author, + 'created' => date("Y-m-d"), + 'time' => date("H-i-s"), + 'navtitle' => $item->name + ] + ]; + + $meta = $this->addFileTimeToMeta($meta, $item, $settings); + + $this->updateYaml($settings['contentFolder'], $item->pathWithoutType . '.yaml', $meta); + + return $meta; + } + + public function getNavtitle($url) + { + # get the extended structure where the navigation title is stored + $extended = $this->getYaml('cache', 'structure-extended.yaml'); + + if(isset($extended[$url]['navtitle'])) + { + return $extended[$url]['navtitle']; + } + return ''; + } + + # used by articleApiController and pageController to add title and description if an article is published + public function completePageMeta($content, $settings, $item) + { + $meta = $this->getPageMeta($settings, $item); + + if(!$meta) + { + return $this->getPageMetaDefaults($content, $settings, $item); + } + + $title = (isset($meta['meta']['title']) AND $meta['meta']['title'] !== '') ? true : false; + $description = (isset($meta['meta']['description']) AND $meta['meta']['description'] !== '') ? true : false; + + if($title && $description) + { + return $meta; + } + + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # if content is not an array, then transform it + if(!is_array($content)) + { + # turn markdown into an array of markdown-blocks + $content = $parsedown->markdownToArrayBlocks($content); + } + + # delete markdown from title + if(!$title && isset($content[0])) + { + $meta['meta']['title'] = trim($content[0], "# "); + } + + if(!$description && isset($content[1])) + { + $meta['meta']['description'] = $this->generateDescription($content, $parsedown, $item); + } + + $this->updateYaml($settings['contentFolder'], $item->pathWithoutType . '.yaml', $meta); + + 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; + $fileType = isset($item->fileType) ? $item->fileType : 'md'; + + # check if url is a folder. + if($item->elementType == 'folder') + { + $filePath = $settings['contentFolder'] . $item->path . DIRECTORY_SEPARATOR . 'index.'. $fileType; + } + + # add the modified date for the file + $meta['meta']['modified'] = file_exists($filePath) ? date("Y-m-d",filemtime($filePath)) : date("Y-m-d"); + + return $meta; + } + + public function generateDescription($content, $parsedown, $item) + { + $description = isset($content[1]) ? $content[1] : ''; + + # create description or abstract from content + if($description !== '') + { + $firstLineArray = $parsedown->text($description); + $description = strip_tags($parsedown->markup($firstLineArray, $item->urlAbs)); + + # if description is very short + if(strlen($description) < 100 && isset($content[2])) + { + $secondLineArray = $parsedown->text($content[2]); + $description .= ' ' . strip_tags($parsedown->markup($secondLineArray, $item->urlAbs)); + } + + # if description is too long + if(strlen($description) > 300) + { + $description = substr($description, 0, 300); + $lastSpace = strrpos($description, ' '); + $description = substr($description, 0, $lastSpace); + } + } + return $description; + } + + public function transformPagesToPosts($folder){ + + $filetypes = array('md', 'txt', 'yaml'); + + foreach($folder->folderContent as $page) + { + # create old filename without filetype + $oldFile = $this->basePath . 'content' . $page->pathWithoutType; + + # set default date + $date = date('Y-m-d', time()); + $time = date('H-i', time()); + + $meta = $this->getYaml('content', $page->pathWithoutType . '.yaml'); + + if($meta) + { + # get dates from meta + if(isset($meta['meta']['manualdate'])){ $date = $meta['meta']['manualdate']; } + elseif(isset($meta['meta']['created'])){ $date = $meta['meta']['created']; } + elseif(isset($meta['meta']['modified'])){ $date = $meta['meta']['modified']; } + + # set time + if(isset($meta['meta']['time'])) + { + $time = $meta['meta']['time']; + } + } + + $datetime = $date . '-' . $time; + $datetime = implode(explode('-', $datetime)); + $datetime = substr($datetime,0,12); + + # create new file-name without filetype + $newFile = $this->basePath . 'content' . $folder->path . DIRECTORY_SEPARATOR . $datetime . '-' . $page->slug; + + $result = true; + + foreach($filetypes as $filetype) + { + $oldFilePath = $oldFile . '.' . $filetype; + $newFilePath = $newFile . '.' . $filetype; + + #check if file with filetype exists and rename + if($oldFilePath != $newFilePath && file_exists($oldFilePath)) + { + if(@rename($oldFilePath, $newFilePath)) + { + $result = $result; + } + else + { + $result = false; + } + } + } + } + } + + public function transformPostsToPages($folder){ + + $filetypes = array('md', 'txt', 'yaml'); + $index = 0; + + foreach($folder->folderContent as $page) + { + # create old filename without filetype + $oldFile = $this->basePath . 'content' . $page->pathWithoutType; + + $order = $index; + + if($index < 10) + { + $order = '0' . $index; + } + + # create new file-name without filetype + $newFile = $this->basePath . 'content' . $folder->path . DIRECTORY_SEPARATOR . $order . '-' . $page->slug; + + $result = true; + + foreach($filetypes as $filetype) + { + $oldFilePath = $oldFile . '.' . $filetype; + $newFilePath = $newFile . '.' . $filetype; + + #check if file with filetype exists and rename + if($oldFilePath != $newFilePath && file_exists($oldFilePath)) + { + if(@rename($oldFilePath, $newFilePath)) + { + $result = $result; + } + else + { + $result = false; + } + } + } + + $index++; + } + } +} \ No newline at end of file diff --git a/system/Models/WriteYaml.php b/system/Models/WriteYaml.php index 283ee2d..6473dcc 100644 --- a/system/Models/WriteYaml.php +++ b/system/Models/WriteYaml.php @@ -2,8 +2,6 @@ namespace Typemill\Models; -use Typemill\Extensions\ParsedownExtension; - class WriteYaml extends Write { /** @@ -37,235 +35,4 @@ class WriteYaml extends Write } return false; } - - # used by contentApiController (backend) and pageController (frontend) - public function getPageMeta($settings, $item) - { - $meta = $this->getYaml($settings['contentFolder'], $item->pathWithoutType . '.yaml'); - - if(!$meta) - { - 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; - } - - # used by contentApiController (backend) and pageController (frontend) - public function getPageMetaDefaults($content, $settings, $item) - { - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # if content is not an array, then transform it - if(!is_array($content)) - { - # turn markdown into an array of markdown-blocks - $content = $parsedown->markdownToArrayBlocks($content); - } - - $title = false; - - # delete markdown from title - if(isset($content[0])) - { - $title = trim($content[0], "# "); - } - - $description = false; - - # delete markdown from title - if(isset($content[1])) - { - $firstLineArray = $parsedown->text($content[1]); - $description = strip_tags($parsedown->markup($firstLineArray, $item->urlAbs)); - $description = substr($description, 0, 300); - $lastSpace = strrpos($description, ' '); - $description = substr($description, 0, $lastSpace); - } - - $author = $settings['author']; - - if(isset($_SESSION)) - { - if(isset($_SESSION['firstname']) && $_SESSION['firstname'] !='' && isset($_SESSION['lastname']) && $_SESSION['lastname'] != '') - { - $author = $_SESSION['firstname'] . ' ' . $_SESSION['lastname']; - } - elseif(isset($_SESSION['user'])) - { - $author = $_SESSION['user']; - } - } - - # create new meta-file - $meta = [ - 'meta' => [ - 'title' => $title, - 'description' => $description, - 'author' => $author, - 'created' => date("Y-m-d"), - 'time' => date("H-i-s"), - ] - ]; - - $this->updateYaml($settings['contentFolder'], $item->pathWithoutType . '.yaml', $meta); - - $meta = $this->addFileTimeToMeta($meta, $item, $settings); - - 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; - $fileType = isset($item->fileType) ? $item->fileType : 'md'; - - # check if url is a folder. - if($item->elementType == 'folder') - { - $filePath = $settings['contentFolder'] . $item->path . DIRECTORY_SEPARATOR . 'index.'. $fileType; - } - - # add the modified date for the file - $meta['meta']['modified'] = file_exists($filePath) ? date("Y-m-d",filemtime($filePath)) : false; - - return $meta; - } - - - public function transformPagesToPosts($folder){ - - $filetypes = array('md', 'txt', 'yaml'); - - foreach($folder->folderContent as $page) - { - # create old filename without filetype - $oldFile = $this->basePath . 'content' . $page->pathWithoutType; - - # set default date - $date = date('Y-m-d', time()); - $time = date('H-i', time()); - - $meta = $this->getYaml('content', $page->pathWithoutType . '.yaml'); - - if($meta) - { - # get dates from meta - if(isset($meta['meta']['manualdate'])){ $date = $meta['meta']['manualdate']; } - elseif(isset($meta['meta']['created'])){ $date = $meta['meta']['created']; } - elseif(isset($meta['meta']['modified'])){ $date = $meta['meta']['modified']; } - - # set time - if(isset($meta['meta']['time'])) - { - $time = $meta['meta']['time']; - } - } - - $datetime = $date . '-' . $time; - $datetime = implode(explode('-', $datetime)); - $datetime = substr($datetime,0,12); - - # create new file-name without filetype - $newFile = $this->basePath . 'content' . $folder->path . DIRECTORY_SEPARATOR . $datetime . '-' . $page->slug; - - $result = true; - - foreach($filetypes as $filetype) - { - $oldFilePath = $oldFile . '.' . $filetype; - $newFilePath = $newFile . '.' . $filetype; - - #check if file with filetype exists and rename - if($oldFilePath != $newFilePath && file_exists($oldFilePath)) - { - if(@rename($oldFilePath, $newFilePath)) - { - $result = $result; - } - else - { - $result = false; - } - } - } - } - } - - public function transformPostsToPages($folder){ - - $filetypes = array('md', 'txt', 'yaml'); - $index = 0; - - foreach($folder->folderContent as $page) - { - # create old filename without filetype - $oldFile = $this->basePath . 'content' . $page->pathWithoutType; - - $order = $index; - - if($index < 10) - { - $order = '0' . $index; - } - - # create new file-name without filetype - $newFile = $this->basePath . 'content' . $folder->path . DIRECTORY_SEPARATOR . $order . '-' . $page->slug; - - $result = true; - - foreach($filetypes as $filetype) - { - $oldFilePath = $oldFile . '.' . $filetype; - $newFilePath = $newFile . '.' . $filetype; - - #check if file with filetype exists and rename - if($oldFilePath != $newFilePath && file_exists($oldFilePath)) - { - if(@rename($oldFilePath, $newFilePath)) - { - $result = $result; - } - else - { - $result = false; - } - } - } - - $index++; - } - } } \ No newline at end of file diff --git a/system/Settings.php b/system/Settings.php index 7dde0a9..9bf141c 100644 --- a/system/Settings.php +++ b/system/Settings.php @@ -27,14 +27,14 @@ class Settings $themes = array_diff(scandir($themefolder), array('..', '.')); $firsttheme = reset($themes); - # if there is a theme with valid theme settings-file - if($firsttheme && self::getObjectSettings('themes', $firsttheme)) + # if there is a theme with an index.twig-file + if($firsttheme && file_exists($themefolder . $firsttheme . DIRECTORY_SEPARATOR . 'index.twig')) { $settings['theme'] = $firsttheme; } else { - die('There is no theme in the theme-folder. Please add a theme from https://themes.typemill.net'); + die('You need at least one theme with an index.twig-file in your theme-folder.'); } } @@ -166,6 +166,7 @@ class Settings 'startpage' => true, 'author' => true, 'year' => true, + 'headlineanchors' => true, 'theme' => true, 'editor' => true, 'formats' => true, diff --git a/system/author/css/style.css b/system/author/css/style.css index 6197987..b4f6c00 100644 --- a/system/author/css/style.css +++ b/system/author/css/style.css @@ -141,18 +141,28 @@ a.tm-download::before{ width: 30px; height: 30px; line-height: 30px; - font-family: "Comic Sans MS",cursive,sans-serif; + font-family: "Comic Sans MS",cursive,sans-serif; + font-size: 1.3em; + font-weight: 900; border: 2px solid #e0474c; border-radius: 50%; text-align: center; - text-decoration: underline; + text-decoration: none; } a.tm-download:hover::before{ - text-decoration:underline; + text-decoration: none; color: #fff; background: #e0474c; } +a.tm-heading-anchor { + display: none; + position: absolute; + top: 0; + left: -1em; + width: 1em; + opacity: 0; +} /******************** * COMMONS * diff --git a/system/author/js/lazy-video.js b/system/author/js/typemillutils.js similarity index 100% rename from system/author/js/lazy-video.js rename to system/author/js/typemillutils.js diff --git a/system/author/js/vue-blox-config.js b/system/author/js/vue-blox-config.js index 170b501..8368a4a 100644 --- a/system/author/js/vue-blox-config.js +++ b/system/author/js/vue-blox-config.js @@ -35,8 +35,15 @@ let determiner = { } return false; }, + video: function(block,lines,firstChar,secondChar,thirdChar){ + if( (firstChar == '!' && secondChar == '[' && lines[0].indexOf('.youtube') != -1) || (firstChar == '[' && secondChar == '!' && lines[0].indexOf('.youtube') != -1) ) + { + return "video-component"; + } + return false; + }, image: function(block,lines,firstChar,secondChar,thirdChar){ - if( (firstChar == '!' && secondChar == '[') || (firstChar == '[' && secondChar == '!' && thirdChar == '[') ) + if( (firstChar == '!' && secondChar == '[' ) || (firstChar == '[' && secondChar == '!' && thirdChar == '[') ) { return "image-component"; } diff --git a/system/author/js/vue-blox.js b/system/author/js/vue-blox.js index a64347b..e2006e1 100644 --- a/system/author/js/vue-blox.js +++ b/system/author/js/vue-blox.js @@ -253,6 +253,16 @@ const contentComponent = Vue.component('content-block', { { params.new = true; } + else + { + var oldVideoID = this.$root.$data.blockMarkdown.match(/#.*? /); + if(this.compmarkdown.indexOf(oldVideoID[0].substring(1).trim()) !== -1) + { + this.activatePage(); + this.switchToPreviewMode(); + return; + } + } } else if(this.componentType == 'file-component') { @@ -282,7 +292,7 @@ const contentComponent = Vue.component('content-block', { self.activatePage(); publishController.errors.message = "Looks like you are logged out. Please login and try again."; } - else if(response) + else if(response) { self.activatePage(); @@ -342,9 +352,14 @@ const contentComponent = Vue.component('content-block', { self.$root.checkMath(result.id); /* check youtube here */ - if(thisBlockType == "video-component" || thisBlockType == "image-component") + if(thisBlockType == "video-component") { - self.$root.checkVideo(result.id); + setTimeout(function(){ + self.$nextTick(function () + { + self.$root.checkVideo(result.id); + }); + }, 300); } /* update the navigation and mark navigation item as modified */ @@ -1192,7 +1207,6 @@ const definitionComponent = Vue.component('definition-component', { }, }) - const videoComponent = Vue.component('video-component', { props: ['compmarkdown', 'disabled', 'load'], template: '