1
0
mirror of https://github.com/typemill/typemill.git synced 2025-08-05 05:37:45 +02:00

Version 1.4.1: Custom Fields half ready

This commit is contained in:
trendschau
2020-10-29 14:38:48 +01:00
parent b29325c50a
commit 2282ab7fb8
8 changed files with 242 additions and 5 deletions

View File

@@ -0,0 +1,29 @@
<?php
namespace Plugins\Adamhall;
use Typemill\Plugin;
class Adamhall extends Plugin
{
public static function getSubscribedEvents()
{
return array(
'onTwigLoaded' => 'onTwigLoaded',
'onMetaLoaded' => 'onMetaLoaded'
);
}
public function onTwigLoaded()
{
$this->addEditorJS('/adamhall/js/adamhall.js');
}
public function onMetaLoaded($meta)
{
$meta = $meta->getData();
# do something with the fields:
$myTabInformation = $meta['mytab'];
}
}

View File

@@ -0,0 +1,15 @@
name: Adam Hall Plugin
version: 1.0.0
description: Plugin for Adam Hall that integrates Custom Fields
author: Sebastian Schürmanns
homepage: http://trendschau.net
licence: Owned by Adam Hall
metatabs:
adamhall:
fields:
myfield:
type: customfields
label: Simple Custom Field
description: Please add some custom fields
data: array

View File

@@ -0,0 +1,26 @@
Vue.component('tab-adamhall', {
props: ['saved', 'errors', 'formdata', 'schema'],
template: '<section><form>' +
'<component v-for="(field, index) in schema.fields"' +
':key="index"' +
':is="selectComponent(field)"' +
':errors="errors"' +
':name="index"' +
'v-model="formdata[index]"' +
'v-bind="field">' +
'</component>' +
'<div v-if="saved" class="metaLarge"><div class="metaSuccess">Saved successfully</div></div>' +
'<div v-if="errors" class="metaLarge"><div class="metaErrors">Please correct the errors above</div></div>' +
'<div class="large"><input type="submit" @click.prevent="saveInput" value="save"></input></div>' +
'</form></section>',
methods: {
selectComponent: function(field)
{
return 'component-'+field.type;
},
saveInput: function()
{
this.$emit('saveform');
},
}
})

View File

@@ -122,6 +122,28 @@ class MetaApiController extends ContentController
{
$metascheme[$tabname][$fieldname] = true;
$metadata[$tabname][$fieldname] = isset($pagemeta[$tabname][$fieldname]) ? $pagemeta[$tabname][$fieldname] : null;
# special treatment for customfields
if(isset($fielddefinitions['type']) && ($fielddefinitions['type'] == 'customfields' ) )
{
# loop through the customdata
foreach($metadata[$tabname][$fieldname] as $key => $value)
{
# and make sure that arrays are transformed back into strings
if(isset($value['value']) && is_array($value['value']))
{
$valuestring = implode('\n',$value['value']);
$metadata[$tabname][$fieldname][$key]['value'] = $valuestring;
}
}
/*
echo 'fielddefinition: <pre>';
print_r($fielddefinitions);
echo '</pre>metadata: <pre>';
print_r($pagemeta[$tabname][$fieldname]);
die();
*/
}
}
}
@@ -208,6 +230,21 @@ class MetaApiController extends ContentController
{
$errors[$tab][$fieldName] = $result[$fieldName][0];
}
# special treatment for customfields
if($fieldDefinition && isset($fieldDefinition['type']) && ($fieldDefinition['type'] == 'customfields' ) && isset($fieldDefinition['data']) && ($fieldDefinition['data'] == 'array' ) )
{
foreach($fieldValue as $key => $valuePair)
{
if(isset($valuePair['value']))
{
$arrayValues = explode(PHP_EOL,$valuePair['value']);
echo '<pre>';
print_r($arrayValues);
}
}
die();
}
}
}

View File

