1
0
mirror of https://github.com/typemill/typemill.git synced 2025-07-31 19:30:40 +02:00

V2.10.0 introduse native videos to visual editor

This commit is contained in:
trendschau
2024-10-16 12:40:29 +02:00
parent 0ad0bc93f3
commit 7caaa9f7c3
12 changed files with 1369 additions and 905 deletions

46
composer.lock generated
View File

@@ -116,12 +116,12 @@
"source": {
"type": "git",
"url": "https://github.com/erusev/parsedown-extra.git",
"reference": "fd33d68349630d18d56367712a64445a6e0bc83e"
"reference": "cd57cf183be7b56e90498091c0a7955ad4df223c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/fd33d68349630d18d56367712a64445a6e0bc83e",
"reference": "fd33d68349630d18d56367712a64445a6e0bc83e",
"url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/cd57cf183be7b56e90498091c0a7955ad4df223c",
"reference": "cd57cf183be7b56e90498091c0a7955ad4df223c",
"shasum": ""
},
"require": {
@@ -163,7 +163,7 @@
"issues": "https://github.com/erusev/parsedown-extra/issues",
"source": "https://github.com/erusev/parsedown-extra/tree/master"
},
"time": "2021-10-25T08:46:29+00:00"
"time": "2024-09-29T09:07:37+00:00"
},
{
"name": "fig/http-message-util",
@@ -406,16 +406,16 @@
},
{
"name": "laravel/serializable-closure",
"version": "v1.3.4",
"version": "v1.3.5",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
"reference": "61b87392d986dc49ad5ef64e75b1ff5fee24ef81"
"reference": "1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/61b87392d986dc49ad5ef64e75b1ff5fee24ef81",
"reference": "61b87392d986dc49ad5ef64e75b1ff5fee24ef81",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c",
"reference": "1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c",
"shasum": ""
},
"require": {
@@ -463,7 +463,7 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
"time": "2024-08-02T07:48:17+00:00"
"time": "2024-09-23T13:33:08+00:00"
},
{
"name": "nikic/fast-route",
@@ -1452,16 +1452,16 @@
},
{
"name": "slim/twig-view",
"version": "3.4.0",
"version": "3.4.1",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Twig-View.git",
"reference": "1b351536b9a07ed90a3563ee9d71a987c5d74610"
"reference": "b4268d87d0e327feba5f88d32031e9123655b909"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Twig-View/zipball/1b351536b9a07ed90a3563ee9d71a987c5d74610",
"reference": "1b351536b9a07ed90a3563ee9d71a987c5d74610",
"url": "https://api.github.com/repos/slimphp/Twig-View/zipball/b4268d87d0e327feba5f88d32031e9123655b909",
"reference": "b4268d87d0e327feba5f88d32031e9123655b909",
"shasum": ""
},
"require": {
@@ -1469,12 +1469,12 @@
"psr/http-message": "^1.1 || ^2.0",
"slim/slim": "^4.12",
"symfony/polyfill-php81": "^1.29",
"twig/twig": "^3.8"
"twig/twig": "^3.11"
},
"require-dev": {
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/phpstan": "^1.10.59",
"phpunit/phpunit": "^9.6",
"phpunit/phpunit": "^9.6 || ^10",
"psr/http-factory": "^1.0",
"squizlabs/php_codesniffer": "^3.9"
},
@@ -1511,9 +1511,9 @@
],
"support": {
"issues": "https://github.com/slimphp/Twig-View/issues",
"source": "https://github.com/slimphp/Twig-View/tree/3.4.0"
"source": "https://github.com/slimphp/Twig-View/tree/3.4.1"
},
"time": "2024-04-28T20:36:39+00:00"
"time": "2024-09-26T05:42:02+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -2126,16 +2126,16 @@
},
{
"name": "symfony/yaml",
"version": "v5.4.43",
"version": "v5.4.44",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "62f96e1cfd4cf518882a36bfedcf1fe4093c1299"
"reference": "7025b964f123bbf1896d7563db6ec7f1f63e918a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/62f96e1cfd4cf518882a36bfedcf1fe4093c1299",
"reference": "62f96e1cfd4cf518882a36bfedcf1fe4093c1299",
"url": "https://api.github.com/repos/symfony/yaml/zipball/7025b964f123bbf1896d7563db6ec7f1f63e918a",
"reference": "7025b964f123bbf1896d7563db6ec7f1f63e918a",
"shasum": ""
},
"require": {
@@ -2181,7 +2181,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v5.4.43"
"source": "https://github.com/symfony/yaml/tree/v5.4.44"
},
"funding": [
{
@@ -2197,7 +2197,7 @@
"type": "tidelift"
}
],
"time": "2024-08-11T17:40:32+00:00"
"time": "2024-09-16T14:36:56+00:00"
},
{
"name": "twig/twig",

View File

@@ -0,0 +1,114 @@
<?php
namespace Typemill\Extensions;
use \Symfony\Component\EventDispatcher\EventSubscriberInterface;
class MediaExtension implements EventSubscriberInterface
{
private $rootpath;
private $baseurl;
function __construct($rootpath, $baseurl)
{
$this->rootpath = $rootpath;
$this->baseurl = $baseurl;
}
public static function getSubscribedEvents()
{
return [
'onShortcodeFound' => 'onShortcodeFound',
];
}
public function onShortcodeFound($shortcode)
{
$shortcodeArray = $shortcode->getData();
if(is_array($shortcodeArray) && $shortcodeArray['name'] == 'video' && isset($shortcodeArray['params']['path']))
{
$relUrl = $shortcodeArray['params']['path'];
$relUrl = '/' . trim($relUrl, '/');
# Convert the relative URL to an absolute file path
$filePath = $this->rootpath . $relUrl;
# check file exists
if(!file_exists($filePath))
{
$html = '<p style="color:red">File not found</p>';
}
else
{
# Get file extension using pathinfo()
$fileInfo = pathinfo($filePath);
$extension = strtolower($fileInfo['extension']); // Get file extension and convert to lowercase
$absUrl = $this->baseurl . $relUrl;
# Determine the correct file type for the video tag
$type = '';
switch ($extension) {
case 'mp4':
$type = 'mp4';
break;
case 'webm':
$type = 'webm';
break;
case 'ogg':
$type = 'ogg';
break;
default:
$html = '<p style="color:red">Unsupported file type</p>';
return; // Exit if file type is not supported
}
$width = $shortcodeArray['params']['width'] ?? '500';
if (!preg_match('/^(\d+)(px|%)?$/', $width))
{
$width = '500';
}
$preload = 'none';
if(isset($shortcodeArray['params']['preload']) && ($shortcodeArray['params']['preload'] == 'auto' or $shortcodeArray['params']['preload'] == 'metadata'))
{
$preload = $shortcodeArray['params']['preload'];
}
$poster = '';
if(isset($shortcodeArray['params']['poster']))
{
$relImgUrl = $shortcodeArray['params']['poster'];
$relImgUrl = '/' . trim($relImgUrl, '/');
# Convert the relative URL to an absolute file path
$imgPath = $this->rootpath . $relImgUrl;
# check file exists
if(file_exists($imgPath))
{
$absImgUrl = $this->baseurl . $relImgUrl;
$poster = ' poster="' . $absImgUrl . '"';
}
}
$html = '<video
controls
width = "' . $width . '"
preload = "' . $preload . '"
' . $poster . '
class = "center"
>
<source src="' . $absUrl . '" type="video/' . $type . '" />
Download the
<a href="' . $absUrl . '">' . $type . '</a>
video.
</video>';
}
$shortcode->setData($html);
}
}
}

View File

@@ -409,7 +409,7 @@
.blox-preview .notice3:before{ content: "!!!"; }
.blox-preview .notice4:before{ content: "!!!!"; }
.blox-preview img, img.uploadPreview{
.blox-preview video, .blox-preview audio, .blox-preview img, img.uploadPreview{
display: block;
margin: auto;
max-width: 100%;

File diff suppressed because it is too large Load Diff

View File

@@ -2306,128 +2306,338 @@ bloxeditor.component('file-component', {
bloxeditor.component('video-component', {
props: ['markdown', 'disabled', 'index'],
template: `<div class="video dropbox p-8">
<div class="absolute top-3 -left-5 text-stone-400">
<svg class="icon icon-play">
<use xlink:href="#icon-play"></use>
</svg>
components: {
medialib: medialib
},
template: `<div class="dropbox">
<input type="hidden" ref="markdown" :value="markdown" :disabled="disabled" @input="updatemarkdown" />
<div class="flex">
<div class="imageupload relative w-1/2 border-r border-dotted border-stone-700">
<input type="file" name="file" accept="video/mp4,video/webm,video/ogg" class="opacity-0 w-full h-24 absolute cursor-pointer z-10" @change="onFileChange( $event )" />
<p class="text-center p-6">
<svg class="icon icon-upload">
<use xlink:href="#icon-upload"></use>
</svg>
{{ $filters.translate('upload video') }}
</p>
</div>
<div class="flex mt-2 mb-2">
<label class="w-1/5 py-2" for="video">{{ $filters.translate('Link to youtube') }}: </label>
<input class="w-4/5 p-2 bg-stone-200 text-stone-900" type="url" ref="markdown" placeholder="https://www.youtube.com/watch?v=" :value="markdown" :disabled="disabled" @input="updatemarkdown($event.target.value)">
<button class="imageselect w-1/2 text-center p-6" @click.prevent="openmedialib('files')">
<svg class="icon icon-paperclip baseline">
<use xlink:href="#icon-paperclip"></use>
</svg>
{{ $filters.translate('select from medialib') }}
</button>
</div>
<Transition name="initial" appear>
<div v-if="showmedialib == 'files'" class="fixed top-0 left-0 right-0 bottom-0 bg-stone-100 z-50">
<button class="w-full bg-stone-200 hover:bg-rose-500 hover:text-white p-2 transition duration-100" @click.prevent="showmedialib = false">{{ $filters.translate('close library') }}</button>
<medialib parentcomponent="files" @addFromMedialibEvent="addFromMedialibFunction"></medialib>
</div>
</div>`,
</Transition>
<Transition name="initial" appear>
<div v-if="showmedialib == 'images'" class="fixed top-0 left-0 right-0 bottom-0 bg-stone-100 z-50">
<button class="w-full bg-stone-200 hover:bg-rose-500 hover:text-white p-2 transition duration-100" @click.prevent="showmedialib = false">{{ $filters.translate('close library') }}</button>
<medialib parentcomponent="images" @addFromMedialibEvent="addFromMedialibFunction"></medialib>
</div>
</Transition>
<div class="absolute top-3 -left-5 text-stone-400">
<svg class="icon icon-paperclip">
<use xlink:href="#icon-paperclip"></use>
</svg>
</div>
<div v-if="load" class="loadwrapper"><span class="load"></span></div>
<div class="imgmeta p-8" v-if="filemeta">
<input
title = "fileid"
type = "hidden"
placeholder = "id"
v-model = "fileid"
@input = "createmarkdown"
max = "140"
/>
<div class="flex mb-2">
<label class="w-1/5 py-2" for="path">{{ $filters.translate('Path') }}: </label>
<input class="w-4/5 p-2 bg-stone-200 text-stone-900" name="path" type="text" readonly="true" v-model="fileurl" />
</div>
<div class="flex mb-2">
<label class="w-1/5 py-2" for="width">{{ $filters.translate('Width') }}: </label>
<input class="w-4/5 p-2 bg-stone-200 text-stone-900" name="width" type="text" placeholder="500" v-model="width" @input="createmarkdown" />
</div>
<div class="flex mb-2">
<label class="w-1/5 py-2" for="videopreload">{{ $filters.translate('Preload') }}: </label>
<select class="w-4/5 p-2 bg-stone-200 text-stone-900" name="videopreload" v-model="preload" @change="createmarkdown">
<option value="none">none</option>
<option value="metadata">metadata</option>
<option value="auto">auto</option>
</select>
</div>
<div class="flex mb-2">
<label class="w-1/5 py-2" for="imagepath">{{ $filters.translate('Image') }}: </label>
<div class="flex w-4/5 justify-between">
<button @click.prevent="deleteImage()" class="w-8 bg-rose-500 dark:bg-stone-600 hover:bg-rose-600 hover:dark:bg-rose-500 text-white">x</button>
<input class="w-full p-2 bg-stone-200 text-stone-900" name="path" type="text" readonly="true" v-model="imageurl" />
<button @click.prevent="openmedialib('images')" class="w-8 bg-stone-600 dark:bg-stone-600 hover:bg-stone-800 hover:dark:bg-stone-500 text-white"><svg class="icon icon-image"><use xlink:href="#icon-image"></use></svg></button>
</div>
</div>
</div>
</div>`,
data: function(){
return {
edited: false,
url: false,
videoid: false,
param: false,
path: false,
provider: false,
providerurl: false,
compmarkdown: '',
maxsize: 100, // megabyte
showmedialib: false,
load: false,
filemeta: false,
fileextension: '',
allowedImageExtensions: ['webp', 'png', 'svg', 'jpg', 'jpeg'],
allowedExtensions: ['mp4', 'webm', 'ogg'],
fileurl: '',
width: '500',
fileid: '',
imageurl: '',
savefile: false,
mediatypes: 'files',
preload: 'none',
}
},
mounted: function(){
mounted: function() {
eventBus.$on('beforeSave', this.beforeSave);
eventBus.$on('beforeSave', this.beforeSave );
this.$refs.markdown.focus();
this.$refs.markdown.focus();
if (this.markdown)
{
this.filemeta = true;
if(this.markdown)
{
this.parseImageMarkdown(this.markdown);
}
var fileurl = this.markdown.match(/path="(.*?)"/);
if (fileurl && fileurl[1])
{
this.fileurl = fileurl[1];
}
var width = this.markdown.match(/width="(.*?)"/);
if (width && width[1])
{
this.width = width[1];
}
var preload = this.markdown.match(/preload="(.*?)"/);
if (preload && preload[1])
{
this.preload = preload[1];
}
var poster = this.markdown.match(/poster="(.*?)"/);
if (poster && poster[1])
{
this.imageurl = poster[1];
}
}
},
methods: {
generateMarkdown()
addFromMedialibFunction(file)
{
this.compmarkdown = '![' + this.provider + '-video](' + this.path + ' "click to load video"){#' + this.videoid + ' .' + this.provider + '}';
this.showmedialib = false;
this.savefile = false;
this.filemeta = true;
if (typeof file === 'string')
{
let fileExtension = file.split('.').pop().toLowerCase();
if (this.allowedImageExtensions.includes(fileExtension))
{
this.imageurl = file;
}
else
{
let message = "Unsupported file type. Please select an image with format webp, png, jpg, jpeg. svg.";
eventBus.$emit('publishermessage', message);
return;
}
}
else if (this.allowedExtensions.includes(file.info.extension.toLowerCase()))
{
this.filetitle = file.name;
this.fileextension = file.info.extension.toLowerCase();
this.fileurl = file.url;
}
else
{
let message = "Unsupported file type. Please select a valid video file (webm, mp4, ogg).";
eventBus.$emit('publishermessage', message);
return;
}
this.createmarkdown();
},
parseImageMarkdown(imageMarkdown)
openmedialib(type)
{
let regexpurl = /\((.*)(".*")\)/;
let match = imageMarkdown.match(regexpurl);
let imageUrl = match[1];
let regexprov = /live\/(.*?)-/;
let matchprov = imageUrl.match(regexprov);
this.provider = matchprov[1];
if(this.provider == 'youtube')
{
this.providerurl = "https://www.youtube.com/watch";
this.param = "v=";
}
let videoid = imageMarkdown.match(/#.*? /);
if(videoid)
{
this.videoid = videoid[0].trim().substring(1);
}
this.updatemarkdown(this.providerurl + "?" + this.param + this.videoid);
this.showmedialib = type;
},
parseUrl(url)
deleteImage()
{
let urlparts = url.split('?');
let urlParams = new URLSearchParams(urlparts[1]);
this.providerurl = urlparts[0];
if(urlParams.has("v"))
this.imageurl = '';
},
isChecked(classname)
{
if(this.fileclass == classname)
{
this.param = "v=";
this.videoid = urlParams.get("v");
this.provider = "youtube";
}
if(this.provider != "youtube")
{
this.updatemarkdown("");
let message = this.$filters.translate("We only support youtube right now.");
eventBus.$emit('publishermessage', message);
return ' checked';
}
},
updatemarkdown(url)
updatemarkdown(event, url)
{
this.edited = true;
this.url = url;
this.parseUrl(url);
this.generateMarkdown();
this.$emit('updateMarkdownEvent', url);
this.$emit('updateMarkdownEvent', event.target.value);
},
createmarkdown()
{
var errors = false;
var filemarkdown = false;
if (this.fileurl !== '')
{
if (this.fileurl.length < 101)
{
var width = this.width ? ' width="' + this.width + '"' : '';
var preload = this.preload ? ' preload="' + this.preload + '"' : ' preload="none"';
var poster = this.imageurl ? ' poster="' + this.imageurl + '"' : '';
filemarkdown = '[:video path="' + this.fileurl + '"' + width + preload + poster + ' :]';
}
else
{
errors = this.$filters.translate('Maximum size of file link is 100 characters');
}
}
if (errors)
{
eventBus.$emit('publishermessage', this.$filters.translate(errors));
}
else if (filemarkdown)
{
this.$emit('updateMarkdownEvent', filemarkdown);
this.compmarkdown = filemarkdown;
}
},
onFileChange( e )
{
if(e.target.files.length > 0)
{
let uploadedFile = e.target.files[0];
let allowedVideoTypes = ['video/mp4', 'video/webm', 'video/ogg'];
if (!allowedVideoTypes.includes(uploadedFile.type)) {
let message = "Unsupported file type. Please select a video file (mp4, webm, ogg).";
eventBus.$emit('publishermessage', message);
return;
}
let size = uploadedFile.size / 1024 / 1024;
if (size > this.maxsize)
{
let message = "The maximal size of a file is " + this.maxsize + " MB";
eventBus.$emit('publishermessage', message);
return;
}
else
{
self = this;
self.load = true;
let reader = new FileReader();
reader.readAsDataURL(uploadedFile);
reader.onload = function(e) {
tmaxios.post('/api/v1/file',{
'url': data.urlinfo.route,
'file': e.target.result,
'name': uploadedFile.name,
})
.then(function (response) {
self.load = false;
self.filemeta = true;
self.savefile = true;
self.filetitle = response.data.fileinfo.title;
self.fileextension = response.data.fileinfo.extension;
self.fileurl = response.data.filepath;
self.selectedrole = '';
self.createmarkdown();
})
.catch(function (error)
{
self.load = false;
if(error.response)
{
let message = self.$filters.translate(error.response.data.message);
eventBus.$emit('publishermessage', message);
}
});
}
}
}
},
beforeSave()
{
if(!this.edited)
/* publish file before you save markdown */
if(!this.fileurl)
{
eventBus.$emit('closeComponents');
let message = this.$filters.translate('file is missing.');
eventBus.$emit('publishermessage', message);
return;
}
var self = this;
tmaxios.post('/api/v1/video',{
'url': data.urlinfo.route,
'videourl': this.url,
'provider': this.provider,
'providerurl': this.providerurl,
'videoid': this.videoid,
})
.then(function (response)
const fileExtension = this.fileurl.split('.').pop().toLowerCase();
if (!this.allowedExtensions.includes(fileExtension))
{
let message = this.$filters.translate('Unsupported file format. Only MP4, WebM, and OGG files are allowed.');
eventBus.$emit('publishermessage', message);
return;
}
if(!this.savefile)
{
self.path = response.data.path;
self.$emit('saveBlockEvent');
})
.catch(function (error)
this.createmarkdown();
this.$emit('saveBlockEvent');
}
else
{
if(error.response)
var self = this;
tmaxios.put('/api/v1/file',{
'url': data.urlinfo.route,
'file': this.fileurl,
})
.then(function (response)
{
let message = self.$filters.translate(error.response.data.message);
eventBus.$emit('publishermessage', message);
}
});
},
},
self.fileurl = response.data.path;
self.createmarkdown();
self.$emit('saveBlockEvent');
})
.catch(function (error)
{
if(error.response)
{
let message = self.$filters.translate(error.response.data.message);
eventBus.$emit('publishermessage', message);
}
});
}
},
}
})
bloxeditor.component('shortcode-component', {
@@ -2593,4 +2803,131 @@ bloxeditor.component('shortcode-component', {
this.$emit('updateMarkdownEvent', event.target.value);
},
},
})
})
/* deprecated, use embed plugin instead */
bloxeditor.component('youtube-component', {
props: ['markdown', 'disabled', 'index'],
template: `<div class="video dropbox p-8">
<div class="absolute top-3 -left-5 text-stone-400">
<svg class="icon icon-play">
<use xlink:href="#icon-play"></use>
</svg>
</div>
<div class="flex mt-2 mb-2">
<label class="w-1/5 py-2" for="video">{{ $filters.translate('Link to youtube') }}: </label>
<input class="w-4/5 p-2 bg-stone-200 text-stone-900" type="url" ref="markdown" placeholder="https://www.youtube.com/watch?v=" :value="markdown" :disabled="disabled" @input="updatemarkdown($event.target.value)">
</div>
</div>`,
data: function(){
return {
edited: false,
url: false,
videoid: false,
param: false,
path: false,
provider: false,
providerurl: false,
compmarkdown: '',
}
},
mounted: function(){
eventBus.$on('beforeSave', this.beforeSave );
this.$refs.markdown.focus();
if(this.markdown)
{
this.parseImageMarkdown(this.markdown);
}
},
methods: {
generateMarkdown()
{
this.compmarkdown = '![' + this.provider + '-video](' + this.path + ' "click to load video"){#' + this.videoid + ' .' + this.provider + '}';
},
parseImageMarkdown(imageMarkdown)
{
let regexpurl = /\((.*)(".*")\)/;
let match = imageMarkdown.match(regexpurl);
let imageUrl = match[1];
let regexprov = /live\/(.*?)-/;
let matchprov = imageUrl.match(regexprov);
this.provider = matchprov[1];
if(this.provider == 'youtube')
{
this.providerurl = "https://www.youtube.com/watch";
this.param = "v=";
}
let videoid = imageMarkdown.match(/#.*? /);
if(videoid)
{
this.videoid = videoid[0].trim().substring(1);
}
this.updatemarkdown(this.providerurl + "?" + this.param + this.videoid);
},
parseUrl(url)
{
let urlparts = url.split('?');
let urlParams = new URLSearchParams(urlparts[1]);
this.providerurl = urlparts[0];
if(urlParams.has("v"))
{
this.param = "v=";
this.videoid = urlParams.get("v");
this.provider = "youtube";
}
if(this.provider != "youtube")
{
this.updatemarkdown("");
let message = this.$filters.translate("We only support youtube right now.");
eventBus.$emit('publishermessage', message);
}
},
updatemarkdown(url)
{
this.edited = true;
this.url = url;
this.parseUrl(url);
this.generateMarkdown();
this.$emit('updateMarkdownEvent', url);
},
beforeSave()
{
if(!this.edited)
{
eventBus.$emit('closeComponents');
return;
}
var self = this;
tmaxios.post('/api/v1/video',{
'url': data.urlinfo.route,
'videourl': this.url,
'provider': this.provider,
'providerurl': this.providerurl,
'videoid': this.videoid,
})
.then(function (response)
{
self.path = response.data.path;
self.$emit('saveBlockEvent');
})
.catch(function (error)
{
if(error.response)
{
let message = self.$filters.translate(error.response.data.message);
eventBus.$emit('publishermessage', message);
}
});
},
},
})

View File

@@ -49,21 +49,12 @@ const 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(block.indexOf("-video") != -1)
{
return "video-component";
return "youtube-component";
}
return "image-component";
}
@@ -76,6 +67,13 @@ const determiner = {
}
return false;
},
video: function(block,lines,firstChar,secondChar,thirdChar){
if (lines[0].startsWith('[:video'))
{
return "video-component";
}
return false;
},
code: function(block,lines,firstChar,secondChar,thirdChar){
if( firstChar == '`' && secondChar == '`' && thirdChar == '`')
{
@@ -115,22 +113,23 @@ const bloxFormats = {
quote: { label: '<svg class="icon icon-quotes-left"><use xlink:href="#icon-quotes-left"></use></svg>', title: 'Quote', component: 'quote-component' },
notice: { label: '<svg class="icon icon-exclamation-circle"><use xlink:href="#icon-exclamation-circle"></use></svg>', title: 'Notice', component: 'notice-component' },
image: { label: '<svg class="icon icon-image"><use xlink:href="#icon-image"></use></svg>', title: 'Image', component: 'image-component' },
video: { label: '<svg class="icon icon-play"><use xlink:href="#icon-play"></use></svg>', title: 'Video', component: 'video-component' },
video: { label: '<svg class="icon icon-film"><use xlink:href="#icon-film"></use></svg>', title: 'Video', component: 'video-component' },
file: { label: '<svg class="icon icon-paperclip"><use xlink:href="#icon-paperclip"></use></svg>', title: 'File', component: 'file-component' },
toc: { label: '<svg class="icon icon-list-alt"><use xlink:href="#icon-list-alt"></use></svg>', title: 'Table of Contents', component: 'toc-component' },
hr: { label: '<svg class="icon icon-pagebreak"><use xlink:href="#icon-pagebreak"></use></svg>', title: 'Horizontal Line', component: 'hr-component' },
definition: { label: '<svg class="icon icon-dots-two-vertical"><use xlink:href="#icon-dots-two-vertical"></use></svg>', title: 'Definition List', component: 'definition-component' },
code: { label: '<svg class="icon icon-embed"><use xlink:href="#icon-embed"></use></svg>', title: 'Code', component: 'code-component' },
shortcode: { label: '<svg class="icon icon-square-brackets"><use xlink:href="#icon-square-brackets"></use></svg>', title: 'Shortcode', component: 'shortcode-component' },
youtube: { label: '<svg class="icon icon-play"><use xlink:href="#icon-play"></use></svg>', title: 'YouTube', component: 'youtube-component' },
};
const formatConfig = data.settings.formats;
const activeFormats = [];
const activeFormats = {};
for(var i = 0; i < formatConfig.length; i++)
for (const format in bloxFormats)
{
if(bloxFormats[formatConfig[i]] !== undefined)
{
activeFormats.push(bloxFormats[formatConfig[i]]);
}
}
if (formatConfig.includes(format))
{
activeFormats[format] = bloxFormats[format];
}
}

View File

@@ -173,7 +173,7 @@ bloxeditor.component('content-block', {
componentType: false,
updatedmarkdown: false,
formats: bloxFormats,
formats: activeFormats,
hasUnsafedContent: false,
countUpdates: 0
}
@@ -269,7 +269,6 @@ bloxeditor.component('content-block', {
let markdown = this.element.markdown;
let lines = markdown.split("\n");
let blockType = 'markdown-component';
for (var method in determiner)
{
@@ -277,11 +276,11 @@ bloxeditor.component('content-block', {
if(specialBlock)
{
blockType = specialBlock;
return specialBlock;
}
}
return blockType;
return 'markdown-component';
},
updateMarkdownFunction(value)
{
@@ -433,7 +432,7 @@ bloxeditor.component('new-block',{
`,
data: function () {
return {
formats: bloxFormats,
formats: activeFormats,
componentType: false,
disabled: false,
newblockmarkdown: '',

View File

@@ -252,12 +252,12 @@ const medialib = {
if(this.parentcomponent == 'files')
{
this.showFiles();
this.active = 'pageFiles';
/* this.active = 'pageFiles'; */
}
if(this.parentcomponent == 'images')
{
this.showImages();
this.active = 'pageImages';
/* this.active = 'pageImages'; */
}
},
computed: {

View File

@@ -42,6 +42,10 @@
<title>{{ translate('VIDEO') }}</title>
<path d="M30.662 5.003c-4.488-0.645-9.448-1.003-14.662-1.003s-10.174 0.358-14.662 1.003c-0.86 3.366-1.338 7.086-1.338 10.997s0.477 7.63 1.338 10.997c4.489 0.645 9.448 1.003 14.662 1.003s10.174-0.358 14.662-1.003c0.86-3.366 1.338-7.086 1.338-10.997s-0.477-7.63-1.338-10.997zM12 22v-12l10 6-10 6z"></path>
</symbol>
<symbol id="icon-film" viewBox="0 0 32 32">
<title>{{ translate('VIDEO') }}</title>
<path d="M0 4v24h32v-24h-32zM6 26h-4v-4h4v4zM6 18h-4v-4h4v4zM6 10h-4v-4h4v4zM24 26h-16v-20h16v20zM30 26h-4v-4h4v4zM30 18h-4v-4h4v4zM30 10h-4v-4h4v4zM12 10v12l8-6z"></path>
</symbol>
<symbol id="icon-quotes-left" viewBox="0 0 32 32">
<title>{{ translate('QUOTES') }}</title>
<path d="M7.031 14c3.866 0 7 3.134 7 7s-3.134 7-7 7-7-3.134-7-7l-0.031-1c0-7.732 6.268-14 14-14v4c-2.671 0-5.182 1.040-7.071 2.929-0.364 0.364-0.695 0.751-0.995 1.157 0.357-0.056 0.724-0.086 1.097-0.086zM25.031 14c3.866 0 7 3.134 7 7s-3.134 7-7 7-7-3.134-7-7l-0.031-1c0-7.732 6.268-14 14-14v4c-2.671 0-5.182 1.040-7.071 2.929-0.364 0.364-0.695 0.751-0.995 1.157 0.358-0.056 0.724-0.086 1.097-0.086z"></path>

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -132,6 +132,7 @@ fieldsetwriting:
'definition': 'definition list'
'code': 'code'
'shortcode': 'shortcode'
'youtube': 'YouTube (deprecated)'
headlineanchors:
type: checkbox
label: 'Headline anchors'

View File

@@ -33,6 +33,7 @@ use Typemill\Middleware\FlashMessages;
use Typemill\Middleware\AssetMiddleware;
use Typemill\Middleware\SecurityMiddleware;
use Typemill\Middleware\CustomHeadersMiddleware;
use Typemill\Extensions\MediaExtension;
use Typemill\Extensions\TwigCsrfExtension;
use Typemill\Extensions\TwigUrlExtension;
use Typemill\Extensions\TwigUserExtension;
@@ -222,6 +223,9 @@ if(isset($updateSettings))
$settingsModel->updateSettings($pluginSettings, 'plugins');
}
# add media extension to integrate video/audio with shortcodes
$dispatcher->addSubscriber(new MediaExtension($settings['rootPath'], $urlinfo['baseurl']));
# add final settings to the container
$container->set('settings', function() use ($settings){ return $settings; });

View File

@@ -140,7 +140,7 @@ img, figure,figure img{
height: auto;
aspect-ratio: attr(width) / attr(height);
}
figure{
figure, video, audio{
display: table;
margin: 2em auto;
padding: 0;