mirror of
https://github.com/typemill/typemill.git
synced 2025-07-18 04:51:13 +02:00
Finished svg-sanitizer and error messages in blox editor
This commit is contained in:
@@ -6,7 +6,10 @@
|
||||
"homepage": "https://typemill.net",
|
||||
"license": "MIT",
|
||||
"config": {
|
||||
"vendor-dir": "system/vendor"
|
||||
"vendor-dir": "system/vendor",
|
||||
"allow-plugins": {
|
||||
"composer/installers": true
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
@@ -26,7 +29,7 @@
|
||||
"laminas/laminas-permissions-acl": "^2.10",
|
||||
"akrabat/proxy-detection-middleware": "^1.0.0",
|
||||
"gregwar/captcha": "1.*"
|
||||
},
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Typemill\\": "system/typemill/",
|
||||
|
@@ -1 +1 @@
|
||||
["# ToDos Version 2","[TOC]","## System settings","* DONE: Migrate from backend to frontend with vue and api\n* DONE: Redesign\n* DONE: License feature\n* DONE: Enhance with plugins","----","## Visual Editor","* DONE: Refactor and redesign\n* DONE: Fix toc component in new block\n* DONE: Fix hr component in new block\n* DONE: finish shortcode component\n* DONE: Fix inline formats\n* DONE: fix lenght of page\n* DONE: Fix design of new block at the end (background color)\n* DONE: Move Block\n* DONE: Fix headline design\n* DONE: Fix save on two enter\n* DONE: fix quote design\n* DONE: Fix toc preview\n* DONE: disable enable \n* DONE: Add load sign (from navigation)\n* DONE: File is not published from tmp to media\/files if you save the block.\n* ToDo: Customfields not styled yet.\n* ToDo: Warn if open another block\n* ToDo: finish youtube component","## Raw Editor","* DONE: Refactor and redesign\n* DONE: Integrate highlighting","## Navigation","* DONE: Refactor and redesign\n* DONE: fix status in navigation\n* DONE: refresh navigation after changes\n* ToDo: fix error messages\n* ToDo: Wrong frontend navigation if unpublished pages","## Publish Controller","* DONE: Refactor and redesign\n* DONE: Create \n* DONE: publish\n* DONE: unpublish\n* DONE: discard\n* DONE: delete\n* DONE: save draft\n* DONE: switch to raw","## Meta Tabs","* DONE: Refactor and redesign\n* DONE: Enhance with plugins","## Medialib","* DONE: Refactor and redesign","## Posts","* DONE: Refactor and redesign","## Plugins","* Asset Class in progress","## Frontend","* DONE: Refactor\n* DONE: Test restrictions","## Other big tasks","* DONE: System setup\n* DONE: Recover Password","## Medium tasks","* DONE: Merge processAssets modell\n* DONE: Table of content duplicated for published pages\n* DONE: Session handling: csrf fail and session start error if restrictions are active\n* DONE: Image and files for meta","## Open tasks","* DONE: Sitemap and ping\n* DONE: Version check\n* SVG checker: https:\/\/github.com\/TribalSystems\/SVG-Sanitizer\n* Markdown secure rendering\n* Responsive design\n* Backend form builder\n* Proxy support\n* Image generation on the fly\n* Captcha integration\n* Fix error api systemnavi\n* Reference feature\n* Typemill Utilities\n* Clear cache\n* Show security Log\n* User search only for +10 users","## Cleanups:","* DONE: Events\n* Error messages\n* Translations","## Info: Select userroles","* Userroles for file restriction: in vue-blox-components loaded via api\n* Userroles for userfields: in php model user getUserFields()\n* Userroles for meta: in php controller apiAuthorMeta getMeta()\n* Plugins and themes: in php model extension getThemeDefinitions()","## Info: License Check","* On activation in apiControllerExtension. It checks the license in yaml.\n* In plugin php code with setPremiumLicense\n* In static plugins, it checks manual premium list and method setPremiumLicense and more "]
|
||||
["# ToDos Version 2","[TOC]","## System settings","* DONE: Migrate from backend to frontend with vue and api\n* DONE: Redesign\n* DONE: License feature\n* DONE: Enhance with plugins","----","## Visual Editor","* DONE: Refactor and redesign\n* DONE: Fix toc component in new block\n* DONE: Fix hr component in new block\n* DONE: finish shortcode component\n* DONE: Fix inline formats\n* DONE: fix lenght of page\n* DONE: Fix design of new block at the end (background color)\n* DONE: Move Block\n* DONE: Fix headline design\n* DONE: Fix save on two enter\n* DONE: fix quote design\n* DONE: Fix toc preview\n* DONE: disable enable \n* DONE: Add load sign (from navigation)\n* DONE: File is not published from tmp to media\/files if you save the block.\n* ToDo: Customfields not styled yet.\n* ToDo: Warn if open another block\n* ToDo: finish youtube component","## Raw Editor","* DONE: Refactor and redesign\n* DONE: Integrate highlighting","## Navigation","* DONE: Refactor and redesign\n* DONE: fix status in navigation\n* DONE: refresh navigation after changes\n* ToDo: fix error messages\n* ToDo: Wrong frontend navigation if unpublished pages","## Publish Controller","* DONE: Refactor and redesign\n* DONE: Create \n* DONE: publish\n* DONE: unpublish\n* DONE: discard\n* DONE: delete\n* DONE: save draft\n* DONE: switch to raw","## Meta Tabs","* DONE: Refactor and redesign\n* DONE: Enhance with plugins","## Medialib","* DONE: Refactor and redesign","## Posts","* DONE: Refactor and redesign","## Plugins","* Asset Class in progress","## Frontend","* DONE: Refactor\n* DONE: Test restrictions","## Other big tasks","* DONE: System setup\n* DONE: Recover Password","## Medium tasks","* DONE: Merge processAssets modell\n* DONE: Table of content duplicated for published pages\n* DONE: Session handling: csrf fail and session start error if restrictions are active\n* DONE: Image and files for meta","## Open tasks","* DONE: Sitemap and ping\n* DONE: Version check\n* DONE: Proxy support\n* DONE: SVG checker: https:\/\/github.com\/TribalSystems\/SVG-Sanitizer\n* Markdown secure rendering\n* Responsive design\n* Backend form builder\n* Image generation on the fly\n* Captcha integration\n* Fix error api systemnavi\n* Reference feature\n* Typemill Utilities\n* Clear cache\n* Show security Log\n* User search only for +10 users","## Cleanups:","* DONE: Events\n* Error messages\n* Translations","## Info: Select userroles","* Userroles for file restriction: in vue-blox-components loaded via api\n* Userroles for userfields: in php model user getUserFields()\n* Userroles for meta: in php controller apiAuthorMeta getMeta()\n* Plugins and themes: in php model extension getThemeDefinitions()","## Info: License Check","* On activation in apiControllerExtension. It checks the license in yaml.\n* In plugin php code with setPremiumLicense\n* In static plugins, it checks manual premium list and method setPremiumLicense and more "]
|
1
data/navigation/navi-live.txt
Normal file
1
data/navigation/navi-live.txt
Normal file
File diff suppressed because one or more lines are too long
57
media/tmp/1528492471.svg
Normal file
57
media/tmp/1528492471.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 26 KiB |
@@ -206,45 +206,46 @@ class ControllerApiFile extends Controller
|
||||
|
||||
$media = new Media();
|
||||
|
||||
$fileinfo = $fileProcessor->storeFile($params['file'], $params['name']);
|
||||
$filePath = str_replace('media/files', 'media/tmp', $fileinfo['url']);
|
||||
|
||||
if($fileinfo)
|
||||
$fileinfo = $media->storeFile($params['file'], $params['name']);
|
||||
if(!$fileinfo OR !isset($fileinfo['url']))
|
||||
{
|
||||
# if the previous check of the mtype with the base64 string failed, then do it now again with the temporary file
|
||||
if(!$mtype)
|
||||
{
|
||||
$fullPath = $this->settings['rootPath'] . $filePath;
|
||||
$finfo = finfo_open( FILEINFO_MIME_TYPE );
|
||||
$mtype = @finfo_file( $finfo, $fullPath );
|
||||
finfo_close($finfo);
|
||||
|
||||
if(!$mtype OR !$this->checkAllowedMimeTypes($mtype, $extension))
|
||||
{
|
||||
$media->clearTempFolder();
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'The mime-type is missing, not allowed or does not fit to the file extension.'
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(422);
|
||||
}
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'File has been stored',
|
||||
'fileinfo' => $fileinfo,
|
||||
'filepath' => $filePath
|
||||
'message' => 'We Could not store file to temporary folder.',
|
||||
'fullerrors' => $media->errors
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(422);
|
||||
}
|
||||
|
||||
# if the previous check of the mtype with the base64 string failed, then do it now again with the temporary file
|
||||
if(!$mtype)
|
||||
{
|
||||
$fullPath = $this->settings['rootPath'] . $filePath;
|
||||
$finfo = finfo_open( FILEINFO_MIME_TYPE );
|
||||
$mtype = @finfo_file( $finfo, $fullPath );
|
||||
finfo_close($finfo);
|
||||
|
||||
if(!$mtype OR !$this->checkAllowedMimeTypes($mtype, $extension))
|
||||
{
|
||||
$media->clearTempFolder();
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'The mime-type is missing, not allowed or does not fit to the file extension.'
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(422);
|
||||
}
|
||||
}
|
||||
|
||||
$filePath = str_replace('media/files', 'media/tmp', $fileinfo['url']);
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'message' => 'Could not store file to temporary folder.'
|
||||
'message' => 'File has been stored',
|
||||
'fileinfo' => $fileinfo,
|
||||
'filepath' => $filePath
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json')->withStatus(422);
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
public function publishFile(Request $request, Response $response, $args)
|
||||
|
@@ -155,7 +155,7 @@ class ControllerApiImage extends Controller
|
||||
|
||||
# check if image name already exisits in live folder and create an unique name (do not overwrite existing files)
|
||||
$storage = new StorageWrapper('\Typemill\Models\Storage');
|
||||
$uniqueImageName = $storage->createUniqueImageName($img->getFilename(), $img->getExtension());
|
||||
$uniqueImageName = $storage->createUniqueImageName($media->getFilename(), $media->getExtension());
|
||||
$media->setFilename($uniqueImageName);
|
||||
|
||||
# store the original image
|
||||
|
@@ -7,6 +7,7 @@ use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Slim\Routing\RouteContext;
|
||||
use Typemill\Models\Navigation;
|
||||
use Typemill\Models\Content;
|
||||
use Typemill\Models\SvgSanitizer;
|
||||
use Typemill\Events\OnPagetreeLoaded;
|
||||
use Typemill\Events\OnItemLoaded;
|
||||
use Typemill\Events\OnMarkdownLoaded;
|
||||
@@ -17,6 +18,9 @@ class ControllerWebAuthor extends Controller
|
||||
{
|
||||
public function showBlox(Request $request, Response $response, $args)
|
||||
{
|
||||
|
||||
$svg = new SvgSanitizer();
|
||||
|
||||
# get url for requested page
|
||||
$url = isset($args['route']) ? '/' . $args['route'] : '/';
|
||||
$urlinfo = $this->c->get('urlinfo');
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace Typemill\Models;
|
||||
|
||||
use Typemill\Models\Folder;
|
||||
use Typemill\Models\SvgSanitizer;
|
||||
use Typemill\Static\Slug;
|
||||
|
||||
class Media
|
||||
@@ -92,10 +93,6 @@ class Media
|
||||
|
||||
public function decode(string $file)
|
||||
{
|
||||
$fileParts = explode(";base64,", $file);
|
||||
$fileType = explode("/", $fileParts[0]);
|
||||
$fileData = base64_decode($fileParts[1]);
|
||||
|
||||
$fileParts = explode(";base64,", $file);
|
||||
|
||||
if(!isset($fileParts[0]) OR !isset($fileParts[1]))
|
||||
@@ -106,7 +103,7 @@ class Media
|
||||
}
|
||||
|
||||
$type = explode("/", $fileParts[0]);
|
||||
$this->filetype = strtolower($fileType[0]);
|
||||
$this->filetype = strtolower($type[1]);
|
||||
$this->filedata = base64_decode($fileParts[1]);
|
||||
|
||||
return true;
|
||||
@@ -195,6 +192,29 @@ class Media
|
||||
|
||||
$this->decode($file);
|
||||
|
||||
if($this->extension == "svg")
|
||||
{
|
||||
$svg = new SvgSanitizer();
|
||||
|
||||
$loaded = $svg->loadSVG($this->filedata);
|
||||
if($loaded === false)
|
||||
{
|
||||
$this->errors[] = 'We could not load the svg file, it is probably corrupted.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$svg->sanitize();
|
||||
$sanitized = $svg->saveSVG();
|
||||
if($sanitized === false)
|
||||
{
|
||||
$this->errors[] = 'We could not create a sanitized version of the svg, it probably has invalid content.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->filedata = $sanitized;
|
||||
}
|
||||
|
||||
|
||||
$fullpath = $this->getFullPath();
|
||||
|
||||
if($this->filedata !== false && file_put_contents($fullpath, $this->filedata))
|
||||
@@ -301,6 +321,28 @@ class Media
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->extension == "svg")
|
||||
{
|
||||
$svg = new SvgSanitizer();
|
||||
|
||||
$loaded = $svg->loadSVG($this->filedata);
|
||||
if($loaded === false)
|
||||
{
|
||||
$this->errors[] = 'We could not load the svg file, it is probably corrupted.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$svg->sanitize();
|
||||
$sanitized = $svg->saveSVG();
|
||||
if($sanitized === false)
|
||||
{
|
||||
$this->errors[] = 'We could not create a sanitized version of the svg, it probably has invalid content.';
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->filedata = $sanitized;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -335,8 +377,9 @@ class Media
|
||||
public function saveOriginal($destinationfolder = 'ORIGINAL')
|
||||
{
|
||||
$path = $this->tmpFolder . $destinationfolder . '+' . $this->filename . '.' . $this->extension;
|
||||
|
||||
if(!file_put_contents($path, $this->filedata))
|
||||
|
||||
$result = file_put_contents($path, $this->filedata);
|
||||
if($result === false)
|
||||
{
|
||||
$this->errors[] = 'could not store the image in the temporary folder';
|
||||
}
|
||||
@@ -347,6 +390,12 @@ class Media
|
||||
{
|
||||
$this->saveOriginal('LIVE');
|
||||
$this->saveOriginal('THUMBS');
|
||||
|
||||
if(empty($this->errors))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function createImage()
|
||||
|
116
system/typemill/Models/SvgSanitizer.php
Normal file
116
system/typemill/Models/SvgSanitizer.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* SVGSantiizer
|
||||
*
|
||||
* Whitelist-based PHP SVG sanitizer.
|
||||
*
|
||||
* @link https://github.com/alister-/SVG-Sanitizer}
|
||||
* @author Alister Norris
|
||||
* @copyright Copyright (c) 2013 Alister Norris
|
||||
* @license http://opensource.org/licenses/mit-license.php The MIT License
|
||||
* @package svgsanitizer
|
||||
*/
|
||||
|
||||
namespace Typemill\Models;
|
||||
|
||||
class SvgSanitizer {
|
||||
|
||||
private $xmlDoc; // PHP XML DOMDocument
|
||||
|
||||
// defines the whitelist of elements and attributes allowed.
|
||||
private static $whitelist = [
|
||||
'a' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'id' => true, 'mask' => true, 'opacity' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true, 'xlink:title' => true],
|
||||
'circle' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'cx' => true, 'cy' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'id' => true, 'mask' => true, 'opacity' => true, 'r' => true, 'requiredFeatures' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true],
|
||||
'clipPath' => ['class' => true, 'clipPathUnits' => true, 'id' => true],
|
||||
'defs' => [],
|
||||
'style' => ['type' => true],
|
||||
'desc' => [],
|
||||
'ellipse' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'cx' => true, 'cy' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'id' => true, 'mask' => true, 'opacity' => true, 'requiredFeatures' => true, 'rx' => true, 'ry' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true],
|
||||
'feGaussianBlur' => ['class' => true, 'color-interpolation-filters' => true, 'id' => true, 'requiredFeatures' => true, 'stdDeviation' => true],
|
||||
'filter' => ['class' => true, 'color-interpolation-filters' => true, 'filterRes' => true, 'filterUnits' => true, 'height' => true, 'id' => true, 'primitiveUnits' => true, 'requiredFeatures' => true, 'width' => true, 'x' => true, 'y' => true],
|
||||
'foreignObject' => ['class' => true, 'font-size' => true, 'height' => true, 'id' => true, 'opacity' => true, 'requiredFeatures' => true, 'style' => true, 'transform' => true, 'width' => true, 'x' => true, 'y' => true],
|
||||
'g' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'id' => true, 'display' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'mask' => true, 'opacity' => true, 'requiredFeatures' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true, 'font-family' => true, 'font-size' => true, 'font-style' => true, 'font-weight' => true, 'text-anchor' => true],
|
||||
'image' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'filter' => true, 'height' => true, 'id' => true, 'mask' => true, 'opacity' => true, 'requiredFeatures' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true, 'width' => true, 'x' => true, 'xlink:title' => true, 'y' => true],
|
||||
'line' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'id' => true, 'marker-end' => true, 'marker-mid' => true, 'marker-start' => true, 'mask' => true, 'opacity' => true, 'requiredFeatures' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true, 'x1' => true, 'x2' => true, 'y1' => true, 'y2' => true],
|
||||
'linearGradient' => ['class' => true, 'id' => true, 'gradientTransform' => true, 'gradientUnits' => true, 'requiredFeatures' => true, 'spreadMethod' => true, 'systemLanguage' => true, 'x1' => true, 'x2' => true, 'y1' => true, 'y2' => true],
|
||||
'marker' => ['id' => true, 'class' => true, 'markerHeight' => true, 'markerUnits' => true, 'markerWidth' => true, 'orient' => true, 'preserveAspectRatio' => true, 'refX' => true, 'refY' => true, 'systemLanguage' => true, 'viewBox' => true],
|
||||
'mask' => ['class' => true, 'height' => true, 'id' => true, 'maskContentUnits' => true, 'maskUnits' => true, 'width' => true, 'x' => true, 'y' => true],
|
||||
'metadata' => ['class' => true, 'id' => true],
|
||||
'path' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'd' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'id' => true, 'marker-end' => true, 'marker-mid' => true, 'marker-start' => true, 'mask' => true, 'opacity' => true, 'requiredFeatures' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true],
|
||||
'pattern' => ['class' => true, 'height' => true, 'id' => true, 'patternContentUnits' => true, 'patternTransform' => true, 'patternUnits' => true, 'requiredFeatures' => true, 'style' => true, 'systemLanguage' => true, 'viewBox' => true, 'width' => true, 'x' => true, 'y' => true],
|
||||
'polygon' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'id' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'id' => true, 'class' => true, 'marker-end' => true, 'marker-mid' => true, 'marker-start' => true, 'mask' => true, 'opacity' => true, 'points' => true, 'requiredFeatures' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true],
|
||||
'polyline' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'id' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'marker-end' => true, 'marker-mid' => true, 'marker-start' => true, 'mask' => true, 'opacity' => true, 'points' => true, 'requiredFeatures' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true],
|
||||
'radialGradient' => ['class' => true, 'cx' => true, 'cy' => true, 'fx' => true, 'fy' => true, 'gradientTransform' => true, 'gradientUnits' => true, 'id' => true, 'r' => true, 'requiredFeatures' => true, 'spreadMethod' => true, 'systemLanguage' => true],
|
||||
'rect' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'height' => true, 'id' => true, 'mask' => true, 'opacity' => true, 'requiredFeatures' => true, 'rx' => true, 'ry' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true, 'width' => true, 'x' => true, 'y' => true],
|
||||
'stop' => ['class' => true, 'id' => true, 'offset' => true, 'requiredFeatures' => true, 'stop-color' => true, 'stop-opacity' => true, 'style' => true, 'systemLanguage' => true],
|
||||
'svg' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'filter' => true, 'id' => true, 'height' => true, 'mask' => true, 'preserveAspectRatio' => true, 'requiredFeatures' => true, 'style' => true, 'systemLanguage' => true, 'viewBox' => true, 'width' => true, 'x' => true, 'xmlns' => true, 'xmlns:se' => true, 'xmlns:xlink' => true, 'y' => true],
|
||||
'switch' => ['class' => true, 'id' => true, 'requiredFeatures' => true, 'systemLanguage' => true],
|
||||
'symbol' => ['class' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'font-family' => true, 'font-size' => true, 'font-style' => true, 'font-weight' => true, 'id' => true, 'opacity' => true, 'preserveAspectRatio' => true, 'requiredFeatures' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true, 'viewBox' => true],
|
||||
'text' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'font-family' => true, 'font-size' => true, 'font-style' => true, 'font-weight' => true, 'id' => true, 'mask' => true, 'opacity' => true, 'requiredFeatures' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'text-anchor' => true, 'transform' => true, 'x' => true, 'xml:space' => true, 'y' => true],
|
||||
'textPath' => ['class' => true, 'id' => true, 'method' => true, 'requiredFeatures' => true, 'spacing' => true, 'startOffset' => true, 'style' => true, 'systemLanguage' => true, 'transform' => true],
|
||||
'title' => [],
|
||||
'tspan' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'dx' => true, 'dy' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'font-family' => true, 'font-size' => true, 'font-style' => true, 'font-weight' => true, 'id' => true, 'mask' => true, 'opacity' => true, 'requiredFeatures' => true, 'rotate' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'systemLanguage' => true, 'text-anchor' => true, 'textLength' => true, 'transform' => true, 'x' => true, 'xml:space' => true, 'y' => true],
|
||||
'use' => ['class' => true, 'clip-path' => true, 'clip-rule' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'filter' => true, 'height' => true, 'id' => true, 'mask' => true, 'stroke' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'stroke-width' => true, 'style' => true, 'transform' => true, 'width' => true, 'x' => true, 'y' => true],
|
||||
];
|
||||
|
||||
function __construct() {
|
||||
$this->xmlDoc = new \DOMDocument();
|
||||
$this->xmlDoc->preserveWhiteSpace = false;
|
||||
}
|
||||
|
||||
//Load the SVG data from a file
|
||||
function load($file) {
|
||||
$this->xmlDoc->load($file);
|
||||
}
|
||||
|
||||
function loadSVG(string $string) {
|
||||
$result = $this->xmlDoc->loadXML($string);
|
||||
}
|
||||
|
||||
//Remove any elements from the XML that are unrelated to SVGs
|
||||
function sanitize() {
|
||||
|
||||
//Get every element in the document, and loop through them all
|
||||
$allElements = $this->xmlDoc->getElementsByTagName("*");
|
||||
|
||||
for ($i = 0; $i < $allElements->length; $i++) {
|
||||
$currentNode = $allElements->item($i);
|
||||
|
||||
//Remove any elements not on the whitelist
|
||||
if (!isset(self::$whitelist[$currentNode->tagName])) {
|
||||
$currentNode->parentNode->removeChild($currentNode);
|
||||
$i--;
|
||||
|
||||
} else {
|
||||
$attributesWhitelist = self::$whitelist[$currentNode->tagName];
|
||||
$attributesToRemove = [];
|
||||
|
||||
//If the element is allowed, loop through checking its attributes v.s. the attributes allowed for that element
|
||||
for ($j = 0; $j < $currentNode->attributes->length; $j++) {
|
||||
$attrName = $currentNode->attributes->item($j)->name;
|
||||
|
||||
if (!isset($attributesWhitelist[$attrName])) {
|
||||
$attributesToRemove[] = $attrName;
|
||||
}
|
||||
}
|
||||
|
||||
//Remove any blocked attributes
|
||||
if (!empty($attributesToRemove)) {
|
||||
foreach ($attributesToRemove as $attrName) {
|
||||
$currentNode->removeAttribute($attrName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function saveSVG() {
|
||||
$this->xmlDoc->formatOutput = true;
|
||||
return($this->xmlDoc->saveXML());
|
||||
}
|
||||
|
||||
function save($file) {
|
||||
$this->xmlDoc->formatOutput = true;
|
||||
return($this->xmlDoc->save($file));
|
||||
}
|
||||
}
|
@@ -1639,13 +1639,11 @@ bloxeditor.component('image-component', {
|
||||
|
||||
if(errors)
|
||||
{
|
||||
console.info(errors);
|
||||
eventBus.$emit('publishermessage', errors);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.compmarkdown = imgmarkdown;
|
||||
// publishController.errors.message = false;
|
||||
// this.$parent.activatePage();
|
||||
|
||||
this.$emit('updateMarkdownEvent', imgmarkdown);
|
||||
}
|
||||
@@ -1718,13 +1716,14 @@ bloxeditor.component('image-component', {
|
||||
|
||||
if (!imageFile.type.match('image.*'))
|
||||
{
|
||||
alert("wrong format");
|
||||
// publishController.errors.message = "Only images are allowed.";
|
||||
let message = this.$filters.translate('Only images are allowed');
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
else if (size > this.maxsize)
|
||||
{
|
||||
alert("wrong size");
|
||||
// publishController.errors.message = "The maximal size of images is " + this.maxsize + " MB";
|
||||
let message = "The maximal size of images is " + this.maxsize + " MB";
|
||||
message = this.$filters.translate(message);
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1761,9 +1760,10 @@ bloxeditor.component('image-component', {
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
alert("errror in response");
|
||||
let message = error.response.data.message;
|
||||
message = self.$filters.translate(message);
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1775,7 +1775,8 @@ bloxeditor.component('image-component', {
|
||||
|
||||
if(!this.imgfile)
|
||||
{
|
||||
alert("no file");
|
||||
let message = this.$filters.translate("Imagefile is missing.");
|
||||
eventBus.$emit('publishermessage', message);
|
||||
return;
|
||||
}
|
||||
if(!this.saveimage)
|
||||
@@ -1804,7 +1805,8 @@ bloxeditor.component('image-component', {
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
console.info(error.response);
|
||||
let message = self.$filters.translate(error.response.data.message);
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1976,14 +1978,10 @@ bloxeditor.component('file-component', {
|
||||
}
|
||||
if(errors)
|
||||
{
|
||||
alert("errors");
|
||||
// this.$parent.freezePage();
|
||||
// publishController.errors.message = errors;
|
||||
eventBus.$emit('publishermessage', this.$filters.translate(errors));
|
||||
}
|
||||
else
|
||||
{
|
||||
// publishController.errors.message = false;
|
||||
// this.$parent.activatePage();
|
||||
this.$emit('updateMarkdownEvent', filemarkdown);
|
||||
this.compmarkdown = filemarkdown;
|
||||
}
|
||||
@@ -2011,7 +2009,11 @@ bloxeditor.component('file-component', {
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
alert("error response");
|
||||
if(error.response.data.message)
|
||||
{
|
||||
let message = myself.$filters.translate(error.response.data.message);
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
});
|
||||
},
|
||||
updaterestriction: function()
|
||||
@@ -2033,15 +2035,13 @@ bloxeditor.component('file-component', {
|
||||
|
||||
if (size > this.maxsize)
|
||||
{
|
||||
alert("error size");
|
||||
// publishController.errors.message = "The maximal size of a file is " + this.maxsize + " MB";
|
||||
let message = "The maximal size of a file is " + this.maxsize + " MB";
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
else
|
||||
{
|
||||
self = this;
|
||||
|
||||
// self.$parent.freezePage();
|
||||
// self.$root.$data.file = true;
|
||||
self.load = true;
|
||||
|
||||
let reader = new FileReader();
|
||||
@@ -2056,7 +2056,6 @@ bloxeditor.component('file-component', {
|
||||
.then(function (response) {
|
||||
|
||||
self.load = false;
|
||||
// self.$parent.activatePage();
|
||||
|
||||
self.filemeta = true;
|
||||
self.savefile = true;
|
||||
@@ -2070,11 +2069,10 @@ bloxeditor.component('file-component', {
|
||||
.catch(function (error)
|
||||
{
|
||||
self.load = false;
|
||||
// self.$parent.activatePage();
|
||||
if(error.response)
|
||||
{
|
||||
alert("error response")
|
||||
// publishController.errors.message = error.response.data.errors;
|
||||
let message = self.$filters.translate(error.response.data.message);
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2087,7 +2085,9 @@ bloxeditor.component('file-component', {
|
||||
|
||||
if(!this.fileurl)
|
||||
{
|
||||
alert("no file");
|
||||
let message = this.$filters.translate('file is missing.');
|
||||
eventBus.$emit('publishermessage', message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2116,7 +2116,8 @@ bloxeditor.component('file-component', {
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
console.info(error.response);
|
||||
let message = self.$filters.translate(error.response.data.message);
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2206,7 +2207,8 @@ bloxeditor.component('video-component', {
|
||||
if(this.provider != "youtube")
|
||||
{
|
||||
this.updatemarkdown("");
|
||||
alert("we only support youtube right now");
|
||||
let message = this.$filters.translate("We only support youtube right now.");
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
},
|
||||
updatemarkdown(url)
|
||||
@@ -2242,7 +2244,8 @@ bloxeditor.component('video-component', {
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
console.info(error.response);
|
||||
let message = self.$filters.translate(error.response.data.message);
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -2286,23 +2289,27 @@ bloxeditor.component('shortcode-component', {
|
||||
|
||||
var myself = this;
|
||||
|
||||
tmaxios.get('/api/v1/shortcodedata',{
|
||||
params: {
|
||||
tmaxios.get('/api/v1/shortcodedata',
|
||||
{
|
||||
params:
|
||||
{
|
||||
'url': data.urlinfo.route,
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
if(response.data.shortcodedata !== false)
|
||||
{
|
||||
myself.shortcodedata = response.data.shortcodedata;
|
||||
myself.parseshortcode();
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
}
|
||||
})
|
||||
.then(function (response)
|
||||
{
|
||||
if(response.data.shortcodedata !== false)
|
||||
{
|
||||
if(error.response)
|
||||
myself.shortcodedata = response.data.shortcodedata;
|
||||
myself.parseshortcode();
|
||||
}
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
|
||||
let message = self.$filters.translate(error.response.data.message);
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
Reference in New Issue
Block a user