@@ -63,6 +63,23 @@ class Validation
return true;
}, 'contains one or more invalid ip-adress');
Validator::addRule('customfields', function($field, $value, array $params, array $fields) use ($user)
{
foreach($value as $customfield)
{
if(!isset($customfield['key']) OR empty($customfield['key']) OR (preg_match('/^([a-z0-9])+$/i', $customfield['key']) == false) )
{
return false;
}
if (!isset($customfield['value']) OR empty($customfield['value']) OR ( $customfield['value'] != strip_tags($customfield['value']) ) )
{
return false;
}
}
return true;
}, 'contains one or more invalid values');
Validator::addRule('checkPassword', function($field, $value, array $params, array $fields) use ($user)
{
$userdata = $user->getUser($fields['username']);
@@ -459,6 +476,10 @@ class Validation
$v->rule('lengthMax', $fieldName, 1000);
$v->rule('image_types', $fieldName);
break;
case "customfields":
$v->rule('array', $fieldName);
$v->rule('customfields', $fieldName);
break;
default:
$v->rule('lengthMax', $fieldName, 1000);
$v->rule('regex', $fieldName, '/^[\pL0-9_ \-]*$/u');

View File

@@ -128,6 +128,9 @@ input.upload{
opacity: 0;
}
/* fix wrong text-align/family */
input, textarea, button {font-family: arial, sans-serif}
/****************************
* download-commponent *
****************************/
@@ -1280,6 +1283,16 @@ span.error{
background: #70c1b3;
}
/********************
* Customfields *
********************/
.customkey{
width: 29%;
}
.customvalue{
width: 60%;
}
/********************
* UPDATE-BANNER *

View File

@@ -1248,13 +1248,13 @@ const definitionComponent = Vue.component('definition-component', {
template: '<div class="definitionList">' +
'<div class="contenttype"><svg class="icon icon-dots-two-vertical"><use xlink:href="#icon-dots-two-vertical"></use></svg></div>' +
'<draggable v-model="definitionList" :animation="150" @end="moveDefinition">' +
'<div class="definitionRow" v-for="(definition, dindex) in definitionList" :key="definition.id">' +
'<div class="definitionRow" v-for="(definition, dindex) in definitionList" :key="definition.id">' +
'<svg class="icon icon-arrows-v"><use xlink:href="#icon-arrows-v"></use></svg>' +
'<input type="text" class="definitionTerm" v-bind:placeholder="\'term\'|translate" :value="definition.term" :disabled="disabled" @input="updateterm($event,dindex)" @blur="updateMarkdown">' +
'<svg class="icon icon-dots-two-vertical"><use xlink:href="#icon-dots-two-vertical"></use></svg>' +
'<textarea class="definitionDescription" v-bind:placeholder="\'description\'|translate" v-html="definition.description" :disabled="disabled" @input="updatedescription($event, dindex)" @keydown.13.prevent="enter" @blur="updateMarkdown"></textarea>' +
'<button class="delDL" @click.prevent="deleteDefinition(dindex)"><svg class="icon icon-minus"><use xlink:href="#icon-minus"></use></svg></button>' +
'</div>' +
'<svg class="icon icon-dots-two-vertical"><use xlink:href="#icon-dots-two-vertical"></use></svg>' +
'<textarea class="definitionDescription" v-bind:placeholder="\'description\'|translate" v-html="definition.description" :disabled="disabled" @input="updatedescription($event, dindex)" @keydown.13.prevent="enter" @blur="updateMarkdown"></textarea>' +
'<button class="delDL" @click.prevent="deleteDefinition(dindex)"><svg class="icon icon-minus"><use xlink:href="#icon-minus"></use></svg></button>' +
'</div>' +
'</draggable>' +
'<button class="addDL" @click.prevent="addDefinition()"><svg class="icon icon-plus"><use xlink:href="#icon-plus"></use></svg> {{ \'add definition\'|translate }}</button>' +
'<div v-if="load" class="loadwrapper"><span class="load"></span></div>' +

View File

@@ -332,6 +332,7 @@ Vue.component('component-checkboxlist', {
'<span class="checkmark"></span>' +
'<span v-if="errors[name]" class="error">{{ errors[name] }}</span>' +
'<span v-else class="fielddescription"><small>{{ description|translate }}</small></span>' +
'</label>' +
'</div>',
methods: {
update: function($event, value, optionvalue, name)
@@ -373,6 +374,92 @@ Vue.component('component-radio', {
},
})
Vue.component('component-customfields', {
props: ['class', 'id', 'description', 'readonly', 'required', 'disabled', 'options', 'label', 'name', 'type', 'value', 'errors'],
data: function () {
return {
fielderrors: false,
fielddetails: {},
}
},
template: '<div class="large">' +
'<label class="mb2">{{ label|translate }}</label>' +
'<div v-if="fielderrors" class="error mb2"><small>{{ fielderrors }}</small></div>' +
'<div v-else="description" class="fielddescription mb2"><small>{{ description|translate }}</small></div>' +
'<div class="customrow flex items-start mb3" v-for="(pairobject, pairindex) in value">' +
'<input type="text" placeholder="key" class="customkey" :class="pairobject.keyerror" :value="pairobject.key" @input="updatePairKey(pairindex,$event)">' +
'<div class="mt3"><svg class="icon icon-dots-two-vertical"><use xlink:href="#icon-dots-two-vertical"></use></svg></div>' +
'<textarea placeholder="value" class="customvalue pa3" :class="pairobject.valueerror" v-html="pairobject.value" @input="updatePairValue(pairindex,$event)"></textarea>' +
'<button class="bg-tm-red white bn ml2 h1 w2 br1" @click.prevent="deleteField(pairindex)"><svg class="icon icon-minus"><use xlink:href="#icon-minus"></use></svg></button>' +
'</div>' +
'<button class="bg-tm-green white bn br1 pa2 f6" @click.prevent="addField()"><svg class="icon icon-plus f7"><use xlink:href="#icon-plus"></use></svg> Add Fields</button>' +
'</div>',
mounted: function(){
if(this.value === null)
{
this.value = [{}];
}
},
methods: {
update: function(value, name)
{
FormBus.$emit('forminput', {'name': name, 'value': value});
},
updatePairKey: function(index,event)
{
this.value[index].key = event.target.value;
var regex = /^[a-z0-9]+$/i;
if(regex.test(event.target.value))
{
this.fielderrors = false;
delete this.value[index].keyerror;
this.update(this.value,this.name);
}
else
{
this.value[index].keyerror = 'red';
this.fielderrors = 'Only alphanumeric for keys allowed';
}
},
updatePairValue: function(index, event)
{
this.value[index].value = event.target.value;
var regex = /<.*(?=>)/gm;
if(event.target.value == '' || regex.test(event.target.value))
{
this.value[index].valueerror = 'red';
this.fielderrors = 'No empty values or html tags are allowed';
}
else
{
this.fielderrors = false;
delete this.value[index].valueerror;
this.update(this.value,this.name);
}
},
addField: function()
{
for(object in this.value)
{
if(Object.keys(this.value[object]).length === 0)
{
return;
}
}
this.value.push({});
},
deleteField: function(index)
{
this.value.splice(index,1);
},
},
})
Vue.component('tab-meta', {
props: ['saved', 'errors', 'formdata', 'schema'],
template: '<section><form>' +
@@ -473,9 +560,18 @@ let meta = new Vue({
}
});
/* update single value or array
this.$set(this.someObject, 'b', 2) */
FormBus.$on('forminput', formdata => {
this.$set(this.formData[this.currentTab], formdata.name, formdata.value);
});
/* update values that are objects
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 }) */
FormBus.$on('forminputobject', formdata => {
this.formData[this.currentTab][formdata.name] = Object.assign({}, this.formData[this.currentTab][formdata.name], formdata.value);
});
},
methods: {
saveForm: function()