# Create a new widget
# Tailor package
There are three kind of packages :
- Starter (old: Basic)
- Premium
- Tailor
All new widgets not included in the list of basic and premium packages will be named "Tailor".
Please refer to this list before creating some new.
# Starting a new tailor
Go to minotaure-theme/widgets
cd htdocs/content/themes/minotaure-theme/widgets
In your repository widgets
create a new folder named tailor
without any caps and a subfolder for your widget.
In this exemple, we are going to create a simple accordion widget.
mdkir tailor
cd tailor
mdkir accordion
cd accordion
2
3
4
5
6
7
Then create 3 files :
- Register.php
- m_form.twig
- m_widget.twig
touch Register.php
touch m_form.twig
touch m_widget.twig
2
3
You may have the following structure
├──minotaure-theme
| ├──widgets
| | ├──tailor
| | | ├── accordion
| | | | └── Register.php
| | | | └── m_form.twig
| | | | └── m_widget.twig
2
3
4
5
6
7
# Register.php
This file manages the connexion between wordpress datas, back-end view and front-end view. This is the most important file in a widget, without it nothing can work.
The code uses the php classes, google it if classes are a mistery to you.
# Create a new class
First, a class needs a namespace to be called
TIP
There is an only and unique way to name a widget namespace in minotaure-theme :
namespace Be\Minotaure\Widgets\Tailor\Your_widget_name
Your class will be called Register
, this a standard for all widgets.
<?php
namespace Be\Minotaure\Widgets\Tailor\Accordion;
class Register extends \WP_Widget
{
}
?>
2
3
4
5
6
7
8
9
10
# __construct()
Now, we are going to create a constructor with some Minotaure standards
public function __construct()
{
parent::__construct(
'm_accordion',
__("Accordion", M_TD)
);
$this->default = [
'title' => '',
'text' => '',
];
}
2
3
4
5
6
7
8
9
10
11
12
m_accordion
is the id of your widget. A widget ID is always prefixed bym_
. It is the diminutive ofminotaure
Accordion
is the title of the widget in the widgets panel.$this->default
contains all inputs you want in your widget
# form()
The form function is used to get datas from the database to your widget form.
The function needs to pass a variable named $instance
, it contains all datas already registered.
public function form($instance)
{
}
2
3
4
It is time to merge $this->default
previously defined and $instance
in case there is data.
public function form($instance)
{
$data = array_merge($this->default, $instance, [
'widget' => $this
]);
}
2
3
4
5
6
The user may have to create several blocks in their accordion ,so we need to merge the widget and future sections.
public function form($instance)
{
$data = array_merge($this->default, $instance, [
'widget' => $this,
'sections' => ''
]);
}
2
3
4
5
6
7
All text from an input type text must be decoded before being sent in the form input. Decoding not including editor type.
public function form($instance)
{
$data = array_merge($this->default, $instance, [
'widget' => $this,
'sections' => ''
]);
$data['title'] = html_entity_decode($data['title']);
}
2
3
4
5
6
7
8
9
It will also be necessary to create the sections with the data received and to configure them. Then push them in the $data
array.
The Section
calls come from a helper class in Minotaure. This class is not autoload so we need to call it just after the namespace.
use Be\Minotaure\Widgets\Core\Sections;
Sections::create((array) $data);
Sections::set((array) $sectionsCreated, (array) $this)
public function form($instance)
{
$data = array_merge($this->default, $instance, [
'widget' => $this,
'sections' => ''
]);
$data['title'] = html_entity_decode($data['title']);
$newSections = Sections::create($data);
$data['sections'] = Sections::set($newSections, $this);
}
2
3
4
5
6
7
8
9
10
11
12
Last but not least, we need to return this datas to our future form. Same as Section
class, we need to load SetView
class.
use Be\Minotaure\Widgets\Core\SetView;
SetView::form((string) view.path, (array) $datas);
public function form($instance)
{
$data = array_merge($this->default, $instance, [
'widget' => $this,
'sections' => ''
]);
$data['title'] = html_entity_decode($data['title']);
$newSections = Sections::create($data);
$data['sections'] = Sections::set($newSections, $this);
SetView::form('tailor.accordion', $data);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Now, you will be able to disable your widget in the dashboard and see the widget button in the panel. But you need a form and save all data from inputs.
# update()
Update function get all the data from your form, sanitize them before being sent to the database.
The function need to pass your $instance
and $old_instance
, it contains all datas already registered and new datas.
public function update($instance, $old_instance)
{
}
2
3
4
First get and update sections in the DB
public function update($instance, $old_instance)
{
$sections = Sections::get($instance);
newSections = Sections::update($sections);
}
2
3
4
5
Then do the same with the data. For security purposes it is essential to validate data before sending them in the db.
We need a two class Validator
. One for all input, the second one only for editors.
use Be\Minotaure\Plugin\Facades\Validator;
use Be\Minotaure\Widgets\Core\Validator;
2
Oops both have the same class name, let's rename one of them.
use Be\Minotaure\Plugin\Facades\Validator;
use Be\Minotaure\Widgets\Core\Validator as ValidatorEditor;
2
3
Then use it and merge sections with array of data before returning it. All type of input need a different validation. Read the complete list here
public function update($instance, $old_instance)
{
$sections = Sections::get($instance);
$newSections = Sections::update($sections);
$data = Validator::multiple($instance, [
'title' => ['textfield'],
'text' => ValidatorEditor::editor(),
]);
return array_merge($data, $newSections);
}
2
3
4
5
6
7
8
9
10
11
12
Now the form is visible and is able to update data.
# widget()
Widget function call data and send it to the front-end.
The function need to pass $args
from Composer
and $instance
.
public function widget($args, $instance)
{
}
2
3
First merge $args
, $this->default
, $instance
together and decode all text input type.
public function widget($args, $instance)
{
$data = array_merge($args, $this->default, $instance);
$data['title'] = html_entity_decode($data['title']);
}
2
3
4
5
Then we need to get sections in our instance and parse them if there has, to finally add them to array $data
public function widget($args, $instance)
{
$data = array_merge($args, $this->default, $instance);
$data['title'] = html_entity_decode($data['title']);
$sections = Sections::get($instance);
if (!empty($sections)) $sections = Sections::parse($sections);
$data['sections'] = $sections;
}
2
3
4
5
6
7
8
9
The very last, we call the widget view.
SetView::view((string) view.path, (array) $datas);
public function widget($args, $instance)
{
$data = array_merge($args, $this->default, $instance);
$data['title'] = html_entity_decode($data['title']);
$sections = Sections::get($instance);
if (!empty($sections)) $sections = Sections::parse($sections);
$data['sections'] = $sections;
SetView::widget('tailor.accordion', $data);
}
2
3
4
5
6
7
8
9
10
11
# m_form.twig
Previously we were using php
, now we will continue with twig language.
React manage for you a big part of the widget instance. You only need to create a form to send data.
Most of HTML input are already created with right classes, id, etc…
# Create new input
In the Register.php
, we define two inputs :
- title
- text
Title is a simple input text but Text is a complete editor.
First create the title
<fieldset>
{% include 'm_components/form/input/text.twig' with {
label : "title",
value: title,
title_text: __("Title", constant('M_TD')),
class: "widefat"
} %}
</fieldset>
2
3
4
5
6
7
8
(string) label
: The ID of the input in this specific instance, must be the same than the value and in register file.
(var) value
: Call the variable sent to $data
(string) title_text
: Label of the input (client side)
(string) class
: Add input class
Then create the editor
<fieldset>
{% include 'm_components/form/input/text.twig' with {
label : "title",
value: title,
title_text: __("Title", constant('M_TD')),
class: "widefat"
} %}
{% include 'm_components/form/input/editor.twig' with {
label: "text",
value: text,
title_text:__("Description", constant('M_TD')),
class: "text-text"
} %}
</fieldset>
2
3
4
5
6
7
8
9
10
11
12
13
14
See all inputs available here
Now we are going to create a div for the sections registered
<div class="repeater-wrapper">
{% if sections %}
<div class="mt-repeater-hidden">
<div class="mt-repeater-hidden-inputs">
{{ sections|raw }}
</div>
</div>
{% endif %}
</div>
2
3
4
5
6
7
8
9
and a div to create the repeater
<div class="mt-repeater-main-container repeater-container-columns">
"Input":{
"label": "Titre",
"name": "title"
},
"Editor":{
"label": "Texte",
"name": "text"
}
</div>
2
3
4
5
6
7
8
9
10
(string) "Input"
: the name of the function to call
(string) "label"
: Label of the input
(string) "name"
: name and id of the input
See all inputs available for the repeater here
# m_widget.twig
All variable stocked in the $data
are available at the same name. In Register.php
, we previously defined :
- title
- text
- sections
First create a condition to show the widget only if there is one of those variables.
{% if sections or title or text %}
{% endif %}
2
3
Then create widget section. All global class for a widget must be prefixed by a m_
.
You also need to add a data named m-pack="tailor"
.
{% if sections or title or text %}
<section class="m_accordion" m-pack="tailor">
</section>
{% endif %}
2
3
4
5
Now you can construct your html.
{% if sections or title or text %}
<section class="m_panel m_accordion" m-pack="tailor">
{% include 'm_components/widgets/headline.twig' with {title: title, text: text} only %}
{% if sections %}
<div class="m_details">
{% for section in sections %}
<div class="m_details-item">
<h2 class="m_details-item__head">{{section.title|raw}}</h2>
<div class="m_details-item__body">
{{fn.apply_filters('the_content', fn.html_entity_decode(section.text))|raw}}
</div>
</div>
{% endfor %}
</div>
{% endif %}
</section>
{% endif %}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
WARNING
Texts need to be escape for special characters. Use |raw
Editor needs to be decoded and escaped. Use fn.html_entity_decode(section.text))|raw