mirror of
https://github.com/typemill/typemill.git
synced 2025-08-01 20:00:37 +02:00
Version 1.5.1: File restrictions, download controller, noindex, fix youtube, cyanine third teaser
This commit is contained in:
@@ -64,7 +64,7 @@ RewriteRule ^(system\/author\/img\/) - [L]
|
||||
RewriteRule ^(system\/author\/js\/) - [L]
|
||||
|
||||
# redirect all other direct requests to the following physical folders to the index.php so pages with same name work
|
||||
RewriteRule ^(system|content|data|settings) index.php [QSA,L]
|
||||
RewriteRule ^(system|content|data|settings|(media\/files\/)) index.php [QSA,L]
|
||||
|
||||
# disallow browsing other folders generally
|
||||
Options -Indexes
|
||||
|
@@ -2,10 +2,7 @@
|
||||
|
||||
Cyanine provides a lot of other settings for your content area. For example:
|
||||
|
||||
* Add an edit-button for github, gitlab or other plattforms.
|
||||
* Show the author.
|
||||
* Show the publish date.
|
||||
* Show the chapter numbers in the navigation.
|
||||
[ebook (EPUB, 496.65 KB)](media/files/ebook.epub){.tm-download file-epub}
|
||||
|
||||
The Cyanine theme supports all content elements like tables, images, notices or downloads. It also supports anchor-links next to headlines, so you can deep link to certain content sections of your page. You can activate the anchors in the system settings of Typemill.
|
||||
|
||||
|
@@ -180,7 +180,7 @@ class ControllerAuthorArticleApi extends ControllerAuthor
|
||||
|
||||
# check if it is a folder and if the folder has published pages.
|
||||
$message = false;
|
||||
if($this->item->elementType == 'folder')
|
||||
if($this->item->elementType == 'folder' && isset($this->item->folderContent))
|
||||
{
|
||||
foreach($this->item->folderContent as $folderContent)
|
||||
{
|
||||
|
@@ -6,6 +6,7 @@ use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Typemill\Models\ProcessImage;
|
||||
use Typemill\Models\ProcessFile;
|
||||
use Typemill\Models\WriteYaml;
|
||||
use Typemill\Controllers\ControllerAuthorBlockApi;
|
||||
|
||||
class ControllerAuthorMediaApi extends ControllerAuthor
|
||||
@@ -92,6 +93,69 @@ class ControllerAuthorMediaApi extends ControllerAuthor
|
||||
return $response->withJson(['errors' => 'file not found or file name invalid'],404);
|
||||
}
|
||||
|
||||
public function getFileRestrictions(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->params = $request->getParams();
|
||||
$this->uri = $request->getUri()->withUserInfo('');
|
||||
$restriction = 'all';
|
||||
|
||||
$userroles = $this->c->acl->getRoles();
|
||||
|
||||
if(isset($this->params['filename']) && $this->params['filename'] != '')
|
||||
{
|
||||
$writeYaml = new WriteYaml();
|
||||
$restrictions = $writeYaml->getYaml('media' . DIRECTORY_SEPARATOR . 'files', 'filerestrictions.yaml');
|
||||
if(isset($restrictions[$this->params['filename']]))
|
||||
{
|
||||
$restriction = $restrictions[$this->params['filename']];
|
||||
}
|
||||
}
|
||||
|
||||
return $response->withJson(['userroles' => $userroles, 'restriction' => $restriction]);
|
||||
}
|
||||
|
||||
public function updateFileRestrictions(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
$this->params = $request->getParams();
|
||||
$this->uri = $request->getUri()->withUserInfo('');
|
||||
$filename = isset($this->params['filename']) ? $this->params['filename'] : false;
|
||||
$role = isset($this->params['role']) ? $this->params['role'] : false;
|
||||
|
||||
if(!$filename OR !$role)
|
||||
{
|
||||
return $response->withJson(['errors' => ['message' => 'Filename or userrole is missing.']], 422);
|
||||
}
|
||||
|
||||
$userroles = $this->c->acl->getRoles();
|
||||
|
||||
if($role != 'all' AND !in_array($role, $userroles))
|
||||
{
|
||||
return $response->withJson(['errors' => ['message' => 'Userrole is unknown.']], 422);
|
||||
}
|
||||
|
||||
$writeYaml = new WriteYaml();
|
||||
$restrictions = $writeYaml->getYaml('media' . DIRECTORY_SEPARATOR . 'files', 'filerestrictions.yaml');
|
||||
if(!$restrictions)
|
||||
{
|
||||
$restrictions = [];
|
||||
}
|
||||
|
||||
if($role == 'all')
|
||||
{
|
||||
unset($restrictions[$filename]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$restrictions[$filename] = $role;
|
||||
}
|
||||
|
||||
$writeYaml->updateYaml('media' . DIRECTORY_SEPARATOR . 'files', 'filerestrictions.yaml', $restrictions);
|
||||
|
||||
return $response->withJson(['restrictions' => $restrictions]);
|
||||
}
|
||||
|
||||
public function createImage(Request $request, Response $response, $args)
|
||||
{
|
||||
# get params from call
|
||||
@@ -382,14 +446,14 @@ class ControllerAuthorMediaApi extends ControllerAuthor
|
||||
return $response->withJson(['errors' => ['message' => 'Please check if your media-folder exists and all folders inside are writable.']], 500);
|
||||
}
|
||||
|
||||
$tmpImage = $imageProcessor->createImage($imageData64, $desiredSizes);
|
||||
$tmpImage = $imageProcessor->createImage($imageData64, $videoID, $desiredSizes);
|
||||
|
||||
if(!$tmpImage)
|
||||
{
|
||||
return $response->withJson(array('errors' => 'could not create temporary image'));
|
||||
}
|
||||
|
||||
$imageUrl = $imageProcessor->publishImage($desiredSizes, $videoID);
|
||||
$imageUrl = $imageProcessor->publishImage();
|
||||
if($imageUrl)
|
||||
{
|
||||
$this->params['markdown'] = '{#' . $videoID. ' .' . $class . '}';
|
||||
|
@@ -313,6 +313,7 @@ class ControllerAuthorMetaApi extends ControllerAuthor
|
||||
# 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;
|
||||
$metaInput['noindex'] = (isset($metaInput['noindex']) && $metaInput['noindex'] !== null) ? $metaInput['noindex'] : false;
|
||||
|
||||
# input values are empty but entry in structure exists
|
||||
if(!$metaInput['hide'] && $metaInput['navtitle'] == "" && isset($extended[$this->item->urlRelWoF]))
|
||||
@@ -327,10 +328,12 @@ class ControllerAuthorMetaApi extends ControllerAuthor
|
||||
($this->hasChanged($metaInput, $metaPage['meta'], 'navtitle'))
|
||||
OR
|
||||
($this->hasChanged($metaInput, $metaPage['meta'], 'hide'))
|
||||
OR
|
||||
($this->hasChanged($metaInput, $metaPage['meta'], 'noindex'))
|
||||
)
|
||||
{
|
||||
# add new file data. Also makes sure that the value is set.
|
||||
$extended[$this->item->urlRelWoF] = ['hide' => $metaInput['hide'], 'navtitle' => $metaInput['navtitle']];
|
||||
$extended[$this->item->urlRelWoF] = ['hide' => $metaInput['hide'], 'navtitle' => $metaInput['navtitle'], 'noindex' => $metaInput['noindex']];
|
||||
|
||||
$structure = true;
|
||||
}
|
||||
|
144
system/Controllers/ControllerDownload.php
Normal file
144
system/Controllers/ControllerDownload.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Typemill\Controllers;
|
||||
|
||||
use Typemill\Models\WriteYaml;
|
||||
|
||||
class ControllerDownload extends ControllerShared
|
||||
{
|
||||
public function download($request, $response, $args)
|
||||
{
|
||||
|
||||
$filename = isset($args['params']) ? $args['params'] : false;
|
||||
$root = $this->c->get('settings')['rootPath'];
|
||||
$mediapath = 'media/files/';
|
||||
$filepath = $root . $mediapath;
|
||||
|
||||
if(!$filename)
|
||||
{
|
||||
die('the requested file does not exist.');
|
||||
}
|
||||
|
||||
# validate
|
||||
$allowedFiletypes = [];
|
||||
if(!$this->validate($filepath, $filename, $allowedFiletypes))
|
||||
{
|
||||
die('the requested file is not allowed.');
|
||||
}
|
||||
|
||||
$writeYaml = new WriteYaml();
|
||||
$restrictions = $writeYaml->getYaml('media' . DIRECTORY_SEPARATOR . 'files', 'filerestrictions.yaml');
|
||||
|
||||
if($restrictions && isset($restrictions[$mediapath . $filename]))
|
||||
{
|
||||
$allowedrole = $restrictions[$mediapath . $filename];
|
||||
|
||||
if(!isset($_SESSION['role']))
|
||||
{
|
||||
die("You have to be an authenticated $allowedrole to download this file.");
|
||||
}
|
||||
elseif(
|
||||
$_SESSION['role'] != 'administrator'
|
||||
AND $_SESSION['role'] != $allowedrole
|
||||
AND !$this->c->acl->inheritsRole($_SESSION['role'], $allowedrole)
|
||||
)
|
||||
{
|
||||
die("You have to be a $allowedrole to download this file.");
|
||||
}
|
||||
}
|
||||
|
||||
$file = $filepath . $filename;
|
||||
|
||||
# for now we only allow one download
|
||||
$this->sendDownload($file);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if the file exists and if
|
||||
* there is a permission (download dir) to download this file
|
||||
*
|
||||
* You should ALWAYS call this method if you don't want
|
||||
* somebody to download files not intended to be for the public.
|
||||
*
|
||||
* @param string $file GET parameter
|
||||
* @param array $allowedFiletypes (defined in the head of this file)
|
||||
* @return bool true if validation was successfull
|
||||
*/
|
||||
private function validate($path, $file, $allowedFiletypes)
|
||||
{
|
||||
$filepath = $path . $file;
|
||||
|
||||
# check if file exists
|
||||
if (!isset($filepath) || empty($filepath) || !file_exists($filepath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
# check allowed filetypes
|
||||
if(!empty($allowedFiletypes))
|
||||
{
|
||||
$fileAllowed = false;
|
||||
foreach ($allowedFiletypes as $filetype)
|
||||
{
|
||||
if (strpos($file, $filetype) === (strlen($file) - strlen($filetype)))
|
||||
{
|
||||
$fileAllowed = true; //ends with $filetype
|
||||
}
|
||||
}
|
||||
|
||||
if (!$fileAllowed) return false;
|
||||
}
|
||||
|
||||
# check download directory
|
||||
if (strpos($file, '..') !== false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download function.
|
||||
* Sets the HTTP header and supplies the given file
|
||||
* as a download to the browser.
|
||||
*
|
||||
* @param string $file path to file
|
||||
*/
|
||||
private function sendDownload($file)
|
||||
{
|
||||
# Parse information
|
||||
$pathinfo = pathinfo($file);
|
||||
$extension = strtolower($pathinfo['extension']);
|
||||
$mimetype = null;
|
||||
|
||||
# Get mimetype for extension
|
||||
# This list can be extended as you need it.
|
||||
# A good start to find mimetypes is the apache mime.types list
|
||||
# http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
||||
switch ($extension) {
|
||||
case 'zip': $mimetype = "application/zip"; break;
|
||||
default: $mimetype = "application/force-download";
|
||||
}
|
||||
|
||||
# Required for some browsers like Safari and IE
|
||||
if (ini_get('zlib.output_compression'))
|
||||
{
|
||||
ini_set('zlib.output_compression', 'Off');
|
||||
}
|
||||
|
||||
header('Pragma: public');
|
||||
header('Content-Encoding: none');
|
||||
header('Expires: 0');
|
||||
header('Accept-Ranges: bytes'); # Allow support for download resume
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($file)) . ' GMT');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header('Cache-Control: private', false); # required for some browsers
|
||||
header('Content-Type: application/zip');
|
||||
header('Content-Disposition: attachment; filename="'.basename($file).'";'); # Make the browser display the Save As dialog
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
header('Content-Length: '.filesize($file));
|
||||
ob_end_flush();
|
||||
readfile($file); # This is necessary in order to get it to actually download the file, otherwise it will be 0Kb
|
||||
}
|
||||
}
|
@@ -177,7 +177,7 @@ abstract class ControllerShared
|
||||
$pagetreeLive = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft = false );
|
||||
|
||||
# if there is content, then get the content details
|
||||
if(count($pagetreeLive) > 0)
|
||||
if($pagetreeLive && count($pagetreeLive) > 0)
|
||||
{
|
||||
# get the extended structure files with changes like navigation title or hidden pages
|
||||
$yaml = new writeYaml();
|
||||
@@ -295,28 +295,37 @@ abstract class ControllerShared
|
||||
$opts = array(
|
||||
'http'=>array(
|
||||
'method'=>"GET",
|
||||
'ignore_errors' => true,
|
||||
'timeout' => 5
|
||||
)
|
||||
);
|
||||
|
||||
$context = stream_context_create($opts);
|
||||
$context = stream_context_create($opts);
|
||||
|
||||
$resultBing = file_get_contents($pingBingUrl, false, $context);
|
||||
$resultGoogle = file_get_contents($pingGoogleUrl, false, $context);
|
||||
$responseBing = file_get_contents($pingBingUrl, false, $context);
|
||||
$responseGoogle = file_get_contents($pingGoogleUrl, false, $context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function generateUrlSets($structureLive)
|
||||
{
|
||||
{
|
||||
$urlset = '';
|
||||
|
||||
|
||||
foreach($structureLive as $item)
|
||||
{
|
||||
if($item->elementType == 'folder')
|
||||
if($item->elementType == 'folder' && isset($item->noindex) && $item->noindex === true)
|
||||
{
|
||||
$urlset .= $this->generateUrlSets($item->folderContent, $urlset);
|
||||
}
|
||||
elseif($item->elementType == 'folder')
|
||||
{
|
||||
$urlset = $this->addUrlSet($urlset, $item->urlAbs);
|
||||
$urlset .= $this->generateUrlSets($item->folderContent, $urlset);
|
||||
$urlset .= $this->generateUrlSets($item->folderContent, $urlset);
|
||||
}
|
||||
elseif(isset($item->noindex) && $item->noindex === true )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -154,8 +154,9 @@ class Folder
|
||||
# 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->name = ($extended[$item->urlRelWoF]['navtitle'] != '') ? $extended[$item->urlRelWoF]['navtitle'] : $item->name;
|
||||
$item->hide = ($extended[$item->urlRelWoF]['hide'] === true) ? true : false;
|
||||
$item->noindex = (isset($extended[$item->urlRelWoF]['noindex']) && $extended[$item->urlRelWoF]['noindex'] === true) ? true : false;
|
||||
}
|
||||
|
||||
# sort posts in descending order
|
||||
@@ -217,8 +218,9 @@ class Folder
|
||||
# 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->name = ($extended[$item->urlRelWoF]['navtitle'] != '') ? $extended[$item->urlRelWoF]['navtitle'] : $item->name;
|
||||
$item->hide = ($extended[$item->urlRelWoF]['hide'] === true) ? true : false;
|
||||
$item->noindex = (isset($extended[$item->urlRelWoF]['noindex']) && $extended[$item->urlRelWoF]['noindex'] === true) ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -36,7 +36,7 @@ $app->post('/api/v1/block', ControllerAuthorBlockApi::class . ':addBlock')->setN
|
||||
$app->put('/api/v1/block', ControllerAuthorBlockApi::class . ':updateBlock')->setName('api.block.update')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/block', ControllerAuthorBlockApi::class . ':deleteBlock')->setName('api.block.delete')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/moveblock', ControllerAuthorBlockApi::class . ':moveBlock')->setName('api.block.move')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/video', ControllerAuthorBlockApi::class . ':saveVideoImage')->setName('api.video.save')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/video', ControllerAuthorMediaApi::class . ':saveVideoImage')->setName('api.video.save')->add(new RestrictApiAccess($container['router']));
|
||||
|
||||
$app->get('/api/v1/medialib/images', ControllerAuthorMediaApi::class . ':getMediaLibImages')->setName('api.medialibimg.get')->add(new RestrictApiAccess($container['router']));
|
||||
$app->get('/api/v1/medialib/files', ControllerAuthorMediaApi::class . ':getMediaLibFiles')->setName('api.medialibfiles.get')->add(new RestrictApiAccess($container['router']));
|
||||
@@ -44,6 +44,8 @@ $app->get('/api/v1/image', ControllerAuthorMediaApi::class . ':getImage')->setNa
|
||||
$app->post('/api/v1/image', ControllerAuthorMediaApi::class . ':createImage')->setName('api.image.create')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/image', ControllerAuthorMediaApi::class . ':publishImage')->setName('api.image.publish')->add(new RestrictApiAccess($container['router']));
|
||||
$app->delete('/api/v1/image', ControllerAuthorMediaApi::class . ':deleteImage')->setName('api.image.delete')->add(new RestrictApiAccess($container['router']));
|
||||
$app->get('/api/v1/filerestrictions', ControllerAuthorMediaApi::class . ':getFileRestrictions')->setName('api.file.getrestrictions')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/filerestrictions', ControllerAuthorMediaApi::class . ':updateFileRestrictions')->setName('api.file.updaterestrictions')->add(new RestrictApiAccess($container['router']));
|
||||
$app->get('/api/v1/file', ControllerAuthorMediaApi::class . ':getFile')->setName('api.file.get')->add(new RestrictApiAccess($container['router']));
|
||||
$app->post('/api/v1/file', ControllerAuthorMediaApi::class . ':uploadFile')->setName('api.file.upload')->add(new RestrictApiAccess($container['router']));
|
||||
$app->put('/api/v1/file', ControllerAuthorMediaApi::class . ':publishFile')->setName('api.file.publish')->add(new RestrictApiAccess($container['router']));
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
use Typemill\Controllers\ControllerAuthorEditor;
|
||||
use Typemill\Controllers\ControllerSettings;
|
||||
use Typemill\Controllers\ControllerDownload;
|
||||
use Typemill\Controllers\ControllerFrontendWebsite;
|
||||
use Typemill\Controllers\ControllerFrontendForms;
|
||||
use Typemill\Controllers\ControllerFrontendAuth;
|
||||
@@ -64,6 +65,8 @@ $app->get('/tm/content/raw[/{params:.*}]', ControllerAuthorEditor::class . ':sho
|
||||
$app->get('/tm/content/visual[/{params:.*}]', ControllerAuthorEditor::class . ':showBlox')->setName('content.visual')->add(new accessMiddleware($container['router'], $container['acl'], 'content', 'view'));
|
||||
$app->get('/tm/content[/{params:.*}]', ControllerAuthorEditor::class . ':showEmpty')->setName('content.empty')->add(new accessMiddleware($container['router'], $container['acl'], 'content', 'view'));
|
||||
|
||||
$app->get('/media/files[/{params:.*}]', ControllerDownload::class . ':download')->setName('download.file');
|
||||
|
||||
foreach($routes as $pluginRoute)
|
||||
{
|
||||
$method = $pluginRoute['httpMethod'];
|
||||
|
@@ -17,7 +17,7 @@ const contentComponent = Vue.component('content-block', {
|
||||
'<div v-if="newblock" class="newblock-info">Choose a content-type <button class="newblock-close" @click.prevent="closeNewBlock($event)">close</button></div>' +
|
||||
'<div class="blox-wrapper" :class="{ editactive: edit }">' +
|
||||
'<div class="sideaction" slot="header" v-if="body">' +
|
||||
'<button class="add" :disabled="disabled" :title="\'add content-block\'|translate" @mousedown.prevent="disableSort()" @click.prevent="addNewBlock($event)"><svg class="icon icon-plus"><use xlink:href="#icon-plus"></use></svg></button>' +
|
||||
'<button class="add" :disabled="disabled" :title="\'add content-block\'|translate" @mousedown.prevent="disableSort()" @click.prevent="addNewBlock($event)"><svg class="icon icon-plus"><use xlink:href="#icon-plus"></use></svg></button>' +
|
||||
'<button class="delete" :disabled="disabled" :title="\'delete content-block\'|translate" @mousedown.prevent="disableSort()" @click.prevent="deleteBlock($event)"><svg class="icon icon-close"><use xlink:href="#icon-close"></use></svg></button>' +
|
||||
'</div>' +
|
||||
'<div class="background-helper" @keyup.enter="submitBlock" @click="getData">' +
|
||||
@@ -1769,7 +1769,12 @@ const fileComponent = Vue.component('file-component', {
|
||||
'<label for="filetitle">{{ \'Title\'|translate }}: </label>' +
|
||||
'<input name="filetitle" type="text" placeholder="Add a title for the download-link" v-model="filetitle" @input="createmarkdown" max="64" />' +
|
||||
'<input title="fileid" type="hidden" placeholder="id" v-model="fileid" @input="createmarkdown" max="140" />' +
|
||||
'</div></div>',
|
||||
'<label for="filerestriction">{{ \'Access for\'|translate }}: </label>' +
|
||||
'<select name="filerestriction" v-model="selectedrole" @change="updaterestriction">' +
|
||||
'<option disabled value="">{{ \'Please select\'|translate }}</option>' +
|
||||
'<option v-for="role in userroles">{{ role }}</option>' +
|
||||
'</select>' +
|
||||
'</div></div>',
|
||||
data: function(){
|
||||
return {
|
||||
maxsize: 20, // megabyte
|
||||
@@ -1779,7 +1784,9 @@ const fileComponent = Vue.component('file-component', {
|
||||
filetitle: '',
|
||||
fileextension: '',
|
||||
fileurl: '',
|
||||
fileid: ''
|
||||
fileid: '',
|
||||
userroles: ['all'],
|
||||
selectedrole: '',
|
||||
}
|
||||
},
|
||||
mounted: function(){
|
||||
@@ -1814,6 +1821,27 @@ const fileComponent = Vue.component('file-component', {
|
||||
this.fileurl = fileurl[0].slice(1,-1);
|
||||
}
|
||||
}
|
||||
|
||||
self = this;
|
||||
|
||||
myaxios.get('/api/v1/filerestrictions',{
|
||||
params: {
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
'filename': this.fileurl,
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
self.userroles = self.userroles.concat(response.data.userroles);
|
||||
self.selectedrole = response.data.restriction;
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
openmedialib: function()
|
||||
@@ -1831,6 +1859,27 @@ const fileComponent = Vue.component('file-component', {
|
||||
{
|
||||
this.$emit('updatedMarkdown', event.target.value);
|
||||
},
|
||||
updaterestriction: function()
|
||||
{
|
||||
myaxios.post('/api/v1/filerestrictions',{
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
'filename': this.fileurl,
|
||||
'role': this.selectedrole,
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
console.info(this.selectedrole);
|
||||
},
|
||||
createmarkdown: function()
|
||||
{
|
||||
var errors = false;
|
||||
@@ -1939,9 +1988,8 @@ for(var i = 0; i < formatConfig.length; i++)
|
||||
}
|
||||
|
||||
let editor = new Vue({
|
||||
delimiters: ['${', '}'],
|
||||
delimiters: ['${', '}'],
|
||||
el: '#blox',
|
||||
/* components: componentList, */
|
||||
data: {
|
||||
errors: [],
|
||||
root: document.getElementById("main").dataset.url,
|
||||
@@ -1960,7 +2008,7 @@ let editor = new Vue({
|
||||
bloxOverlay: false,
|
||||
sortdisabled: false,
|
||||
showEditor: 'show',
|
||||
formats: activeFormats
|
||||
formats: activeFormats,
|
||||
},
|
||||
mounted: function(){
|
||||
|
||||
@@ -1969,110 +2017,109 @@ let editor = new Vue({
|
||||
var self = this;
|
||||
|
||||
myaxios.post('/api/v1/article/html',{
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
var contenthtml = response.data.data;
|
||||
self.title = contenthtml[0];
|
||||
self.html = contenthtml;
|
||||
var initialcontent = document.getElementById("initial-content");
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response){
|
||||
var contenthtml = response.data.data;
|
||||
self.title = contenthtml[0];
|
||||
self.html = contenthtml;
|
||||
var initialcontent = document.getElementById("initial-content");
|
||||
|
||||
initialcontent.parentNode.removeChild(initialcontent);
|
||||
|
||||
initialcontent.parentNode.removeChild(initialcontent);
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
self.errors.title = error.response.errors;
|
||||
if(error.response)
|
||||
{
|
||||
self.errors.title = error.response.errors;
|
||||
}
|
||||
});
|
||||
|
||||
myaxios.post('/api/v1/article/markdown',{
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
self.markdown = response.data.data;
|
||||
|
||||
/* activate math plugin */
|
||||
if (typeof renderMathInElement === "function") {
|
||||
self.$nextTick(function () {
|
||||
renderMathInElement(document.getElementById("blox"));
|
||||
});
|
||||
}
|
||||
|
||||
/* check for youtube videos */
|
||||
if (typeof typemillUtilities !== "undefined") {
|
||||
setTimeout(function(){
|
||||
self.markdown = response.data.data;
|
||||
|
||||
/* activate math plugin */
|
||||
if (typeof renderMathInElement === "function") {
|
||||
self.$nextTick(function () {
|
||||
typemillUtilities.start();
|
||||
renderMathInElement(document.getElementById("blox"));
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
/* check for youtube videos */
|
||||
if (typeof typemillUtilities !== "undefined") {
|
||||
setTimeout(function(){
|
||||
self.$nextTick(function () {
|
||||
typemillUtilities.start();
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
self.errors.title = error.response.errors;
|
||||
if(error.response)
|
||||
{
|
||||
self.errors.title = error.response.errors;
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
onStart: function()
|
||||
{
|
||||
|
||||
},
|
||||
moveBlock: function(evt)
|
||||
{
|
||||
publishController.errors.message = false;
|
||||
methods: {
|
||||
onStart: function()
|
||||
{
|
||||
|
||||
var self = this;
|
||||
},
|
||||
moveBlock: function(evt)
|
||||
{
|
||||
publishController.errors.message = false;
|
||||
|
||||
myaxios.put('/api/v1/moveblock',{
|
||||
'url': document.getElementById("path").value,
|
||||
'old_index': evt.oldIndex,
|
||||
'new_index': evt.newIndex,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response) {
|
||||
var self = this;
|
||||
|
||||
myaxios.put('/api/v1/moveblock',{
|
||||
'url': document.getElementById("path").value,
|
||||
'old_index': evt.oldIndex,
|
||||
'new_index': evt.newIndex,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
self.freeze = false;
|
||||
self.freeze = false;
|
||||
|
||||
self.markdown = response.data.markdown;
|
||||
self.blockMarkdown = '';
|
||||
self.blockType = '';
|
||||
self.markdown = response.data.markdown;
|
||||
self.blockMarkdown = '';
|
||||
self.blockType = '';
|
||||
|
||||
if(response.data.toc)
|
||||
{
|
||||
self.html.splice(response.data.toc.id, 1, response.data.toc);
|
||||
}
|
||||
if(response.data.toc)
|
||||
{
|
||||
self.html.splice(response.data.toc.id, 1, response.data.toc);
|
||||
}
|
||||
|
||||
publishController.publishDisabled = false;
|
||||
publishController.publishResult = "";
|
||||
publishController.publishDisabled = false;
|
||||
publishController.publishResult = "";
|
||||
|
||||
/* update the navigation and mark navigation item as modified */
|
||||
navi.getNavi();
|
||||
/* update the navigation and mark navigation item as modified */
|
||||
navi.getNavi();
|
||||
|
||||
/* update the math if plugin is there */
|
||||
self.checkMath(params.new_index+1);
|
||||
})
|
||||
.catch(function (error)
|
||||
/* update the math if plugin is there */
|
||||
self.checkMath(params.new_index+1);
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
publishController.publishDisabled = false;
|
||||
}
|
||||
if(error.response)
|
||||
{
|
||||
if(error)
|
||||
publishController.publishDisabled = false;
|
||||
if(error.response)
|
||||
{
|
||||
publishController.errors.message = error.response.data.errors.message;
|
||||
}
|
||||
});
|
||||
publishController.errors.message = error.response.data.errors.message;
|
||||
}
|
||||
});
|
||||
},
|
||||
setData: function(event, blocktype, body)
|
||||
{
|
||||
|
@@ -119,7 +119,7 @@ Vue.component('tab-meta', {
|
||||
})
|
||||
|
||||
let meta = new Vue({
|
||||
delimiters: ['${', '}'],
|
||||
delimiters: ['${', '}'],
|
||||
el: '#metanav',
|
||||
data: function () {
|
||||
return {
|
||||
|
@@ -36,10 +36,10 @@ meta:
|
||||
label: Hide
|
||||
checkboxlabel: Hide page from navigation
|
||||
class: medium
|
||||
nofollow:
|
||||
noindex:
|
||||
type: checkbox
|
||||
label: Nofollow
|
||||
checkboxlabel: Add no-follow and exclude from sitemap
|
||||
label: Noindex
|
||||
checkboxlabel: Add noindex tag and exclude from sitemap
|
||||
class: medium
|
||||
fieldsetauthor:
|
||||
type: fieldset
|
||||
|
@@ -1,5 +1,5 @@
|
||||
name: Cyanine Theme
|
||||
version: 1.1.8
|
||||
version: 1.1.9
|
||||
description: Cyanine is a modern and flexible multi-purpose theme and the standard theme for typemill.
|
||||
author: Trendschau
|
||||
homepage: https://trendschau.net
|
||||
@@ -138,6 +138,20 @@ forms:
|
||||
type: text
|
||||
label: Teaser 2 Label
|
||||
fieldsize: half
|
||||
teaser3title:
|
||||
type: text
|
||||
label: Teaser 3 Title
|
||||
teaser3text:
|
||||
type: text
|
||||
label: Teaser 3 Text
|
||||
teaser3link:
|
||||
type: text
|
||||
label: Teaser 3 Link
|
||||
fieldsize: half
|
||||
teaser3label:
|
||||
type: text
|
||||
label: Teaser 3 Label
|
||||
fieldsize: half
|
||||
|
||||
landingpageContrast:
|
||||
type: fieldset
|
||||
|
@@ -1,16 +1,27 @@
|
||||
<section class="landingpageteaser w-100 dib tc bl br bb">
|
||||
|
||||
<div class="mw7 pv6 ph3 center flex-m flex-l justify-between">
|
||||
<div class="mw5 center ml0-l mr0-l mv4 dib">
|
||||
<h2 class="f2">{{ settings.themes.cyanine.teaser1title }}</h2>
|
||||
<p class="f5 f4-ns fw3 lh-copy">{{ settings.themes.cyanine.teaser1text }}</p>
|
||||
<a href="{{ settings.themes.cyanine.teaser1link }}" class="button link dim w-100 dib pa3 ma1 ba">{{ settings.themes.cyanine.teaser1label }}</a>
|
||||
</div>
|
||||
<div class="mw5 center ml0-l mr0-l mv4 dib">
|
||||
<h2 class="f2">{{ settings.themes.cyanine.teaser2title }}</h2>
|
||||
<p class="f5 f4-ns fw3 lh-copy">{{ settings.themes.cyanine.teaser2text }}</p>
|
||||
<a href="{{ settings.themes.cyanine.teaser2link }}" class="button link dim w-100 dib pa3 ma1 ba">{{ settings.themes.cyanine.teaser2label }}</a>
|
||||
</div>
|
||||
<div class="mw8 pv6 ph3 center flex-m flex-l justify-around">
|
||||
{% if settings.themes.cyanine.teaser1title %}
|
||||
<div class="mw5 center ml0-l mr0-l mv4 dib">
|
||||
<h2 class="f2">{{ settings.themes.cyanine.teaser1title }}</h2>
|
||||
<p class="f5 f4-ns fw3 lh-copy">{{ settings.themes.cyanine.teaser1text }}</p>
|
||||
<a href="{{ settings.themes.cyanine.teaser1link }}" class="button link dim w-100 dib pa3 ma1 ba">{{ settings.themes.cyanine.teaser1label }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if settings.themes.cyanine.teaser2title %}
|
||||
<div class="mw5 center ml0-l mr0-l mv4 dib">
|
||||
<h2 class="f2">{{ settings.themes.cyanine.teaser2title }}</h2>
|
||||
<p class="f5 f4-ns fw3 lh-copy">{{ settings.themes.cyanine.teaser2text }}</p>
|
||||
<a href="{{ settings.themes.cyanine.teaser2link }}" class="button link dim w-100 dib pa3 ma1 ba">{{ settings.themes.cyanine.teaser2label }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if settings.themes.cyanine.teaser3title %}
|
||||
<div class="mw5 center ml0-l mr0-l mv4 dib">
|
||||
<h2 class="f2">{{ settings.themes.cyanine.teaser3title }}</h2>
|
||||
<p class="f5 f4-ns fw3 lh-copy">{{ settings.themes.cyanine.teaser3text }}</p>
|
||||
<a href="{{ settings.themes.cyanine.teaser3link }}" class="button link dim w-100 dib pa3 ma1 ba">{{ settings.themes.cyanine.teaser3label }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</section>
|
@@ -9,6 +9,10 @@
|
||||
<meta name="author" content="{{ metatabs.meta.author }}" />
|
||||
<meta name="generator" content="TYPEMILL" />
|
||||
<meta name="msapplication-TileColor" content="#F9F8F6" />
|
||||
|
||||
{% if metatabs.meta.noindex %}
|
||||
<meta name="robots" content="noindex">
|
||||
{% endif %}
|
||||
|
||||
{% if favicon %}
|
||||
<meta name="msapplication-TileColor" content="#F9F8F6" />
|
||||
|
Reference in New Issue
Block a user