Unyson Framework
Unyson Framework
Unyson Framework
Release
Contents
Minimum Requirements
Installation
2.1 Install as plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Install anywhere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
5
5
License
3.1 Convention . . . .
3.2 Options . . . . . .
3.3 Extensions . . . .
3.4 Components . . .
3.5 Helpers . . . . . .
3.6 Manifest . . . . .
3.7 Built-in Extensions
3.8 Filters & Actions .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
.
7
. 13
. 47
. 52
. 55
. 74
. 77
. 145
ii
Unyson is a framework for WordPress that facilitates development of a theme. This framework was created from the
ground up by the team behind ThemeFuse from the desire to empower developers to build outstanding WordPress
themes fast and easy.
Note: This documentation assumes you have a working knowledge of WordPress. If you havent, please start by
reading WordPress Documentation.
Contents
Contents
CHAPTER 1
Minimum Requirements
CHAPTER 2
Installation
Chapter 2. Installation
CHAPTER 3
License
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU
General Public License is intended to guarantee your freedom to share and change free software. Unyson inherits the
General Public License (GPL) from WordPress.
3.1 Convention
3.1.1 General
The framework was built following some rules to ensure compatibility between components and to provide an easier
way for developers to work together. Here are some starting rules to keep in mind:
The code should work on php 5.2.4, like WordPress Minimum Requirements says. Dont use php 5.3+ features,
because some hosting providers dont have php 5.3+ installed on the servers.
Follow WordPress Coding Standards.
Note: If you already have some code written with spaces indentation (that does not follow WordPress
Coding Standards), use this RegExp to replace spaces with tabs:
(?<=^\s*) {4} replace with \t
3.1.2 Prefixes
In the framework everything is prefixed to prevent naming conflicts and to give a meaning to functions, classes and
methods names.
Core
Theme
Extensions
Core
Public functions and classes should be prefixed with:
fw_ for functions
7
Note: A Public function is meant to be used by anyone. Usually its a helper function that does
something useful.
Private functions and classes should be prefixed with:
_fw_ for functions
_FW_ for classes
/**
* @internal
*/
function _fw_private_function() {
// ...
}
/**
* @internal
*/
class _FW_Private_Class
{
// ...
}
Note: A private function is used somewhere internally. Dont forget to use the @internal tag
in your PhpDoc in order to make it clear to other developers that this is a private function. It will
also remove the function from your documentation (if you are using an automatic documentation
generator)
Functions and methods used for hooks should be prefixed with:
_action_ for add_action()
_filter_ for add_filter()
/**
* @internal
*/
function _action_init_something() {
// ...
}
add_action('init', '_action_init_something');
Important: Be sure the function name is unique enough in order to minimize the chances to be
defined by someone else. Do not use too simple function names like _action_init.
Chapter 3. License
class FW_Example
{
public function __construct()
{
add_filter('the_content', array($this, '_filter_the_content'));
}
/**
* @internal
*/
public function _filter_the_content($content) {
// ...
return $content;
}
}
Theme
Public functions and classes should be prefixed with:
fw_theme_ for functions
FW_Theme_ for classes
function fw_theme_head() {
// ...
}
class FW_Theme_Pagination
{
// ...
}
3.1. Convention
// ...
}
Extensions
Public functions and classes should be prefixed with:
fw_ext_<extension-name>_ for functions
FW_Ext_<extension-name>_ for classes
Private functions and classes should be prefixed with:
_fw_ext_<extension-name>_ for functions
_FW_Ext_<extension-name>_ for classes
Functions used for hooks should be prefixed with:
_action_fw_ext_<extension-name>_ for add_action()
_filter_fw_ext_<extension-name>_ for add_filter()
For e.g. if extension name is demo:
/**
* @internal
*/
function _filter_fw_ext_demo_the_content($content) {
// ...
return $content;
10
Chapter 3. License
}
add_filter('the_content', '_filter_fw_ext_demo_the_content');
/**
* @internal
*/
function _action_fw_ext_demo_init() {
// ...
}
add_action('init', '_action_fw_ext_demo_init');
themes/
-parent-theme/
| -framework-customizations/
|
-extensions/
|
| -extension-name/
|
| -...
|
-theme/
|
-manifest.php
# Theme details: title, description, version, dependencies, etc.
|
-config.php
# Theme specific configuration
|
-options/
|
-settings.php # Theme settings options
|
-customizer.php # Customizer options
|
-posts/
# Post types options
|
| -post.php
|
| -testimonial.php
|
| -{post-type}.php
|
| -...
|
-taxonomies/ # Taxonomy terms options
|
-category.php
|
-post_tag.php
|
-{taxonomy}.php
|
-...
-child-theme/
-framework-customizations/
-... # same as in then parent theme, but here you can overwrite specific files from the parent th
Lets take a closer look at each directory and file, and understand how it works.
framework-customizations/theme/ - Contains options, views, helpers, and all bunch of theme stuff,
well take a closer look at every file below.
framework-customizations/theme/manifest.php - Contains an array with information about
theme, accessible through fw()->theme->manifest->get(key);. More details about the theme
manifest.
3.1. Convention
11
Plugin wp-content/plugins/whaterever-plugin/custom-dir/extensions/{extension-name}
12
Chapter 3. License
3.2 Options
3.2.1 Introduction
Introduction
Options files
Containers
Restrictions
Introduction
Options are intended for creating form fields representing different kind of data e.g. rich and plain text, icons, media
content, fonts and more. With options you can easily create tabs, boxes and form inputs for the admin pages. You just
build an array and it will be transformed to html. On form submit, values will be saved into the database, and you will
be able to access them anywhere you want using fw_get_db_..._option() helper functions.
For advanced users, this is an easy way to create form inputs and use them for various purposes. The simplest options
array looks something like this:
$options = array(
'option_id' => array(
'type' => 'text'
)
);
This will generate a text input. The array key is used as option id, it should be unique. Values in the database will be
stored as array(option_id => value).
Note: The only required parameter for any option is type.
All options have some base parameters:
label (string) Label
desc (string) Description
value (mixed) Default value
attr (array) HTML attributes (some options will place these attributes in input, other in wrapper div)
help (string|array) Additional info about option. This will generate an
text in a tip popup.
Some options can have additional (optional) parameters. A better customized option will look like this:
$options = array(
'option_id' =>
'type' =>
'value' =>
'label' =>
'desc' =>
'attr' =>
'help' =>
3.2. Options
array(
'text',
'Default value',
__('Option Label', '{domain}'),
__('Option Description', '{domain}'),
array('class' => 'custom-class', 'data-foo' => 'bar'),
__('Some html that will appear in tip popup', '{domain}'),
13
)
);
You can test the above array by creating any of the below options file and placing the array in it.
Options files
These are the main places where options are used:
Note: Like options, containers have a minimum set of required parameters: type and options. The type
parameter in Customizer options is optional and its not used (has no effect).
There are 4 built-in container types:
box - WordPress metabox
'box_id' => array(
'type' => 'box',
'options' => array(
'option_id' => array( 'type' => 'text' ),
),
'title' => __('Box Title', '{domain}'),
'attr' => array('class' => 'custom-class', 'data-foo' => 'bar'),
/**
* When used in Post Options on the first array level
* the ``box`` container accepts additional parameters
*/
//'context' => 'normal|advanced|side',
//'priority' => 'default|high|core|low',
),
tab - One tab (Tabs from the same array level will be collected and rendered as multiple tabs)
14
Chapter 3. License
group - Group options into a wrapper div. Has no design. Usually used to show/hide a group of options from
javascript
'group_id' => array(
'type' => 'group',
'options' => array(
'option_id' => array( 'type' => 'text' ),
),
'attr' => array('class' => 'custom-class', 'data-foo' => 'bar'),
),
Restrictions
3.2. Options
15
Customizer Options
1. Create {theme}/framework-customizations/theme/options/customizer.php with the following contents:
<?php if (!defined( 'FW' )) die('Forbidden');
$options = array(
'body-color' => array(
'type' => 'color-picker',
'label' => __('Body Color', '{domain}'),
'value' => '#ADFF2F',
),
);
2. Add in {theme}/functions.php
function _action_theme_wp_print_styles() {
if (!defined('FW')) return; // prevent fatal error when the framework is not active
$option_value = fw_get_db_customizer_option('body-color');
echo '<style type="text/css">'
. 'body { '
. 'border: 30px solid '. esc_html($option_value) .'; '
. '}'
. '</style>';
}
add_action('wp_print_styles', '_action_theme_wp_print_styles');
3. Go to menu Appearance > Customize, find the Body Color option and change it.
Hint: You can enable Live Preview for customizer options.
Settings Options
1. Create {theme}/framework-customizations/theme/options/settings.php with the following contents:
<?php if (!defined( 'FW' )) die('Forbidden');
$options = array(
'body-color' => array(
'type' => 'color-picker',
'label' => __('Body Color', '{domain}'),
'value' => '#ADFF2F',
),
);
2. Add in {theme}/functions.php
function _action_theme_wp_print_styles() {
if (!defined('FW')) return; // prevent fatal error when the framework is not active
$option_value = fw_get_db_settings_option('body-color');
16
Chapter 3. License
3. Go to menu Appearance > Theme Settings, find the Body Color option, change it and press Save.
4. Go to frontend and see the changes.
Post Options
1. Create {theme}/framework-customizations/theme/options/posts/post.php with the following contents:
<?php if (!defined( 'FW' )) die('Forbidden');
$options = array(
'main' => array(
'type' => 'box',
'title' => __('Testing Options', '{domain}'),
'options' => array(
'body-color' => array(
'type' => 'color-picker',
'label' => __('Body Color', '{domain}'),
'value' => '#ADFF2F',
),
),
),
);
2. Add in {theme}/functions.php
function _action_theme_wp_print_styles() {
if (!defined('FW')) return; // prevent fatal error when the framework is not active
global $post;
if (!$post || $post->post_type != 'post') {
return;
}
$option_value = fw_get_db_post_option($post->ID, 'body-color');
echo '<style type="text/css">'
. 'body { '
. 'border: 30px solid '. esc_html($option_value) .'; '
. '}'
. '</style>';
}
add_action('wp_print_styles', '_action_theme_wp_print_styles');
3. Create a new Post, find Body Color option, change it and save the post.
4. Open the post in frontend and see the changes.
3.2. Options
17
3.2.3 Customizer
Starting with v2.3.0 options can be used in Customizer.
Customizer Options {theme}/framework-customizations/theme/options/customizer.php are
turned into Customizer elements (panels, sections and controls).
The customizer elements have a strict structure which also applies to options array structure:
Containers can be nested only 2 levels
container > option is turned into section > control
container > container > option is turned into panel > section > control
container > container > container > option will not work panel > section >
ERROR
Containers must contain only options or only containers, because a panel cant contain both sections and controls.
Examples
Try the below arrays in {theme}/framework-customizations/theme/options/customizer.php.
Create a Section
$options = array(
'section_1' => array(
'title' => __('Unyson Section', '{domain}'),
'options' => array(
'option_1' => array(
'type' => 'text',
'value' => 'Default Value',
'label' => __('Unyson Option', '{domain}'),
'desc' => __('Option Description', '{domain}'),
),
),
),
);
18
Chapter 3. License
),
),
'section_2' => array(
'title' => __('Unyson Section #2', '{domain}'),
'options' => array(
'option_2' => array(
'type' => 'text',
'value' => 'Default Value',
'label' => __('Unyson Option #2', '{domain}'),
'desc' => __('Option Description', '{domain}'),
),
'option_3' => array(
'type' => 'text',
'value' => 'Default Value',
'label' => __('Unyson Option #3', '{domain}'),
'desc' => __('Option Description', '{domain}'),
),
),
),
),
),
);
Live Preview
In background, customizer options are converted into customizer elements, so they follow default WordPress behavior
and implementing a live preview can be done using the default WordPress solution.
1. Change the setting transport and enqueue the javascript
// file: {theme}/inc/hooks.php
if (defined('FW')):
/**
* @param WP_Customize_Manager $wp_customize
* @internal
*/
function _action_customizer_live_fw_options($wp_customize) {
if ($wp_customize->get_setting('fw_options[OPTION_ID]')) {
$wp_customize->get_setting('fw_options[OPTION_ID]')->transport = 'postMessage';
3.2. Options
19
function _action_customizer_live_fw_options_preview() {
wp_enqueue_script(
'mytheme-customizer',
get_template_directory_uri() .'/assets/js/theme-customizer.js',
array( 'jquery','customize-preview' ),
fw()->theme->manifest->get_version(),
true
);
}
endif;
( function( $ ) {
wp.customize( 'fw_options[OPTION_ID]', function( value ) {
value.bind( function( newval ) {
/**
* An array of collected html inputs
* [{'name':'input[name]','value':'input value'}]
* or
* [{'name':'input[name]','value':'input value'},{'name':'input[name]','value':'
*/
newval = JSON.parse(newval);
$( 'h1' ).text( newval[0].value );
} );
} );
} )( jQuery );
Note: The value comes in [{name:input[name],value:input value}] format, because the customizer form is not submitted as a regular form. A control can store its value
only inside a single input which has some special attributes (instead of name="...") and it is
monitored for changes by the Customizer script to trigger the preview update. Because of that, the
framework options collect all their inputs values and store them in that special input (here is an
advanced explanation).
20
Chapter 3. License
}
/* wrong */
.some-class {
color: blue;
}
Javascript
All javascript must stick to .fw-option-type-{type} class and work only within the main/wrapper element
(no events attached to the body). If the option type has custom javascript events, those events must be triggered on the
main element.
$someInnerElement.closest('.fw-option-type-demo')
.trigger('fw:option-type:demo:custom-event', {some: 'data'});
If its specified in the documentation that an option type has custom events, it means that you can attach event listeners
on the elements with .fw-option-type-{type} class (not on body or fwEvents).
Caution: Do not confuse .fw-option-type-{type} with .fw-backend-option-type-{type}
class which is used internally by the framework and should not be used in option type scripts.
=>
=>
=>
=>
=>
=>
'text',
'default value',
array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
__('Label', '{domain}'),
__('Description', '{domain}'),
__('Help tip', '{domain}'),
Textarea
Regular textarea.
array(
'type'
'value'
'attr'
'label'
3.2. Options
=>
=>
=>
=>
'textarea',
'default value',
array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
__('Label', '{domain}'),
21
'desc'
'help'
Checkbox
Single checkbox.
array(
'type'
'value'
'attr'
'label'
'desc'
'help'
'text'
)
=>
=>
=>
=>
=>
=>
=>
'checkbox',
true, // checked/unchecked
array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
__('Label', '{domain}'),
__('Description', '{domain}'),
__('Help tip', '{domain}'),
__('Yes', '{domain}'),
Checkboxes
A list of checkboxes.
array(
'type' => 'checkboxes',
'value' => array(
'choice-1' => false,
'choice-2' => true,
),
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'choices' => array( // Note: Avoid bool or int keys http://bit.ly/1cQgVzk
'choice-1' => __('Choice 1', '{domain}'),
'choice-2' => __('Choice 2', '{domain}'),
'choice-3' => __('Choice 3', '{domain}'),
),
// Display choices inline instead of list
'inline' => false,
)
Radio
A list of radio buttons.
array(
'type' => 'radio',
'value' => 'choice-3',
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'choices' => array( // Note: Avoid bool or int keys http://bit.ly/1cQgVzk
'choice-1' => __('Choice 1', '{domain}'),
'choice-2' => __('Choice 2', '{domain}'),
22
Chapter 3. License
Select
Regular select.
array(
'type' => 'select',
'value' => 'choice-3',
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'choices' => array(
'' => '---',
'choice-1' => __('Choice 1', '{domain}'),
'choice-2' => array(
'text' => __('Choice 2', '{domain}'),
'attr' => array('data-foo' => 'bar'),
),
array( // optgroup
'attr'
=> array('label' => __('Group 1', '{domain}')),
'choices' => array(
'choice-3' => __('Choice 3', '{domain}'),
// ...
),
),
),
/**
* Allow save not existing choices
* Useful when you use the select to populate it dynamically from js
*/
'no-validate' => false,
)
Select Multiple
Select with multiple values.
array(
'type' => 'select-multiple',
'value' => array( 'choice-1', 'choice-3' ),
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'choices' => array(
'' => '---',
'choice-1' => __('Choice 1', '{domain}'),
'choice-2' => array(
'text' => __('Choice 2', '{domain}'),
'attr' => array('data-foo' => 'bar'),
3.2. Options
23
),
array( // optgroup
'attr'
=> array('label' => __('Group 1', '{domain}')),
'choices' => array(
'choice-3' => __('Choice 3', '{domain}'),
// ...
),
),
),
)
Multi-Select
Select multiple choices from different sources: posts, taxonomies, users or a custom array.
array(
'type' => 'multi-select',
'value' => array( 'choice-1', 'choice-3' ),
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
/**
* Set population method
* Are available: 'posts', 'taxonomy', 'users', 'array'
*/
'population' => 'array',
/**
* Set post types, taxonomies, user roles to search for
*
* 'population' => 'posts'
* 'source' => 'page',
*
* 'population' => 'taxonomy'
* 'source' => 'category',
*
* 'population' => 'users'
* 'source' => array( 'editor', 'subscriber', 'author' ),
*
* 'population' => 'array'
* 'source' => '' // will populate with 'choices' array
*/
'source' => '',
/**
* Set the number of posts/users/taxonomies that multi-select will be prepopulated
* Or set the value to false in order to disable this functionality.
*/
'prepopulate' => 10,
/**
* An array with the available choices
* Used only when 'population' => 'array'
*/
'choices' => array(
'choice-1' => __('Choice 1', '{domain}'),
'choice-2' => __('Choice 2', '{domain}'),
'choice-3' => __('Choice 3', '{domain}'),
),
24
Chapter 3. License
/**
* Set maximum items number that can be selected
*/
'limit' => 100,
)
Switch
Switch between two choices.
array(
'type' => 'switch',
'value' => 'hello',
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'left-choice' => array(
'value' => 'goodbye',
'label' => __('Goodbye', '{domain}'),
),
'right-choice' => array(
'value' => 'hello',
'label' => __('Hello', '{domain}'),
),
)
Custom Events
Color Picker
Pick a color.
array(
'type' =>
'value' =>
'attr' =>
// palette
'palettes'
'label' =>
'desc' =>
'help' =>
)
'color-picker',
'#FF0000',
array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
colors array
=> array( '#ba4e4e', '#0ce9ed', '#941940' ),
__('Label', '{domain}'),
__('Description', '{domain}'),
__('Help tip', '{domain}'),
25
array(
'type' =>
'value' =>
'attr' =>
// palette
'palettes'
'label' =>
'desc' =>
'help' =>
)
'rgba-color-picker',
'rgba(255,0,0,0.5)',
array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
colors array
=> array( '#ba4e4e', '#0ce9ed', '#941940' ),
__('Label', '{domain}'),
__('Description', '{domain}'),
__('Help tip', '{domain}'),
Gradient
Pick gradient colors.
array(
'type' => 'gradient',
'value' => array(
'primary'
=> '#FF0000',
'secondary' => '#0000FF',
)
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
)
Image Picker
Pick an image.
array(
'type' => 'image-picker',
'value' => 'image-2',
'attr' => array(
'class'
=> 'custom-class',
'data-foo' => 'bar',
),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'choices' => array(
'value-1' => get_template_directory_uri() .'/images/thumbnail.png',
'value-2' => array(
// (required) url for thumbnail
'small' => get_template_directory_uri() .'/images/thumbnail.png',
// (optional) url for large image that will appear in tooltip
'large' => get_template_directory_uri() .'/images/preview.png',
// (optional) choice extra data for js, available in custom events
'data' => array(...)
),
'value-3' => array(
// (required) url for thumbnail
'small' => array(
'src' => get_template_directory_uri() .'/images/thumbnail.png',
'height' => 70
26
Chapter 3. License
),
// (optional) url for large image that will appear in tooltip
'large' => array(
'src' => get_template_directory_uri() .'/images/preview.png',
'height' => 400
),
// (optional) choice extra data for js, available in custom events
'data' => array(...)
),
),
'blank' => true, // (optional) if true, images can be deselected
)
Custom Events
3.2. Options
27
Date Picker
Pick a date in calendar.
array(
'type' => 'date-picker',
'value' => '',
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'monday-first' => true, // The week will begin with Monday; for Sunday, set to false
'min-date' => date('d-m-Y'), // By default minimum date will be current day. Set a date in format
'max-date' => null, // By default there is not maximum date. Set a date in format d-m-Y as a star
)
Datetime Picker
Pick a datetime in calendar.
array(
'type' => 'datetime-picker',
'value' => '',
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'datetime-picker' => array(
'format'
=> 'Y/m/d H:i', // Format datetime.
'maxDate'
=> false, // By default there is not maximum date , set a date in the dateti
'minDate'
=> false, // By default minimum date will be current day, set a date in the
'timepicker'
=> true,
// Show timepicker.
'datepicker'
=> true,
// Show datepicker.
'defaultTime'
=> '12:00' // If the input value is empty, timepicker will set time use defau
),
)
Datetime Range
Set a datetime range.
array(
'type' => 'datetime-range',
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'datetime-pickers' => array(
'from' => array(
'minDate' => '1970/01/01', // By default minimum date will be current day, set a date in the
'maxDate' => '2038/01/19', // By default there is not maximum date , set a date in the dateti
'format' => 'Y/m/d H:i', // Format datetime.
'timepicker' => true,
// Show timepicker.
'datepicker' => true,
// Show datepicker.
),
'to' => array(
28
Chapter 3. License
'minDate' => '1970/01/01', // By default minimum date will be current day, set a date in the
'maxDate' => '2038/01/19', // By default there is not maximum date , set a date in the dateti
'format' => 'Y/m/d H:i', // Format datetime.
'timepicker' => true,
// Show timepicker.
'datepicker' => true,
// Show datepicker.
)
),
'value' => array(
'from' => '',
'to' => ''
)
)
Icon v2
array(
'type'
=> 'icon-v2',
/**
* small | medium | large | sauron
* Yes, sauron. Definitely try it. Great one.
*/
'preview_size' => 'medium',
/**
* small | medium | large
*/
'modal_size' => 'medium',
/**
* There's no point in configuring value from code here.
*
* I'll document the result you get in the frontend here:
* 'value' => array(
'type' => 'icon-font', // icon-font | custom-upload
*
*
// ONLY IF icon-font
*
'icon-class' => '',
*
'icon-class-without-root' => false,
*
'pack-name' => false,
*
'pack-css-uri' => false
*
*
// ONLY IF custom-upload
*
// 'attachment-id' => false,
*
// 'url' => false
*
* ),
*/
'attr'
'label'
'desc'
'help'
=>
=>
=>
=>
Default value is not really supported, because of the complexity of the data that this option type holds.
The second version of the first Icon option type. It was improved a lot in terms of both UI and extensibility. The user
3.2. Options
29
will be able to filter through a list of icon packs and also upload his own icon. The result value will contain type
field and it will contain the type of the selected content. It can be icon-font or custom-upload. Youll also get
favorite icon functionallity which will work out of the box.
Note: Youll have to enable SVG uploads by yourself, with a hook in your theme.
By default, we have just 6 icon packs enabled and served with Unyson itself.
Font Awesome
Entypo
Linecons
Linearicons
Typicons
Unycons
Note: By default, none of this packs will be enqueued in the frontend of your theme.
Icon V2 is easily extensible with a couple of filters you can hook into. First, you may want to configure which of the
already registered packs we should display into the picker.
function _custom_packs_list($current_packs) {
/**
* $current_packs is an array of pack names.
* You should return which one you would like to show in the picker.
*/
return array('font-awesome', 'unycon');
}
add_filter('fw:option_type:icon-v2:filter_packs', '_custom_packs_list');
Note: Thats a global hook which changes behavior for all pickers. Configuring packs per picker is not available and
will not be implemented later. If you have some particular use case for this, please fill an issue.
Long story short, you can add more packs by filtering on fw:option_type:icon-v2:packs filter. Simplest
example, all of the keys are required:
add_filter('fw:option_type:icon-v2:packs', '_add_my_pack');
function _add_my_pack($default_packs) {
/**
* No fear. Defaults packs will be merged in back. You can't remove them.
* Changing some flags for them is allowed.
30
Chapter 3. License
*/
return array(
'my_pack' => array(
'name' => 'my_pack', // same as key
'title' => 'My Cool Pack',
'css_class_prefix' => 'my-pack',
'css_file' => 'path_to_css_file',
'css_file_uri' => 'network_accessible_url'
)
)
}
And this will just work for most of the cases. You dont need to specify which icons specifically to show inside the
picker. All of them will be showed, by default. In fact, theres some magick going on that will extract all of your icons
and show them up. Ill try to make it clear below.
Computing icons list
By default, when you register an icon pack its icons will be extracted from the css file automatically, so that you dont
have to maintain a long array of icons for each pack. Instead we do some trick instead. We look into the css file for
each pack and look for patterns that look like this:
.`css_class_prefix`-some-icon:before {
content: '\266a';
}
css_class_prefix there refers to the css_class_prefix option you specified for your icon pack.
// Those will be considered an icon
.my-pack-some-icon:before { content: '\266a'; }
.my-pack.my-pack-some-icon:before { content: '\266a'; }
.my-pack.my-pack-some-icon:after { content: '\266a'; }
// This one won't
.my-pack.my-pack-some-icon:after { color: red; }
Generally speaking, thats what an icon pack CSS file consist of:
@font-face rules
icon generations we try hard to get just them
some other general purpose helpers theyre encountered not that often
You can also completely stop this mechanism for one pack by specifying an array of icons for the icons option. A
more complete pack definition can be found here.
Upload
Single file upload.
array(
'type' => 'upload',
'value' => array(
/*
'attachment_id' => '9',
'url' => '//site.com/wp-content/uploads/2014/02/whatever.jpg'
*/
3.2. Options
31
),
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
/**
* If set to `true`, the option will allow to upload only images, and display a thumb of the sele
* If set to `false`, the option will allow to upload any file from the media library.
*/
'images_only' => true,
/**
* An array with allowed files extensions what will filter the media library and the upload files
*/
'files_ext' => array( 'doc', 'pdf', 'zip' ),
/**
* An array with extra mime types that is not in the default array with mime types from the javas
* The format is: array( '<mime-type>, <ext1> <ext2> <ext2>' ).
* For example: you set rar format to filter, but the filter ignore it , than you must set
* the array with the next structure array( '.rar, rar' ) and it will solve the problem.
*/
'extra_mime_types' => array( 'audio/x-aiff, aif aiff' )
)
Custom Events
array(
'type' => 'multi-upload',
'value' => array(
/*
array(
'attachment_id' => '9',
'url' => '//site.com/wp-content/uploads/2014/02/whatever.jpg'
),
...
*/
// if value is set in code, it is not considered and not used
// because there is no sense to set hardcode attachment_id
),
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
/**
* If set to `true`, the option will allow to upload only images, and display a thumb of the sele
* If set to `false`, the option will allow to upload any file from the media library.
*/
32
Chapter 3. License
Custom Events
Range Slider
Drag the handles to set a numeric value range.
array(
'type' => 'range-slider',
'value' => array(
'from' => 10,
'to'
=> 33,
),
'properties' => array(
/*
3.2. Options
33
'min' => 0,
'max' => 100,
'step' => 1, // Set slider step. Always > 0. Could be fractional.
*/
),
'attr'
'label'
'desc'
'help'
=>
=>
=>
=>
Popup
Popup with options.
array(
'type' => 'popup',
'value' => array(
'option_1' => 'value 1',
'option_2' => 'value 2',
),
'label' => __('Popup', '{domain}'),
'desc' => __('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor in
'popup-title' => __('Popup Title', '{domain}'),
'button' => __('Edit', '{domain}'),
'popup-title' => null,
'size' => 'small', // small, medium, large
'popup-options' => array(
'option_1' => array(
'label' => __('Text', '{domain}'),
'type' => 'text',
'value' => 'Demo text value',
'desc' => __('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod te
'help' => sprintf("%s \n\n'\"<br/><br/>\n\n <b>%s</b>",
__('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor i
__('Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium dolor
),
),
'option_2' => array(
'label' => __('Textarea', '{domain}'),
'type' => 'textarea',
'value' => 'Demo textarea value',
'desc' => __('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod te
'help' => sprintf("%s \n\n'\"<br/><br/>\n\n <b>%s</b>",
__('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor i
__('Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium dolor
),
),
),
)
Addable Popup
Addable popup with options.
34
Chapter 3. License
array(
'type' => 'addable-popup',
'value' => array(
array(
'option_1' => 'value 1',
'option_2' => 'value 2',
),
// ...
),
'label' => __('Addable Popup', '{domain}'),
'desc' => __('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor in
'template' => '{{- demo_text }}',
'popup-title' => null,
'size' => 'small', // small, medium, large
'limit' => 0, // limit the number of popup`s that can be added
'add-button-text' => __('Add', '{domain}'),
'sortable' => true,
'popup-options' => array(
'option_1' => array(
'label' => __('Text', '{domain}'),
'type' => 'text',
'value' => 'Demo text value',
'desc' => __('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod te
'help' => sprintf("%s \n\n'\"<br/><br/>\n\n <b>%s</b>",
__('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor i
__('Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium dolor
),
),
'option_2' => array(
'label' => __('Textarea', '{domain}'),
'type' => 'textarea',
'value' => 'Demo textarea value',
'desc' => __('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod te
'help' => sprintf("%s \n\n'\"<br/><br/>\n\n <b>%s</b>",
__('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor i
__('Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium dolor
),
),
),
)
Addable Option
Create a list of options.
array(
'type' => 'addable-option',
'value' => array('Value 1', 'Value 2', 'Value 3'),
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'option' => array( 'type' => 'text' ),
'add-button-text' => __('Add', '{domain}'),
'sortable' => true,
)
3.2. Options
35
Custom Events
Custom Events
fw:option-type:addable-box:box:init - Box was initialized. Triggered for each existing box after page
load, or when a box was added.
fw:option-type:addable-box:control:click - A custom control was clicked.
Typography v2
Choose font family, style, weight, size, line-height, letter-spacing and color.
array(
'type' => 'typography-v2',
'value' => array(
'family' => 'Amarante',
// For standard fonts, instead of subset and variation you should set 'style' and 'weight'.
// 'style' => 'italic',
// 'weight' => 700,
'subset' => 'latin-ext',
'variation' => 'regular',
'size' => 14,
36
Chapter 3. License
WP Editor
Textarea with the WordPress Editor like the one you use on the blog posts edit pages.
array(
'type' => 'wp-editor',
'value' => 'default value',
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'size' => 'small', // small, large
'editor_height' => 400,
'wpautop' => true,
'editor_type' => false, // tinymce, html
/**
* Also available
* https://github.com/WordPress/WordPress/blob/4.4.2/wp-includes/class-wp-editor.php#L80-L94
*/
)
Multi-Picker
Pick a choice, then complete options related to that choice.
The picker parameter holds a valid option type with choices. Supported option types are select, radio,
image-picker and switch.
array(
'type' => 'multi-picker',
'label' => false,
'desc' => false,
'value' => array(
/**
* '<custom-key>' => 'default-choice'
*/
'gadget' => 'phone',
3.2. Options
37
/**
* These are the choices and their values,
* they are available after option was saved to database
*/
'laptop' => array(
'price' => '123',
'webcam' => false
),
'phone' => array(
'price' => '456',
'memory' => '32'
)
),
'picker' => array(
// '<custom-key>' => option
'gadget' => array(
'label'
=> __('Choose device', '{domain}'),
'type'
=> 'select', // or 'short-select'
'choices' => array(
'phone' => __('Phone', '{domain}'),
'laptop' => __('Laptop', '{domain}')
),
'desc'
=> __('Description', '{domain}'),
'help'
=> __('Help tip', '{domain}'),
)
),
/*
'picker' => array(
// '<custom-key>' => option
'gadget' => array(
'label'
=> __('Choose device', '{domain}'),
'type'
=> 'radio',
'choices' => array(
'phone' => __('Phone', '{domain}'),
'laptop' => __('Laptop', '{domain}')
),
'desc'
=> __('Description', '{domain}'),
'help'
=> __('Help tip', '{domain}'),
)
),
*/
/*
'picker' => array(
// '<custom-key>' => option
'gadget' => array(
'label'
=> __('Choose device', '{domain}'),
'type'
=> 'image-picker',
'choices' => array(
'phone' => 'http://placekitten.com/70/70',
'laptop' => 'http://placekitten.com/71/70'
),
'desc'
=> __('Description', '{domain}'),
'help'
=> __('Help tip', '{domain}'),
)
),
*/
/*
picker => array(
38
Chapter 3. License
3.2. Options
39
If you want to use in picker an option type that is not supported by default (is not present in the examples above), follow the steps below. In this example, is added support for icon option type (it is not practical, just for demonstration
purposes).
1. Add in {theme}/inc/hooks.php
/**
* Generate array( 'choice_id' => array( Choice Options ) )
* @internal
* @param array $choices
* @param array $data
* @return array
*/
function _filter_theme_option_type_multi_picker_choices_icon($choices, $data) {
$choices = $data['option']['choices'];
// maybe check and remove invalid choices ...
return $choices;
}
add_filter(
'fw_option_type_multi_picker_choices:icon',
'_filter_theme_option_type_multi_picker_choices_icon',
10, 2
);
/**
* @internal
*/
function _admin_theme_multi_picker_custom_picker_scripts() {
wp_enqueue_script(
'multi-picker-custom-pickers',
get_template_directory_uri() . '/js/multi-picker-custom-pickers.js',
array('fw-events'),
false,
true
);
}
add_action(
'admin_enqueue_scripts',
'_admin_theme_multi_picker_custom_picker_scripts'
);
2. Add in {theme}/js/multi-picker-custom-pickers.js
fwEvents.on('fw:option-type:multi-picker:init:icon', function(data){
data.$pickerGroup.find('.fw-option-type-icon > input[type="hidden"]').on('change', funct
data.chooseGroup(
this.value // this is `choice_id` from the `fw_option_type_multi_picker_choices:
);
}).trigger('change');
});
3. Add in {theme}/framework-customizations/theme/options/settings.php
$options = array(
40
Chapter 3. License
4. Open Theme Settings page and pick the Bitcoin or Viacoin icon.
Map
Google maps location.
array(
'type' => 'map',
'value' => array(
'coordinates' => array(
'lat'
=> -34,
'lng'
=> 150,
)
),
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
)
Multi
Group any options database values under a single array key. This option has no design, inner options will look the
same as other options (its like the group container).
3.2. Options
41
Important: The parameter that contains options is named inner-options not options otherwise this will be
treated as a container option.
Hidden
Simple hidden input.
array(
'type' => 'hidden',
'value' => 'default value',
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
)
Tip: The hidden input is not visible, so parameters like label, desc and help have no sense here.
HTML
If you want to display a custom piece of html, use this option type.
Note: This option type has a value stored in a hidden input. Advanced users can create some javascript functionality
42
Chapter 3. License
=>
=>
=>
=>
=>
=>
=>
'html',
'default hidden value',
array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
__('Label', '{domain}'),
__('Description', '{domain}'),
__('Help tip', '{domain}'),
'My <b>custom</b> <em>HTML</em>',
Note: There are html-fixed and html-full option types as well. They are the same as html but has fixed and
full option width.
Password
Regular password input.
array(
'type'
'value'
'attr'
'label'
'desc'
'help'
)
=>
=>
=>
=>
=>
=>
'password',
'default value',
array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
__('Label', '{domain}'),
__('Description', '{domain}'),
__('Help tip', '{domain}'),
Oembed
Generate oembed preview of the inserted link, for more details see Embeds in WordPress.
array(
'type' => 'oembed',
'value' => 'https://vimeo.com/113078377',
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
'preview' => array(
'width' => 300, // optional, if you want to set the fixed width to iframe
'height' => 300, // optional, if you want to set the fixed height to iframe
/**
* if is set to false it will force to fit the dimensions,
* because some widgets return iframe with aspect ratio and ignore applied dimensions
*/
'keep_ratio' => true
)
)
Typography
Choose font family, size, style and color.
3.2. Options
43
array(
'type' => 'typography',
'value' => array(
'family' => 'Arial',
'size'
=> 12,
'style' => '400',
'color' => '#000000'
),
'components' => array(
'family' => true,
'size'
=> true,
'color' => true
),
'attr' => array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
'label' => __('Label', '{domain}'),
'desc' => __('Description', '{domain}'),
'help' => __('Help tip', '{domain}'),
)
Icon
Choose a FontAwesome icon.
array(
'type'
'value'
'attr'
'label'
'desc'
'help'
)
=>
=>
=>
=>
=>
=>
'icon',
'fa-smile-o',
array( 'class' => 'custom-class', 'data-foo' => 'bar' ),
__('Label', '{domain}'),
__('Description', '{domain}'),
__('Help tip', '{domain}'),
44
Chapter 3. License
/**
* @internal
*/
protected function _enqueue_static($id, $option, $data)
{
$uri = get_template_directory_uri() .'/inc/includes/option-types/'. $this->get_type() .'/stat
wp_enqueue_style(
'fw-option-'. $this->get_type(),
$uri .'/css/styles.css'
);
wp_enqueue_script(
'fw-option-'. $this->get_type(),
$uri .'/js/scripts.js',
array('fw-events', 'jquery')
);
}
/**
* @internal
*/
protected function _render($id, $option, $data)
{
/**
* $data['value'] contains correct value returned by the _get_value_from_input()
* You decide how to use it in html
*/
$option['attr']['value'] = (string)$data['value'];
/**
* $option['attr'] contains all attributes.
*
* Main (wrapper) option html element should have "id" and "class" attribute.
*
* All option types should have in main element the class "fw-option-type-{$type}".
* Every javascript and css in that option should use that class.
*
* Remaining attributes you can:
* 1. use them all in main element (if option itself has no input elements)
* 2. use them in input element (if option has input element that contains option value)
*
* In this case you will use second option.
*/
$wrapper_attr = array(
'id'
=> $option['attr']['id'],
'class' => $option['attr']['class'],
);
3.2. Options
45
unset(
$option['attr']['id'],
$option['attr']['class']
);
$html = '<div '. fw_attr_to_html($wrapper_attr) .'>';
$html .= '<input '. fw_attr_to_html($option['attr']) .' type="text" />';
$html .= '<button type="button" class="button">'. __('Clear text', '{domain}') .'</button>';
$html .= '</div>';
return $html;
}
/**
* @internal
*/
protected function _get_value_from_input($option, $input_value)
{
/**
* In this method you receive $input_value (from form submit or whatever)
* and must return correct and safe value that will be stored in database.
*
* $input_value can be null.
* In this case you should return default value from $option['value']
*/
if (is_null($input_value)) {
$input_value = $option['value'];
}
return (string)$input_value;
}
/**
* @internal
*/
protected function _get_defaults()
{
/**
* These are default parameters that will be merged with option array.
* They makes possible that any option has
* only one required parameter array('type' => 'new').
*/
return array(
'value' => ''
);
}
}
FW_Option_Type::register('FW_Option_Type_New');
/**
* Prefix (namespace) all css rules with ".fw-option-type-{$option_type}"
* This guarantees that there will be no conflicts with other styles.
*/
.fw-option-type-new input {
46
Chapter 3. License
background-color: green;
color: white;
}
.fw-option-type-new button {
display: block;
}
jQuery(document).ready(function ($) {
var optionTypeClass = '.fw-option-type-new';
/**
* Listen to special event that is triggered for uninitialized elements
*/
fwEvents.on('fw:options:init', function (data) {
/**
* data.$elements are jQuery selected elements
* that contains options html that needs to be initialized
*
* Find uninitialized options by main class
*/
var $options = data.$elements.find(optionTypeClass +':not(.initialized)');
/**
* Listen for button click and clear input value
*/
$options.on('click', 'button', function(){
$(this).closest(optionTypeClass).find('input').val('');
});
/**
* After everything has done, mark options as initialized
*/
$options.addClass('initialized');
});
});
Option Width
There are three width types:
auto - dynamically adapted to the contents of the option.
fixed - fixed size (it doesnt matter what size, its just fixed).
full - full available width (100%).
Every option has its own width type specified in FW_Option_Type::_get_backend_width_type().
3.3 Extensions
3.3.1 Introduction
Extensions are functionalities that add something new to framework or to another extension. They can be installed via
Extensions page, bundled with a theme or loaded from a plugin (or any directory).
3.3. Extensions
47
Directory Structure
Load Locations
Custom Load Locations
Child Extensions
Directory Structure
Every extension has everything it needs in its own folder: settings, options, scripts, styles, etc. In this way, extensions
can be easy added or removed without affecting other files.
The extension directory has the following structure:
{extension-name}/
-manifest.php # Data about extension: version, name, dependencies, etc.
-class-fw-extension-{extension-name}.php # class FW_Extension_{Extension_Name} extends FW_Extension {
-config.php
# Extension specific configurations
-static.php
# wp_enqueue_style() and wp_enqueue_script()
-posts.php
# register_post_type() and register_taxonomy()
-hooks.php
# add_filter() and add_action()
-helpers.php
# Helper functions and classes
-readme.md.php # Install instructions
-options/
| -posts/
# Post types options
| | -post.php
| | -{post-type}.php
| | -...
| -taxonomies/ # Taxonomies terms options
| | -category.php
| | -post_tag.php
| | -{taxonomy}.php
| | -...
| -...
-settings-options.php # Extension Settings page options
-views/
| -...
-static/
| -js/
| -css/
| -...
-includes/ # All .php files are auto included (no need to require_once)
| -other.php
| -...
---[extensions/] # Directory for sub extensions
Lets take a closer look at each directory and file, and understand how it works.
manifest.php - The only required file, all other files are optional. It contains the base information about
extension. More details about the extension manifest.
class-fw-extension-{extension-name}.php - If the extension has some advanced
functionality, it can define a class that will be the instance of the extension returned by
fw()->extensions->get({extension-name}). By default an instance of default class will
be created, which is an empty class that just extends the FW_Extension class. This file cant be overwritten.
config.php
Configuration
array,
which
is
accessible
through
the
$ext->get_config(key) method.
Users can customize it by creating the same file in
48
Chapter 3. License
{theme-name}/framework-customizations/extension/{extension-name}/config.php
and overwrite only some keys (internally is made array_merge($extension_config,
$theme_customized_config)).
static.php - Enqueue extension scripts and styles.
It is included automatically on
the wp_enqueue_scripts and admin_enqueue_scripts actions,
so you can enqueue both admin and frontend scripts and styles from it, but you will have to use
the is_admin() function.
This file can be overwritten from theme by creating
{theme-name}/framework-customizations/extension/{extension-name}/static.php.
posts.php - Register theme post types and taxonomies in this file. It is included automatically on the init
action.
hooks.php - File containing filters and actions. This file is automatically included as early as possible, in this
way your extension will not miss any action or filter execution.
helpers.php - All extensions helper functions and classes must be in this file.
readme.md.php - Install instructions for users, to make the extension start working.
options/ - A directory containing option files: post types, taxonomies or custom options. The
framework will not automatically pick them (like theme options), only the extension decides
how to use these options.
You can access them through the $ext->get_[...]_options()
methods.
Users can overwrite in the theme any file from the options/ directory, by creating
{theme-name}/framework-customizations/extension/{extension-name}/options/{file-name}.ph
settings-options.php - Options used for the extension settings page. The framework picks them automatically and saves the values in then database. Use the fw_get_db_ext_settings_option() function
to get options values from the database. This file cant be overwritten from the theme, thats why it wasnt
placed in the options/ directory.
3.3. Extensions
49
/**
* @internal
*/
function _filter_plugin_awesome_extensions($locations) {
$locations[ dirname(__FILE__) . '/extensions' ]
=
plugin_dir_url( __FILE__ ) . 'extensions';
return $locations;
}
add_filter('fw_extensions_locations', '_filter_plugin_awesome_extensions');
Child Extensions
Child Extensions are used to split a big extension into sub-extensions to separate the functionalities or when some
extensions are tightly connected to the parent extension and cant exist without it, so they will be loaded only if the
parent extension exists, is loaded and activated.
A child extension can be located in any load location but must be on the same relative path. Here are some examples
where an extension can exists and where its child extensions can be placed:
1. If the hello extension is located in framework, the child extensions can be placed in: framework, parent theme
and child theme.
framework/
-extensions/
-hello/
-extensions/
-hello-child/
-...
-parent-theme/
| -framework-customizations/
|
-extensions/
|
-hello/
|
-extensions/
|
-hello-child/
|
-...
-child-theme/
-framework-customizations/
-extensions/
-hello/
-extensions/
-hello-child/
-...
2. If the hello extension is located in parent theme, the child extensions can be placed in: parent theme and child
theme.
-parent-theme/
| -framework-customizations/
|
-extensions/
|
-hello/
|
-extensions/
|
-hello-child/
|
-...
-child-theme/
-framework-customizations/
50
Chapter 3. License
-extensions/
-hello/
-extensions/
-hello-child/
-...
3. If the hello extension is located in child theme, the child extensions can be placed only in the child theme.
-child-theme/
-framework-customizations/
-extensions/
-hello/
-extensions/
-hello-child/
-...
2. To make the extension visible on the Extensions list page (by default it is hidden) set the manifest display
parameter to true.
3. Make sure you understand what the manifest standalone parameter means.
3.3.3 Cookbook
The Cookbook is a collection of specific recipes that explain how to correctly solve the most recurrent problems that
developers face in their day to day work.
Disable Child Extensions
Validate Child Extensions
3.3. Extensions
51
{
/**
* @internal
*/
protected function _init()
{
// ...
if ($this->something_is_wrong()) {
return false; // prevent child extensions activation
}
}
}
3.4 Components
3.4.1 Introduction
The Unyson framework core has three components:
Theme
Backend
Extensions
Accessing one of the cores component is done in this way:
fw()->{$component}->{$method}()
fw() returns the framework object, this being the only way to access the framework core.
52
Chapter 3. License
3.4.2 Theme
The Theme component makes the connection between the theme and the framework. The working directory is
framework-customizations/theme/ within child and parent themes.
get_options($name)
return
options
array
from
framework-customizations/theme/options/{$name}.php.
specified
option
file
$custom_options = fw()->theme->get_options('custom');
get_taxonomy_options($taxonomy)
return
options
array
framework-customizations/theme/options/taxonomies/{$post_type}.php.
from
$category_options = fw()->theme->get_taxonomy_options('category');
locate_path($rel_path) - search full path of the file by a given relative path. Will search in the child
theme then in the parent theme.
echo fw()->theme->locate_path('/custom.php');
// prints '/.../wp-content/themes/scratch-theme/framework-customizations/theme/custom.php'
3.4.3 Backend
Admin side functionality:
option_type($type) - get instance of a registered option type.
$option_type_text = fw()->backend->option_type('text');
echo $option_type_text->render('demo', array( 'value' => 'Demo Value' ));
render_option($id, $option, $data = array(), $design = default) - render option html together with label, desc and help.
Attention: Does not accept container options.
// simple usage
echo fw()->backend->render_option('demo', array( 'type' => 'text' ));
// advanced usage
3.4. Components
53
echo fw()->backend->render_option(
'demo',
array(
'type' => 'text',
'label' => __('Demo Label', '{domain}'),
'desc' => __('Demo Description', '{domain}'),
'html' => __('Demo Help Tip', '{domain}'),
'value' => 'default value',
),
array(
'id_prefix'
=> 'custom-id-prefix-',
'name_prefix' => 'custom_name_prefix',
'value'
=> 'overwrite default value'
),
'taxonomy'
);
54
Chapter 3. License
3.4.4 Extensions
The core of Extensions.
get($extension_name) - get instance of an existing active extension.
echo fw()->extensions->get('extension_name')->get_name();
3.5 Helpers
3.5.1 Introduction
Helpers are classes and functions with useful functionality. Here are built-in helpers that you can use:
PHP Helpers
JavaScript Helpers
CSS Helpers
3.5. Helpers
55
General
General PHP helpers:
fw_print($value) - styled version of print_r().
fw_html_tag($tag, array $attr, $end = null) - generate html tag.
echo fw_html_tag('script', array('src' => '/demo.js'), true);
// <script src="/demo.js"></script>
represents
array(
'a' => array(
'b' => array(
'c' => null
)
)
)
$demo = array(
'a' => array(
'b' => 'hello'
)
);
echo fw_akg('a/b', $demo);
// 'hello'
56
Chapter 3. License
fw_stripslashes_deep_keys($value) - strip slashes (recursive) from values and keys (if value is
array) if magic_quotes_gpc = On.
fw_addslashes_deep_keys($value) - add slashes (recursive) to values and keys (if value is array) if
magic_quotes_gpc = On.
fw_current_screen_match($rules) - check if current global $current_screen; (available
in admin side) matches the given rules. Used to detect on which admin page you currently are. Thus you can
for example enqueue a script only on a target page, not on all admin pages.
/**
* @internal
*/
function _action_enqueue_demo_admin_scripts() {
// To find out what is the current screen of the current page, uncomment next line
//global $current_screen; fw_print($current_screen);
$only = array(
'only' => array(
array( 'id' => 'dashboard' )
)
3.5. Helpers
57
);
if (fw_current_screen_match($only)) {
// enqueue this script only on dashboard page
wp_enqueue_script(
'demo-dashboard',
get_template_directory_uri() .'/js/demo-only.js'
);
}
$exclude = array(
'exclude' => array(
array( 'id' => 'dashboard' ),
array( 'post_type' => 'post' )
)
);
if (fw_current_screen_match($exclude)) {
// enqueue this script on all admin pages
// except dashboard page and all pages from posts menu (add, edit, categories, tags)
wp_enqueue_script(
'demo-dashboard',
get_template_directory_uri() .'/js/demo-excluded.js'
);
}
}
add_action('admin_enqueue_scripts', '_action_enqueue_demo_admin_scripts');
Note: You can combine only and exclude in the same rules array.
fw_locate_theme_path_uri($rel_path) - search by relative path, in child then in parent theme
directory, and return URI.
echo fw_locate_theme_path_uri('/styles.css');
// http://your-site.com/wp-content/themes/child-theme/style.css
58
Chapter 3. License
echo $message;
echo $private;
*/
// Hello
// Notice: Undefined variable: private
3.5. Helpers
59
fw_htmlspecialchars($string) - UTF-8 version of phps htmlspecialchars(). Just a shorthand not to write two more parameters for default htmlspecialchars() every time.
Note: In php 5.2 htmlspecialchars() default encoding is not UTF-8.
fw_human_time($seconds) - convert seconds to human readable time.
echo fw_human_time(12345);
// '3 hours'
60
Chapter 3. License
array(
'type' => 'box',
'options' => array(
array(
'type' => 'tab',
'options' => array(
'demo-2' => array(
'type' => 'textarea'
)
)
)
)
)
);
print_r( fw_extract_only_options($options) );
/*
array(
'demo-1' => array(
'type' => 'text'
),
'demo-2' => array(
'type' => 'textarea'
)
)
*/
fw_get_options_values_from_input(array $options, $input_array = null) - extract options values from input array. If no input array is provided, values from $_POST will be used.
$options = array(
'demo-1' => array( 'type' => 'text', 'value' => 'default value 1' ),
'demo-2' => array( 'type' => 'text', 'value' => 'default value 2' ),
);
$input_values = array(
'demo-1' => 'input value 1',
'demo-3' => 'input value 3',
);
$values = fw_get_options_values_from_input($options, $input_values);
print_r($values);
/*
array(
'demo-1' => 'input value 1',
'demo-2' => 'default value 2',
)
*/
61
WordPress default behavior. This function is already used in core so you dont have to bother about
passing options values through it each time. Use it if you will do something custom and strings will
not be translated.
Database
fw_get_db_settings_option($option_id, $default_value = null) - get value from
the database of an option from the theme settings page.
Settings options are located in
framework-customizations/theme/options/settings.php.
fw_set_db_settings_option($option_id, $value) - set a value in the database for an option
from the theme settings page.
are
located
in
62
Chapter 3. License
FW_Cache
Use cache to store frequently accessed data. Cache is just a big array and has one useful feature: it will automatically
unset array keys if the php memory is close to full. So it is safe to store in it as much data as you want (of course the
maximum allowed by php, by default is ~100Mb).
function get_foo_bar() {
$cache_key = 'foo/bar';
try {
/**
* This will throw an exception if the key was not found
*/
return FW_Cache::get($cache_key);
} catch (FW_Cache_Not_Found_Exception $e) {
$data = _generate_foo_bar_data();
FW_Cache::set($cache_key, $data);
return $data;
}
}
because FW_Cache::set(...) can fail or the data that was set can be removed after automatically memory
free.
FW_Form
A convenient way to create forms. You can create a form class instance and give it three callbacks that control the
render, validate and save process.
$my_form = new
'render'
'validate'
'save'
));
FW_Form('<unique-id>', array(
=> '_my_form_render',
=> '_my_form_validate',
=> '_my_form_save',
function _my_form_render() {
$input_value = FW_Request::POST('demo');
echo '<input type="text" name="demo" maxlength="10" value="'. esc_attr($input_value) .'">';
}
function _my_form_validate($errors) {
$input_value = FW_Request::POST('demo');
if (fw_strlen($input_value) > 10) {
$errors['demo'] = __('Value cannot be more that 10 characters long', '{domain}');
3.5. Helpers
63
}
return $errors;
}
function _my_form_save() {
$input_value = FW_Request::POST('demo');
// do something with value
}
echo $my_form->render();
// this will output:
// <form ... ><input type="text" name="demo" maxlength="10" value=""></form>
Customize errors By default the errors are displayed right before the <form> tag. You can display the errors in
your own way and cancel the default display. Before the errors are displayed, an action is fired so you can use it:
/**
* @param FW_Form $form
* @internal
*/
function _action_theme_fw_form_errors_display($form) {
/**
* Once the errors was accessed/requested
* the form will cancel/abort the default errors display
*/
$errors = $form->get_errors();
echo '<ul class="your-custom-errors-class">';
foreach ($errors as $input_name => $error_message) {
echo fw_html_tag(
'li',
array('data-input-name' => $input_name),
$error_message
);
}
echo '</ul>';
}
add_action('fw_form_display_errors_frontend', '_action_theme_fw_form_errors_display');
Ajax submit You can use this script to make FW_Form ajax submittable.
Enqueue the script in frontend:
// file: {theme}/inc/static.php
// https://github.com/ThemeFuse/Theme-Includes
if (!is_admin()) {
wp_enqueue_script(
'fw-form-helpers',
fw_get_framework_directory_uri('/static/js/fw-form-helpers.js')
);
wp_localize_script('fw-form-helpers', 'fwAjaxUrl', admin_url( 'admin-ajax.php', 'relative' ));
}
64
Chapter 3. License
jQuery(function(){
fwForm.initAjaxSubmit({
//selector: 'form[some-custom-attribute].or-some-class'
// Open the script code and check the `opts` variable
// to see all options that you can overwrite/customize.
});
});
FW_Settings_Form
3.5. Helpers
65
66
Chapter 3. License
FW_Flash_Messages
You can display small messages that will be stored on the users session for exactly one additional request. This is
useful when processing a form: you want to redirect and have a special message shown on the next page. These types
of messages are called flash messages.
Adding a flash message
FW_Flash_Messages::add(
'unique-id',
__('Test message', '{domain}'),
'success' // available types: info, warning, error, success
);
General
Options Modal
Confirmation
Queueing confirms
Same confirm multiple times
Events
General
General javaScript helpers:
fw.FW_URI - URI to the framework directory.
fw.SITE_URI - URI to the site root directory.
fw.intval(value) - alternative to php intval(). Returns 0 on failure, instead of NaN like parseInt() does.
fw.md5(string) - calculate md5 hash of the string.
fw.loading - show loading on the page.
3.5. Helpers
67
Tip: Useful when doing AJAX requests and want to inform your users about that.
fw.loading.show();
setTimeout(function(){
fw.loading.hide();
}, 3000);
The show() and hide() methods can be called multiple times. If show() is called 10 times, then
hide() should be called 10 times for loading to disappear. This is done for cases when this helper
is used by multiple asynchronous scripts, the loading should not disappear until all scripts complete
the work.
fw.capitalizeFirstLetter(text) - capitalizes the first letter of a string.
fw.ops(properties, value, obj, delimiter) - same as fw_aks(...) from PHP Helpers,
but for javascript objects.
var obj = {foo: {}};
fw.ops('foo/bar', 'demo', obj);
console.log(obj); // {foo: {bar: 'demo'}}
68
Chapter 3. License
Note: Make sure to enqueue scripts and styles for the options you use in modal. Usually it is done before page is
displayed.
fw()->backend->enqueue_options_static($modal_options);
Confirmation
General purpose confirmation mechanism that operates with jQuery.Deferred.
var confirm = fw.soleConfirm.create({
severity: 'info', // warning | info
message: 'Some message to display', // or null, if you don't want any
backdrop: null // null | false | true
});
confirm.result; // Instance of jQuery.Deferred factory
confirm.result.then(function (confirm_instance) {
// confirm_instance is same as confirm
// Handle success branch
});
confirm.result.fail(function (confirm_instance) {
// Handle fail branch
});
confirm.show();
Queueing confirms
Confirm is actually using fw.soleModal under the hood, which is queued one after the other.
var confirm1 = fw.soleConfirm.create();
var confirm2 = fw.soleConfirm.create();
confirm1.show();
confirm2.show();
confirm1.hide(); // That's when the confirm2 will actually pop in, results are buffered
3.5. Helpers
69
Because of the way jQuery.Deferred works, one single confirm instance will resolve its promise exactly one
time. If you really need to use the same confirm once again - just reset it.
var confirm = fw.soleConfirm.create();
confirm.result.then(function () {
// handle success
// will be triggered just once
});
confirm.show();
// ...
// after the user takes his choice
// ...
confirm.show(); // will throw an error!
confirm.reset();
// you'll have to attach your listeners once again, the old one
// will already not be around
confirm.result.then(function () {
// one more handler
});
confirm.show(); // will work just fine
Events
fwEvents is a global object on which you can trigger or listen custom events. This way different scripts can communicate with each other.
// script-1.js
fwEvents.on('script-2:message', function(data){
console.log('script-1 received a message from script-2: '+ data.message);
});
// script-2.js
fwEvents.trigger('script-2:message', {message: 'Hello World!'});
70
Chapter 3. License
General
Alignment classes
Transformation classes
Responsive images
Delete icon
Quick floats
Center content blocks
Clearfix
Showing and hiding content
Image replacement
Grid system
Media queries
Columns
Responsive utilities
Available classes
General
Alignment classes
Transformation classes
Responsive images
Images can be made responsive-friendly via the addition of the .fw-img-responsive class. This applies
max-width: 100%; and height: auto; to the image so that it scales nicely to the parent element.
<img
src="..."
class="fw-img-responsive"
Delete icon
Use the generic delete icon for links that delete something.
<a href="#" class="dashicons fw-x"></a>
3.5. Helpers
71
Quick floats
Float an element to the left or right with a class. !important is included to avoid specificity issues. Classes can
also be used as mixins.
<div class="fw-pull-left">...</div>
<div class="fw-pull-right">...</div>
<div class="fw-center-block">...</div>
Clearfix
Easily clear floats by adding .fw-clearfix to the parent element. Utilizes the micro clearfix as popularized by
Nicolas Gallagher. Can also be used as a mixin.
<div class="fw-clearfix">...</div>
Force an element to be shown or hidden. These classes use !important to avoid specificity conflicts, just like
the quick floats. They are only available for block level toggling. They can also be used as mixins. Furthermore,
.fw-invisible can be used to toggle only the visibility of an element, meaning its display is not modified and the
element can still affect the flow of the document.
<div class="fw-show">...</div>
<div class="fw-hidden">...</div>
Image replacement
Utilize the .fw-text-hide class or mixin to help replace an elements text content with a background image.
<h1 class="fw-text-hide">Custom heading</h1>
Grid system
Css helpers includes a responsive, fluid grid system that appropriately scales up to 12 columns as the device or viewport
size increases. Grid systems are used for creating layouts through a series of rows and columns that house your content.
Heres how the grid system works:
Use rows to create horizontal groups of columns.
Content should be placed within columns, and only columns may be immediate children of rows.
Predefined grid classes like .fw-row and .fw-col-xs-4 are available for quickly making grid layouts.
Grid columns are created by specifying the number of twelve available columns you wish to span. For example,
three equal columns would use three .fw-col-xs-4.
72
Chapter 3. License
If more than 12 columns are placed within a single row, each group of extra columns will, as one unit, wrap onto
a new line.
Grid classes apply to devices with screen widths greater than or equal to the breakpoint sizes, and override grid
classes targeted at smaller devices. Therefore, applying any .fw-col-md- class to an element will not only
affect its styling on medium devices but also on large devices if a .fw-col-lg- class is not present.
This grid system was inspired from bootstrap with some modifications:
Added .fw- prefix to classes
Changed media queries screen sizes
Rows are used without containers (no .container and .container-fluid)
Rows have no padding
Media queries
We use the following media queries to create the key breakpoints to a narrower set of devices.
/* Extra small devices (phones) */
@media (max-width: 782px) { ... }
/* Small devices (tablets) */
@media (min-width: 783px) and (max-width: 900px) { ... }
/* Medium devices (desktops) */
@media (min-width: 901px) and (max-width: 1199px) { ... }
/* Large devices (large desktops) */
@media (min-width: 1200px) { ... }
Columns
Using a set of .fw-col-* classes, you can create grid systems that look good on any device:
.fw-col-xs-* - extra small devices (phones).
.fw-col-sm-* - small devices (tablets)
.fw-col-md-* - medium devices (desktops)
.fw-col-lg-* - large devices (large desktops)
Tip: More details about grid system and examples can be found here.
Responsive utilities
For faster mobile-friendly development, use these utility classes for showing and hiding content by device via media
query.
Important: Try to use these on a limited basis and avoid creating entirely different versions of the same site. Instead,
use them to complement each devices presentation.
3.5. Helpers
73
Available classes
Use a single or combination of the available classes for toggling content across viewport breakpoints.
.visible-xs-*
.visible-sm-*
.visible-md-*
.visible-lg-*
.hidden-xs
.hidden-sm
.hidden-md
.hidden-lg
Small devices
(783px)
Hidden
Visible
Hidden
Hidden
Visible
Hidden
Visible
Visible
Medium devices
(901px)
Hidden
Hidden
Visible
Hidden
Visible
Visible
Hidden
Visible
Large devices
(1200px)
Hidden
Hidden
Hidden
Visible
Visible
Visible
Visible
Hidden
The .visible-*-* classes for each breakpoint come in three variations, one for each CSS display property value
listed below.
Group of classes
.visible-*-block
.visible-*-inline
.visible-*-inline-block
CSS display
display: block;
display: inline;
display: inline-block;
So, for extra small (xs) screens for example, the available .visible-*-* classes are: .visible-xs-block,
.visible-xs-inline, and .visible-xs-inline-block.
3.6 Manifest
3.6.1 Introduction
The Framework, Theme and every Extension has a manifest. The manifest provides important information like: title,
version, dependencies, etc.
The Framework generates a manifest for it self, the theme and every extension with default values automatically. You
can overwrite the default values by creating a manifest.php file in the root folder of the theme or extension and
define the $manifest array.
3.6.2 Framework
The frameworks manifest is located in framework/manifest.php and can be accessed like this:
fw()->manifest->get('version');
74
=
=
=
=
=
=
__('Framework', 'fw');
'http://themefuse.com/framework';
__('WordPress Framework', 'fw');
'1.0';
'ThemeFuse';
'http://themefuse.com/';
Chapter 3. License
$manifest['requirements'] = array(
'wordpress' => array(
'min_version' => '4.0',
/*'max_version' => '4.99.9'*/
),
);
3.6.3 Theme
The themes manifest is located in framework-customizations/theme/manifest.php and can be accessed like this:
fw()->theme->manifest->get('version');
3.6. Manifest
75
3.6.4 Extension
The extensions manifest is located in {extension-name}/manifest.php and can be accessed like this:
fw()->extensions->get('extension-name')->manifest->get('version');
$manifest['name']
= __('Extension Title', '{domain}');
$manifest['uri']
= 'http://extension-homepage.com/';
$manifest['description'] = __('Another awesome framework extension', '{domain}');
$manifest['version']
= '1.0';
$manifest['author']
= 'ThemeFuse';
$manifest['author_uri']
= 'http://themefuse.com/';
$manifest['requirements'] = array(
'wordpress' => array(
'min_version' => '4.0',
/*'max_version' => '4.99.9'*/
),
'framework' => array(
/*'min_version' => '1.0.0',
'max_version' => '1.99.9'*/
),
'extensions' => array(
/*'extension_name' => array(),*/
/*'extension_name' => array(
'min_version' => '1.0.0',
'max_version' => '2.99.9'
),*/
)
);
/**
* @type bool Display on the Extensions page or it's a hidden extension
*/
$manifest['display'] = false;
/**
* @type bool If extension can exist alone
* false - There is no sense for it to exist alone, it exists only when is required by some other ext
76
Chapter 3. License
3.7.2 Shortcodes
The shortcodes extension makes possible the easy creation of WordPress Shortcodes and their optional integration
with the frameworks page builder.
77
Built-in shortcodes
Overwriting shortcodes
Disabling shortcodes
Creating a new shortcode
Directory structure
Config File
Builder icon
Options file
Default view file
Static file
Class file
Cookbook
Creating a simple shortcode
Creating a shortcode with options
Creating an advanced shortcode with a custom class
Enqueue shortcode dynamic css in page head
Built-in shortcodes
Unyson comes with a set of built-in shortcodes like accordion, button, map, testimonials and others.
All shortcodes are located in {some-extension}/shortcodes/ but the vast majority of them are located in
the shortcodes extension (framework/extensions/shortcodes/shortcodes). They can be modified by
overwriting or disabled
Overwriting shortcodes
Some shortcode files can be overwritten (meaning that the files can be swapped). This permits shortcode customization. The files that can be overwritten are config file, options file, static file and view file.
There are three places where the shortcode files are searched until found: child theme (if active), parent theme and
framework.
If the shortcode is built-in (declared in the framework) the files will be first looked up in the child theme (if
active), after that in the parent theme, and in the end the framework will search in the shortcodes declared path
If the shortcode is declared in the parent theme the files will be first searched in the child theme (if active) and
then in the parent theme (where it was declared)
If the shortcode is declared in the child theme the files will be searched only at its declared path
For a better understanding lets look at an example: Imagine that there is a shortcode demo located in the shortcodes
extension (framework/extensions/shortcodes/shortcodes/demo). When the framework loads its
files (options.php for this example) it will follow these simple steps:
2. If
it
did
not
find
the
file
in
the
child
theme
it
will
search
in
{your-parent-theme}/framework-customizations/extensions/shortcodes/shortcodes/demo/opti
3. If it did not find the file in the parent theme it will search at the shortcodes declared path
framework/extensions/shortcodes/shortcodes/demo/options.php
78
Chapter 3. License
Disabling shortcodes
A
shortcode
can
be
disabled
via
the
fw_ext_shortcodes_disable_shortcodes
filter.
A
good
place
to
put
the
code
for
the
disabling
would
be
in
{your-theme}/framework-customizations/extensions/shortcodes/hooks.php. It should
look something like the following:
<?php if (!defined('FW')) die('Forbidden');
function _filter_theme_disable_default_shortcodes($to_disable) {
$to_disable[] = 'accordion';
$to_disable[] = 'button';
return $to_disable;
}
add_filter('fw_ext_shortcodes_disable_shortcodes', '_filter_theme_disable_default_shortcodes');
Attention: The directory name of the shortcode folder will become its tag, hyphens will be replaced with
underscores. This means that if you name the shortcode demo-shortcode it will be transformed into
[demo_shortcode].
79
Config File
The shortcode configuration is a file named config.php placed inside the root directory of the shortcode. It contains
an array that must be stored in a $cfg variable and is typically used to provide configurations for the visual page
builder.
$cfg = array(
'page_builder' => array(
'title'
=> __('Demo
'description'
=> __('Demo
'tab'
=> __('Demo
'popup_size'
=> 'small',
Shortcode', '{domain}'),
shortcode description', '{domain}'),
Elements', '{domain}'),
// can be large, medium or small
/*
// Icon examples
// Note: By default is loaded {your-shortcode}/static/img/page_builder.png
'icon' => 'http://.../image-16x16.png', // background color should be #8c8c8c
'icon' => 'dashicons dashicons-admin-site',
'icon' => 'unycon unycon-crown',
'icon' => 'fa fa-btc',
*/
/*
// Title Template examples
//
// Syntax:
// * {{- variable }} - Output with html escape
// * {{= variable }} - Output raw (without html escape)
// * {{ if (execute.any(javascript, code)) { console.log('Hi'); } }}
//
// Available variables:
// * title - shortcode title (from config)
// * o - an object with all shortcode options values
'title_template' => '{{- title }} Lorem {{- o.option_id }} ipsum {{= o["option-id"] }}',
'title_template' => '{{- title }}: {{- o.label }}{{ if (o.target == "_blank") { }} <span clas
*/
)
);
For the shortcode to appear in the page builder the config array contains a special page_builder key that holds an
array with the following data:
title - the title that will appear in the shortcode box.
80
Chapter 3. License
description - the text that will be shown in a tooltip when hovering the shortcode box.
tab - the builder tab in which the shortcode box will appear.
81
popup_size - the size of the popup in which the shortcode options will be displayed.
Allowed values are large | medium | small. This parameter is optional and the default is
set to small.
Builder icon
To set an icon for the shortcode box, put an image named page_builder.png
{your-shortcode}/static/img/ directory. The image should have the size of 16x16 px.
82
inside
Chapter 3. License
Options file
The shortcode directory can contain a file named options.php with correctly formed options:
$options = array(
'demo_text'
'label'
'desc'
'help'
'type'
),
'demo_select'
'label'
'desc'
'type'
'choices'
'c1'
'c2'
'c3'
),
'value'
)
);
=>
=>
=>
=>
=>
array(
__('Demo text label', '{domain}'),
__('Demo text description', '{domain}'),
__('Demo text help', '{domain}'),
'text'
=>
=>
=>
=>
=>
=>
=>
=>
array(
__('Demo select label', '{domain}'),
__('Demo select description', '{domain}'),
'select',
array(
__('Choice 1', '{domain}'),
__('Choice 2', '{domain}'),
__('Choice 3', '{domain}')
=> 'c2'
If it does, then it will have an icon when dragged into the builders canvas area, indicating that the shortcode can be
edited:
83
When clicking either the edit icon or the shortcode itself, a modal window will open containing the declared options:
The saved options values will be passed into the view file.
Default view file
By default, when WordPress wants to render a shortcode built into the framework, it will serve the html from the
default view file located in {your-shortcode}/views/view.php. 3 variables are passes into the view file :
$atts, $content and $tag.
Tip: More information can be found in the cookbook section.
84
Chapter 3. License
Static file
A shortcode can have a static.php file that is included when the shortcode is rendered. It is meant for enqueuing
static files. Here is an example of a basic static.php file:
<?php if (!defined('FW')) die('Forbidden');
If you want to include custom styles and scripts for a existing shortcode, overwrite the static.php file by creating
framework-customizations/extensions/shortcodes/shortcodes/demo-shortcode/static.php.
Attention: All of the above is valid only in the case that the _render method from the class file was not
overwritten.
Class file
When creating a shortcode folder with all the required files, the framework makes an instance of FW_Shortcode to
ensure the correct default functionality, some of which default functionality can be overwritten by creating a class in
the shortcode directory that extends FW_Shortcode.
Note:
The
class
file
must
respect
the
following
class-fw-shortcode-{your-shortcode-folder-name}.php.
naming
convention:
The
class
inside
the
class
file
must
respect
FW_Shortcode_{Your_Shortcode_Folder_Name}.
naming
convention:
the
following
Note: The framework replaces hyphens with underscores when registering the shortcode, so your-shortcode
will be transformed to [your_shortcode].
So in order to create a class for the [demo_shortcode] shortcode, we need to create a file
demo-shortcode/class-fw-shortcode-demo-shortcode.php and within the file create a class that
extends FW_Shortcode:
class FW_Shortcode_Demo_Shortcode extends FW_Shortcode
{
// ...
}
85
get_declared_path($rel_path = ) - returns the path to where the shortcode folder was declared.
get_declared_URI($rel_path = ) - returns the uri to where shortcode folder was declared.
locate_path($rel_path = ) - searches a rel path given as an argument first in child theme then in
parent theme and last in framework. Returns the found path or false if not found. See overwriting for more
details.
locate_URI($rel_path = ) - does the same as locate_path with uris.
get_config($key = null) - returns the shortcodes whole overwritten config array, or just a particular
key of its given as an argument.
get_options() - returns the shortcodes overwritten options array, if there is any.
The methods that are most prone to be overwritten are:
_init() - is called when the FW_Shortcode instance for the shortcode is created. Useful for loading other
php files (custom option types, libraries, etc.).
_render($atts, $content, $tag) - returns the html that will be displayed when the shortcode will
be executed by WordPress. Useful for changing the default behavior with a custom one.
Tip: More information about this can be found in the cookbook section.
Cookbook
Creating a simple shortcode
This example will go through creating the [hr] (horizontal ruler) shortcode in a few simple steps:
1. Create a hr folder in framework-customizations/extensions/shortcodes/shortcodes/.
2. Create a config file inside that folder:
<?php if (!defined('FW')) die('Forbidden');
$cfg = array(
'page_builder' =>
'title'
'description'
'tab'
)
);
array(
=> __('Horizontal Ruler', '{domain}'),
=> __('Creates a \'hr\' html tag', '{domain}'),
=> __('Demo Elements', '{domain}'),
Note: At this point the shortcode should appear in the Demo Elements tab of the layout builder as
shown below:
86
Chapter 3. License
The [hr] shorcode is completed! The directory structure of the shortcode is as shown below:
framework-customizations/
-extensions/
-shortcodes/
-shortcodes/
-hr/
-config.php
-views/
-view.php
$cfg = array(
'page_builder' => array(
'title'
=> __('Button', '{domain}'),
'description'
=> __('Creates a button with choosable label, size and style', '{dom
'tab'
=> __('Demo Elements', '{domain}'),
)
);
Note: At this point the shortcode should appear in the Demo Elements tab of the layout builder as
shown below:
87
Now, when clicking the shortcode inside the canvas area of the layout builder a pop-up window
containting the options will appear:
88
Chapter 3. License
4. Create a views folder and the view file inside it. Make use of the $atts variable that is avaialble inside the
view, it contains all the options values that the user has selected in the pop-up:
<?php if (!defined('FW')) die('Forbidden'); ?>
<button class="button button-<?php echo $atts['size']; ?> button-<?php echo $atts['style'];
<?php echo $atts['label']; ?>
</button>
Tip: For more information about the view variables check out the default view section.
The [button] shorcode is completed! The directory structure of the shortcode is as shown below:
framework-customizations/
-theme/
-shortcodes/
-button/
-config.php
-options.php
-views/
-view.php
This ex will go through creating a [table_builder] shortcode, it will make use of its own custom option type:
1. Create a table-builder folder in framework-customizations/extensions/shortcodes/shortcodes/.
2. Create a config file inside that folder:
<?php if (!defined('FW')) die('Forbidden');
$cfg = array(
89
'page_builder' =>
'title'
'description'
'tab'
'popup_size'
)
array(
=> __('Table Builder', '{domain}'),
=> __('Creates custom tables', '{domain}'),
=> __('Demo Elements', '{domain}'),
=> 'large'
);
Note: At this point the shortcode should appear in the Demo Elements tab of the layout builder as
shown below:
90
Chapter 3. License
Note: At this point, when clicking the shortcode inside the canvas area of the layout builder a pop-up
window containting the options will appear:
4. Create the view file and make use of the custom option types value.
The [table_builder] shorcode is completed! The directory structure of the shortcode is as shown below:
framework-customizations/
-theme/
-shortcodes/
-table-builder/
-class-fw-shortcode-table-builder.php
-config.php
-options.php
-views/
| -view.php
-includes/
-fw-option-type-table-builder/
-class-fw-option-type-table-builder.php
-static/
-views/
91
When the shortcode has options that affect its css, you can populate the style="..." attribute in view.php:
// file:: {theme}/framework-customizations/extensions/shortcodes/shortcodes/{name}/views/view.php
<p style="color: <?php echo esc_attr($atts['color']) ?>;" >Hello, World!</p>
A better solution would be to assign shortcode an unique id and enqueue in head css for that id.
1. Add a hidden option that will generate an unique id
// file: {theme}/framework-customizations/extensions/shortcodes/shortcodes/{name}/options.ph
$options = array(
'id'
=> array( 'type' => 'unique' ),
'color' => array( 'type' => 'color-picker' ),
...
);
// file: {theme}/framework-customizations/extensions/shortcodes/shortcodes/{name}/views/view
<p id="shortcode-<?php echo esc_attr($atts['id']); ?>" >Hello, World!</p>
// file: {theme}/framework-customizations/extensions/shortcodes/shortcodes/{name}/static.php
wp_enqueue_style(
'theme-shortcode-{name}',
fw_ext('shortcodes')->locate_URI('/shortcodes/{name}/static/css/styles.css')
);
// file: {theme}/framework-customizations/extensions/shortcodes/shortcodes/{name}/static.php
...
if (!function_exists('_action_theme_shortcode_{name}_enqueue_dynamic_css')):
/**
* @internal
* @param array $data
*/
function _action_theme_shortcode_{name}_enqueue_dynamic_css($data) {
$shortcode = '{name}';
$atts = shortcode_parse_atts( $data['atts_string'] );
$atts = fw_ext_shortcodes_decode_attr($atts, $shortcode, $data['post']->ID);
wp_add_inline_style(
'theme-shortcode-'. $shortcode,
'#shortcode-'. $atts['id'] .' { '.
'color: '. $atts['color'] .';'.
' } '
);
}
add_action(
92
Chapter 3. License
'fw_ext_shortcodes_enqueue_static:{name}',
'_action_theme_shortcode_{name}_enqueue_dynamic_css'
);
endif;
3.7.3 Slider
Adds a sliders module to your website from where youll be able to create different built in jQuery sliders for your
homepage and rest of the pages.
Directory Structure
Create a simple slider type
Configuration
Static files
Options
Template
Create advanced slider type
Frontend render
Directory Structure
The slider extension directory has the following structure:
slider/
-...
-extensions/
-{slider-type}/
-...
-{slider-type}/
-class-fw-extension-{slider-type}.php # optional
-config.php
-options/ # optional
| -options.php
| -...
-static/ # optional
| -css/
| | -auto-enqueued-style.css
| | -...
| -img/
| | -preview.jpg
| | -thumb.jpg
| | -...
| -js/
|
-auto-enqueued-script.js
|
-...
-views/
-{slider-type}.php
-...
93
Configuration
Static files
94
Chapter 3. License
Optionally, if your slider have extra options, you can create 2 types of option files within options/ directory:
options.php - extra options shown after default options on add and edit slider page.
{population-method}.php - extra options for concrete population method, shown after default options
on edit slider page.
Template
View the file that contains the slider template for frontend, is located in views/{slider-type}.php. Here is an
example for our demo-slider:
<?php if (!defined('FW')) die('Forbidden');
/**
* @var array $data
*/
The $data variable that is available in view, has the following structure:
$data = array(
'slides' => array(
array(
'title' => 'Slide Title',
'multimedia_type' => 'video|image',
'src'
=> 'Slide src',
'extra' => array(
95
/**
* This array can be empty, it depends on population method
* or if user set extra options for population method
*/
'extra-slide-key' => 'Extra slide value',
...
)
),
...
),
'settings' => array(
'title'
=> 'Slider Title',
'slider_type'
=> '{slider-type}',
'population_method' => 'posts|categories|tags|custom',
'post_id'
=> 10, // ID of the slider (slider is a custom post)
'extra' => array(
/**
* This array can be empty.
* Or will have something in it
* if user set custom options for slider in options/options.php
*/
'extra-slider-key' => 'Extra slider values',
...
)
)
);
In this case the slider type is demo-slider, so the class file will be located in
framework-customizations/extensions/media/extensions/slider/extensions/bx-slider/class-fw-e
and will contain:
<?php if (!defined('FW')) die('Forbidden');
class FW_Extension_Demo_Slider extends FW_Slider {
/** @internal */
public function _init() {}
}
Then you can take a look at the FW_Slider methods to learn what are they doing and decide which one you will
overwrite.
Frontend render
There are two ways you can display a slider in frontend:
1. Builder shortcode - the main slider extension automatically creates a [slider] shortcode which is available
in builder in the Media Elements tab.
2. Render from code - the slider extension has a public method that you can use to render a slider on frontend.
fw()->extensions->get('slider')->render_slider($slider_post_id, array(
'width' => 300,
96
Chapter 3. License
Overview
HTML/CSS
Markup Example
Change Item/Icon Markup
Overwrite the Walker
Item Custom Options
Important: This extensions is not be visible by default in Unyson Extensions page. To make it appear in that list,
you have to:
Add the extension name in theme manifest
$manifest['supported_extensions'] = array(
'megamenu' => array(),
);
Overview
When it is turned on, it enriches menu with the following:
1. Ability to set an icon for any menu item
2. Ability to group several menu items into columns placed in rows
HTML/CSS
The extension adds the following css classes:
.menu-item-has-icon
.menu-item-has-mega-menu
.sub-menu-has-icons
.mega-menu
.mega-menu-row
.mega-menu-col
The markup will be the following:
97
li.menu-item-has-mega-menu
div.mega-menu
ul.mega-menu-row
li.mega-menu-col
li.mega-menu-col
li.mega-menu-col
ul.mega-menu-row
li.mega-menu-col
li.mega-menu-col
li.mega-menu-col
Note: All other standard WordPress classes and HTML remains the same.
Markup Example
<ul>
98
Chapter 3. License
WP_Post $item
string $title
array $attributes
object $args
int $depth
{
$icon_html = '';
if (
fw()->extensions->get('megamenu')->show_icon()
&&
($icon = fw_ext_mega_menu_get_meta($item, 'icon'))
) {
$icon_html = '<i class="'. $icon .'"></i> ';
}
}
99
// file:: {theme}/framework-customizations/extensions/megamenu/includes/class-fw-ext-mega-menu-custom
class FW_Ext_Mega_Menu_Custom_Walker extends FW_Ext_Mega_Menu_Walker
{
function start_lvl( &$output, $depth = 0, $args = array(), $class = 'sub-menu' ) {
fw_print('Hello');
return parent::start_lvl($output, $depth, $args, $class);
}
// other customizations ...
}
3.7.5 Sidebars
Brings another layer of customization freedom to your website by letting you add more than one sidebar to a page, or
different sidebars on different pages.
100
Chapter 3. License
Configuration
Helpers
Filters
Configuration
<?php if (!defined('FW')) die('Forbidden');
// file: framework-customizations/extensions/sidebars/config.php
$cfg = array(
'sidebar_positions' => array(
'position-id' => array(
/**
* Image from: framework-customizations/extensions/sidebars/images/
* (required)
*/
'icon_url' => 'picture.png',
/**
* Number of sidebars on page.
* The maximum number is 4.
* (optional)
* (default 0)
*/
'sidebars_number' => 0
),
// other positions ...
),
/**
* Array that will be passed to register_sidebar($args)
* Should be without 'id' and 'name'.
* Will be used for all dynamic sidebars.
*/
'dynamic_sidebar_args' => array(
'before_widget' => '<div id="%1$s" class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h3>',
'after_title'
=> '</h3>',
),
/**
* Render sidebar metabox in post types.
* By default is set to false.
* If you want to render sidebar in post types set it to true.
*/
'show_in_post_types' => false
);
Helpers
fw_ext_sidebars_show($color) - display sidebar in frontend. The parameter $color is the color of
the sidebar selected from the WordPress admin and can be: blue, yellow, green or red.
fw_ext_sidebars_get_current_position() - can be called in the frontend to find out current position name. It returns position-id from $cfg[sidebar_positions], or null.
101
Filters
fw_ext_sidebars_post_types - use this filter to change/remove post types that are used in extension.
/** @internal */
function _filter_remove_post_type_from_sidebars($post_types_list) {
unset($post_types_list['post_type_name']);
return $post_types_list;
}
add_filter('fw_ext_sidebars_get_post_types', '_filter_remove_post_type_from_sidebars' );
fw_ext_sidebars_taxonomies - use this filter to change/remove taxonomies that are used in extension.
/** @internal */
function _filter_remove_taxonomy_from_sidebars($taxonomy_list) {
unset($taxonomy_list['taxonomy_name']);
return $taxonomy_list;
}
add_filter('fw_ext_sidebars_get_taxonomies', '_filter_remove_taxonomy_from_sidebars');
/** @internal */
function _filter_fw_ext_sidebars_add_conditional_tag($conditional_tags) {
$conditional_tags['is_archive_page_slug'] = array(
'order_option' => 2, // (optional: default is 1) position in the 'Others' lists in b
'check_priority' => 'last', // (optional: default is last, can be changed to 'first'
'name' => __('Portfolio archive', '{domain}'), // conditional tag title
'conditional_tag' => array(
'callback' => 'is_post_type_archive', // existing callback
'params' => array('fw-portfolio') //parameters for callback
)
);
102
Chapter 3. License
return $conditional_tags;
}
add_filter('fw_ext_sidebars_conditional_tags', '_filter_fw_ext_sidebars_add_conditional_tag'
3.7.6 Portfolio
The Portfolio extension allows you to create Portfolio section on your site.
Configuration
Helpers
Hooks
Views
Configuration
In the config.php file, you can set the portfolio Gallery and Featured Image sizes.
$cfg['image_sizes'] = array(
'featured-image' => array(
'width' => 227,
'height' => 142,
'crop'
=> true
),
'gallery-image' => array(
'width' => 474,
'height' => 241,
'crop'
=> true
)
);
Also define if the portfolio custom post will support gallery or not.
$cfg['has-gallery'] = true;
Helpers
fw_ext_portfolio_get_gallery_images( $post_id ) - use this function to return all project
gallery images.
<?php if ( have_posts() ) : ?>
<?php while ( have_posts() ) : ?>
<?php $gallery = fw_ext_portfolio_get_gallery_images(); ?>
<ul class="gallery">
<?php foreach( $gallery as $image ) : ?>
<li>
<a href="<?php echo get_permalink($image['attachment_id'])?>">
<img src="<?php echo $image['url'] ?>" alt=""/>
</a>
</li>
<?php endforeach ?>
</ul>
<?php endwhile ?>
<?php endif ?>
103
Note: If you are in The Loop, the global $post will be used for $post_id
Hooks
fw_ext_portfolio_post_slug - portfolio custom post slug
/**
* @internal
*/
function _filter_custom_portfolio_post_slug($slug) {
return 'work';
}
add_filter('fw_ext_portfolio_post_slug', '_filter_custom_portfolio_post_slug');
104
Chapter 3. License
Views
Templates are located in the views/ directory. Here is the list of templates that you can customize:
single.php
Portfolio
course
single
post
template.
By
default
is
used
single.php from the theme root directory,
you can overwrite it by creating
framework-customizations/extensions/portfolio/views/single.php.
taxonomy.php
Portfolio
category
template.
By
default
is
used
taxonomy.php from the theme root directory,
you can overwrite it by creating
framework-customizations/extensions/portfolio/views/taxonomy.php.
content.php - Default portfolio single page template content.
It is loaded if the
framework-customizations/extensions/portfolio/views/single.php doesnt exist
and is used single.php from the theme root directory. The content of this view is rendered using worpdress
the_content filter, when the course single page is loaded.
105
4. Go to Tools > Demo Content Install menu in the WordPress admin. The demo(s) should be listed on that page.
Demos on remote server
1. Create Content Backup
2. Upload the zip on your server (in any directory you want, for e.g. your-site.com/demo/)
3. Upload this download script, lets say in the same directory your-site.com/demo/
4. In the same directory with the download script, create a config.php file and add your demos in the following
format:
//
'demo-id' => '/path/to/demo.zip',
'awesome-demo' => dirname(__FILE__) .'/awesome-demo.zip',
106
Chapter 3. License
}
add_filter('fw:ext:backups-demo:demos', '_filter_theme_fw_ext_backups_demos');
6. Go to Tools > Demo Content Install menu in the WordPress admin. The demo(s) should be listed on that page.
Hooks
Filter to exclude wp options on database export
107
10, 2
);
Filter to register a custom directory that contains theme demos (for e.g. a plugin bundled with theme)
function _filter_theme_fw_ext_backups_demo_dirs($dirs) {
$dirs['/path/to/dir-with-theme-demos']
= 'http://.../uri/to/dir-with-theme-demos';
return $dirs;
}
add_filter('fw_ext_backups_demo_dirs', '_filter_theme_fw_ext_backups_demo_dirs');
3.7.8 Forms
This extension adds the possibility to create a forms (for e.g. a contact form). Use the drag & drop form builder to
create any form youll ever want or need.
Customize Views
Contact Form Views
Create Form Builder Item Type
Customize Views
Form
Fields
Frontend
form
fields
views
can
be
customized
in
framework-customizations/extensions/forms/form-builder/items/{item-type}/views/view.php
All built-in form builder item types can be found in framework/extensions/forms/includes/option-types/form
directory.
For e.g.
to overwrite the view for item type text (which is located in
framework/extensions/forms/includes/option-types/form-builder/items/text)
create framework-customizations/extensions/forms/form-builder/items/text/views/view.php.
Form Fields Container - The view for the container that wraps the form fields can be customized in
framework-customizations/extensions/forms/form-builder/views/items.php.
Contact Form Views
Form
Content
The
inner
contents
of
the
<form>
can
be
customized
in
framework-customizations/extensions/forms/extensions/contact-forms/views/form.php.
Email Content - The contents of the email that is sent after an user submitted the contact form can be customized
in framework-customizations/extensions/forms/extensions/contact-forms/views/email.php.
Create Form Builder Item Type
First, make sure you understand how the base builder works.
The Forms extension have a built-in form-builder option type (that can be found in the
framework-customizations/extensions/forms/form-builder/ directory) which is used by
the Contact Forms sub-extension. To create an item type for form-builder you have to look in its method called
item_type_is_valid() to see what class you must extend in order to be accepted by the builder.
108
Chapter 3. License
class FW_Option_Type_Form_Builder
{
...
/**
* @param FW_Option_Type_Builder_Item $item_type_instance
* @return bool
*/
protected function item_type_is_valid($item_type_instance)
{
return is_subclass_of($item_type_instance, 'FW_Option_Type_Form_Builder_Item');
}
}
So you have to extend the FW_Option_Type_Form_Builder_Item class and register it as a builder item type.
Below is explained how to create a simple Yes/No question radio input.
Create the framework-customizations/extensions/forms/includes/builder-items/yes-no
directory.
Create framework-customizations/extensions/forms/includes/builder-items/yes-no/class-fw-optio
with the following contents:
class FW_Option_Type_Form_Builder_Item_Yes_No extends FW_Option_Type_Form_Builder_Item
{
/**
* The item type
* @return string
*/
public function get_type()
{
return 'yes-no';
}
/**
* The boxes that appear on top of the builder and can be dragged down or clicked to create items
* @return array
*/
public function get_thumbnails()
{
return array(
array(
'html' =>
'<div class="item-type-icon-title">'.
'
<div class="item-type-icon"><span class="dashicons dashicons-editor-help"></
'
<div class="item-type-title">'. __('Yes/No Question', 'unyson') .'</div>'.
'</div>',
)
);
}
/**
* Enqueue item type scripts and styles (in backend)
*/
public function enqueue_static()
{
$uri = fw_get_template_customizations_directory_uri('/extensions/forms/includes/builder-items
109
wp_enqueue_style(
'fw-form-builder-item-type-yes-no',
$uri .'/backend.css',
array(),
fw()->theme->manifest->get_version()
);
wp_enqueue_script(
'fw-form-builder-item-type-yes-no',
$uri .'/backend.js',
array('fw-events'),
fw()->theme->manifest->get_version(),
true
);
wp_localize_script(
'fw-form-builder-item-type-yes-no',
'fw_form_builder_item_type_yes_no',
array(
'l10n' => array(
'item_title'
=> __('Yes/No', 'unyson'),
'label'
=> __('Label', 'unyson'),
'toggle_required'
=> __('Toggle mandatory field', 'unyson'),
'edit'
=> __('Edit', 'unyson'),
'delete'
=> __('Delete', 'unyson'),
'edit_label'
=> __('Edit Label', 'unyson'),
'yes'
=> __('Yes', 'unyson'),
'no'
=> __('No', 'unyson'),
),
'options' => $this->get_options(),
'defaults' => array(
'type'
=> $this->get_type(),
'options' => fw_get_options_values_from_input($this->get_options(), array())
)
)
);
fw()->backend->enqueue_options_static($this->get_options());
}
/**
* Render item html for frontend form
*
* @param array $item Attributes from Backbone JSON
* @param null|string|array $input_value Value submitted by the user
* @return string HTML
*/
public function frontend_render(array $item, $input_value)
{
return '<pre>'. print_r($item, true) .'</pre>';
}
/**
* Validate item on frontend form submit
*
* @param array $item Attributes from Backbone JSON
* @param null|string|array $input_value Value submitted by the user
* @return null|string Error message
110
Chapter 3. License
*/
public function frontend_validate(array $item, $input_value)
{
return 'Test error message';
}
Create framework-customizations/extensions/forms/includes/builder-items/yes-no/static/backend
111
112
Chapter 3. License
this.model.get('options')
);
}, this);
}
this.widthChangerView = new FwBuilderComponents.ItemView.WidthChanger({
model: this.model,
view: this
});
this.labelInlineEditor = new FwBuilderComponents.ItemView.InlineTextEditor({
model: this.model,
editAttribute: 'options/label'
});
},
render: function () {
this.defaultRender({
label: fw.opg('label', this.model.get('options')),
required: fw.opg('required', this.model.get('options')),
default_value: fw.opg('default_value', this.model.get('options')),
toggle_required: localized.l10n.toggle_required,
edit: localized.l10n.edit,
remove: localized.l10n.delete,
edit_label: localized.l10n.edit_label,
yes: localized.l10n.yes,
no: localized.l10n.no
});
if (this.widthChangerView) {
this.$('.fw-form-item-width').append(
this.widthChangerView.$el
);
this.widthChangerView.delegateEvents();
}
if (this.labelInlineEditor) {
this.$('.fw-form-item-preview-label-edit').append(
this.labelInlineEditor.$el
);
this.labelInlineEditor.delegateEvents();
}
},
openEdit: function() {
this.modal.open();
},
removeItem: function() {
this.remove();
this.model.collection.remove(this.model);
},
toggleRequired: function() {
var values = _.clone(
// clone to not modify by reference, else model.set() will not trigger the 'change' e
this.model.get('options')
);
values.required = !values.required;
113
this.model.set('options', values);
},
openLabelEditor: function() {
this.$('.fw-form-item-preview-label-wrapper').hide();
this.labelInlineEditor.show();
this.listenToOnce(this.labelInlineEditor, 'hide', function() {
this.$('.fw-form-item-preview-label-wrapper').show();
});
},
updateDefaultValueFromPreviewInput: function() {
var values = _.clone(
// clone to not modify by reference, else model.set() will not trigger the 'change' e
this.model.get('options')
);
values.default_value = this.$('.fw-form-item-preview-input input').val();
this.model.set('options', values);
},
onWrapperClick: function(e) {
if (!this.$el.parent().length) {
// The element doesn't exist in DOM. This listener was executed after the item was de
return;
}
if (!fw.elementEventHasListenerInContainer(jQuery(e.srcElement), 'click', this.$el)) {
this.openEdit();
}
}
});
var Item = builder.classes.Item.extend({
defaults: function() {
var defaults = _.clone(localized.defaults);
defaults.shortcode = fwFormBuilder.uniqueShortcode(defaults.type +'_');
return defaults;
},
initialize: function() {
this.defaultInitialize();
this.modalOptions = localized.options;
this.view = new ItemView({
id: 'fw-builder-item-'+ this.cid,
model: this
});
}
});
builder.registerItemClass(Item);
});
Create framework-customizations/extensions/forms/includes/builder-items/yes-no/static/backend
114
Chapter 3. License
/* controls */
/* preview */
.fw-option-type-form-builder .fw-form-builder-item-type-yes-no .fw-form-item-preview {
padding: 5px 0;
}
/** @internal */
function _action_theme_fw_ext_forms_include_custom_builder_items() {
require_once dirname(__FILE__) .'/includes/builder-items/yes-no/class-fw-option-type-form-builder
}
add_action('fw_option_type_form_builder_init', '_action_theme_fw_ext_forms_include_custom_builder_ite
At this point the item is working only in backend. If you save the form, add it in a page (or post) using Page Builder
3.7. Built-in Extensions
115
and open that page in frontend, you will see the item attributes array.
To make item working in frontend, follow the instructions below:
Change the frontend_render() method:
class FW_Option_Type_Form_Builder_Item_Yes_No extends FW_Option_Type_Form_Builder_Item
{
...
public function frontend_render(array $item, $input_value)
{
if (is_null($input_value)) {
$input_value = $item['options']['default_value'];
}
return fw_render_view(
$this->locate_path(
// Search view in 'framework-customizations/extensions/forms/form-builder/items/yes-n
'/views/view.php',
// Use this view by default
dirname(__FILE__) .'/view.php'
),
array(
'item' => $item,
'input_value' => $input_value
)
);
}
}
$options = $item['options'];
?>
<div class="<?php echo esc_attr(fw_ext_builder_get_item_width('form-builder', $item['width'] .'/front
<div class="field-radio input-styled">
<label><?php echo fw_htmlspecialchars($item['options']['label']) ?>
<?php if ($options['required']): ?><sup>*</sup><?php endif; ?>
</label>
<div class="custom-radio">
<div class="options">
<?php
foreach (array('yes' => __('Yes', 'unyson'), 'no' => __('No', 'unyson')) as $value =>
<?php
$choice_attr = array(
'value' => $value,
'type' => 'radio',
'name' => $item['shortcode'],
'id' => 'rand-'. fw_unique_increment(),
);
if ($input_value === $value) {
116
Chapter 3. License
$choice_attr['checked'] = 'checked';
}
?>
<input <?php echo fw_attr_to_html($choice_attr) ?> />
<label for="<?php echo esc_attr($choice_attr['id']) ?>"><?php echo $label ?></lab
<?php endforeach; ?>
</div>
</div>
</div>
</div>
Now the field will be displayed in frontend as a radio box and the validation will work. The submitted value will be
used by the form type you chose when created the form, for e.g. the Contact Forms sub-extensions will send the value
in email.
You can inspect the built-in form items to learn what possibilities for customization are available (for e.g. what
methods from the extended class you can overwrite).
3.7.9 Breadcrumbs
Creates a simplified navigation menu for the pages that can be placed anywhere in the theme. This will make navigating
around the website much easier.
117
Helpers
View
Filters
Date format filters
Helpers
fw_ext_get_breadcrumbs($separator = >) - use this function to return breadcrumbs HTML.
<h3>My page</h3>
<?php echo fw_ext_get_breadcrumbs( '>' ) ?>
<!-- Home >> Books >> PHP For Beginners -->
Note: This function should be used only in the front-end area after WordPress wp action.
fw_ext_breadcrumbs($separator = >) - use this function to render breadcrumbs in your template.
<h3>My page</h3>
<?php fw_ext_breadcrumbs( '>' ) ?>
<!-- Home >> Books >> PHP For Beginners -->
Note: This function should be used only in the front-end area after WordPress wp action.
View
breadcrumbs.php is the template where you can define how the breadcrumbs will be shown on the page.
You can overwrite the default view with your own, by creating a breadcrumbs.php file in the extensions
views directory in the child theme.
Filters
fw_ext_breadcrumbs_build - in some cases you want to modify the breadcrumbs items that will be
rendered, or a specific item. This filter allows you to modify the breadcrumbs items array before it will be
rendered.
/**
* @internal
*/
function _filter_my_custom_breadcrumbs_items( $items ) {
// do some changes ...
return $items;
}
add_filter( 'fw_ext_breadcrumbs_build', '_filter_my_custom_breadcrumbs_items' );
fw_ext_breadcrumbs_search_query - this filter is used in the search archive template and it contains
the search query word. In case you want to modify the word or customize it, like capitalizing it, use this filter.
118
Chapter 3. License
/**
* @internal
*/
function _filter_my_custom_breadcrumbs_search_word( $word ) {
return strtoupper( $word );
}
add_filter( 'fw_ext_breadcrumbs_search_query', '_filter_my_custom_breadcrumbs_search_word' )
/**
* @internal
*/
function _filter_my_custom_breadcrumbs_archive_date_format( $date_format ) {
return 'd, F Y';
}
add_filter( 'fw_ext_breadcrumbs_date_day_format', '_filter_my_custom_breadcrumbs_archive_date_format'
3.7.10 SEO
This extension will enable you to have a fully optimized WordPress website by adding optimized meta titles, keywords
and descriptions. It doesnt have any functionality that is reflected visually in the front end. It offers additional
functionality for its sub-extensions, like Tags module.
Option placeholders
Options Filters
Tags
Add new tag
Update tag value
Actions
Helpers
Option placeholders
In order to keep all sub-extensions options together, the SEO extension creates special options sections in:
Post Options - a section (box or tab) named SEO. In case the post options has the General box with id
general, the seo section will appear as a sub tab for that box, in other cases it creates a new box.
Term Options - a special section in Term Options.
119
Options Filters
All the filters have the same functionality, the only differences is where they add options.
fw_ext_seo_settings_options - use to add your own tab in Settings Options SEO tab.
fw_ext_seo_general_settings - use to add your own box in Settings Options SEO > General tab.
fw_ext_seo_general_setting_options - use to add your own options in Settings Options SEO >
General > General Settings box.
fw_ext_seo_post_type_options - add options in post options SEO box.
fw_ext_seo_taxonomy_options - add options in term options SEO section.
All filters have the same parameter $options array.
/**
* @internal
*/
function _filter_set_my_framework_titles_metas_tab( $options ) {
$options['my_id_tab'] = array(
'title'
=> __( 'My Options', '{domain}' ),
'type'
=> 'tab',
'options' => array(
'my_id_title' => array(
'label' => __( 'Title', '{domain}' ),
'desc' => __( 'Set title', '{domain}' ),
'type' => 'text',
'value' => ''
),
'my_id_description' => array(
'label' => __( 'Description', '{domain}' ),
'desc' => __( 'Set description', '{domain}' ),
'type' => 'textarea',
'value' => ''
),
)
);
return $options;
}
add_filter( 'fw_ext_seo_settings_options', '_filter_set_my_framework_titles_metas_tab' );
Tags
The SEO extension has a list of built in SEO tags, but in some cases youll want to add your own. To add a new SEO
tag you have to use the fw_ext_seo_init_tags filter. This is the format for a SEO tag:
'tag_name' => array(
'desc' => __( 'My new tag', '{domain}' ),
'value' => '',
)
120
Chapter 3. License
The seo tags are created when the extension is initialized, in some cases you cannot know the value of the tag in the
current state, like %%title%% tag. So in fw_ext_seo_init_tags filter, you can add the tag without value, and
define the value after the current page location is defined, by using the fw_ext_seo_update_tags filter.
Update tag value
/**
* @internal
*/
function _filter_update_my_seo_tag( $tags ) {
if ( isset($tags['mytag']) && is_front_page() ) {
$tags['mytag']['value'] = __('Home', '{domain}');
}
return $tags;
}
add_filter( 'fw_ext_seo_update_tags', '_filter_update_my_seo_tag' );
Actions
fw_ext_seo_init_location - is, initialized with WordPress wp action and defines the current page location, used to update SEO tags. Sends as first parameter $location an array with details about current page
location.
Helpers
fw_ext_seo_parse_meta_tags($text) - parses a string and replaces all SEO tags with their values.
Note: Use this function after the fw_ext_seo_init_location action.
A sub-extension of the SEO extension, used to setup the theme SEO title and meta keywords for search engines.
121
Configuration
Views
Hooks
Configuration
/**
* Posts types that you want to exclude from titles and meta settings
*/
$cfg['excluded_post_types'] = array('attachment');
/**
* Taxonomies that you want to exclude from titles and meta settings.
*/
$cfg['excluded_taxonomies'] = array('post_tag');
Views
meta.php - Template to render the meta keywords and description.
Hooks
fw_ext_seo_titles_metas_load_metas - Filter to modify some meta properties before it will be
rendered in front-end.
/**
* @internal
* @param array $data All meta that needs to be rendered on the current page
* @param array $location Current page location details
*/
function _filter_modify_seo_meta($data, $location) {
/**
* The view to display current meta.
* If the view key is not set, then will be loaded meta.php.
*/
$data['view'] = 'my-view';
return $data;
}
add_filter('fw_ext_seo_titles_metas_load_metas', '_filter_modify_seo_meta');
122
Chapter 3. License
}
add_filter('fw_ext_seo_titles_metas_load_title', '_filter_modify_seo_title');
Sitemap
Configuration
/**
* Search engines where to report about the sitemap existence.
* By default the extension supports only Google and Bing.
*/
$cfg['search_engines'] = array('google', 'bing');
/**
* The frequency of the sitemap refresh (measured in days).
*/
$cfg['sitemap_refresh_rate'] = 2;
/**
* Exclude post types from sitemap indexing.
*/
$cfg['excluded_post_types'] = array('attachment');
/**
* Exclude taxonomies from sitemap indexing.
*/
$cfg['excluded_taxonomies'] = array('post_tag');
/**
* Setup the URL frequency and priority for each post_type, taxonomy and the homepage
*/
$cfg['url_settings'] = array(
'home' => array(
'priority' => 1,
'frequency' => 'daily',
),
'posts' => array(
'priority' => 0.6,
'frequency' => 'daily',
/**
* In case you have specific posts type that you want to set different settings
*/
'type' => array(
'page' => array(
'priority' => 0.5,
'frequency' => 'weekly',
)
)
),
123
3.7.11 Events
This extension adds a fully fledged Events module to your theme. It comes with built in pages that contain a calendar
where events can be added.
124
Hooks
New options on the Event edit page
Views
Events Tags
Frontend render
Chapter 3. License
Hooks
fw_theme_ext_events_after_content - adding some html after the content
/** @internal */
function _action_theme_render_html($post) {
if (!empty($post) and $post === fw()->extensions->get( 'events' )->get_post_type_name()
echo '<div>'. __('Hello world', '{domain}') .'</div>';
}
}
add_action('fw_theme_ext_events_after_content', '_action_theme_render_html');
125
Views
Templates are located in the views/ directory. Here is the list of templates that you can customize:
single.php
Events
single
post
template.
By
default
is
used
single.php from the theme root directory,
you can overwrite it by creating
framework-customizations/extensions/events/views/single.php.
taxonomy.php
Events
category
template.
By
default
taxonomy.php from the theme root directory,
you can overwrite it
framework-customizations/extensions/events/views/taxonomy.php.
is
used
by creating
126
Chapter 3. License
echo $shortcode_map->render_custom(
array(
array(
'title' => __('Some Title', '{domain}'),
'url' => 'https://example.com',
'description' => __('Some description', '{domain}'),
'thumb' => array('attachment_id' => get_post_thumbnail_id( $post->ID ) ),
'location' => array(
'coordinates' => array(
'lat' => '-34',
'lng' => '150'
)
)
)
)
);
}
3.7.12 Social
This extension groups in one place all the settings that need to work with social networks.
Filters
Twitter
Filters
Helpers
Facebook
Filters
Helpers
Filters
To be able to insert the settings on this page were created following filters:
fw_ext_social_tabs - Offers the possibility to add tabs.
fw_ext_social_boxes_from_general_tab - Allows adding boxes with options in general tab.
fw_ext_social_main_box_from_general_tab - Allows adding options in general tab.
Twitter
Group the settings to work with the social network Twitter and includes a library to access the API for this social
network.
Filters
127
Helpers
fw_ext_social_facebook_boxes_options - Provides the ability to add boxes and options in facebook tab.
fw_ext_social_facebook_general_box_options - Allow you to add options in main box of facebook tab.
Helpers
3.7.13 Builder
This extension provides the core builder functionality that you can extend to create new builders.
Changing the grid
Changing the grid for all builders
Changing the grid for one builder
The Builder
Data Structure
Creating a Builder
Creating Items
* Registering items in javascript
Generate Custom Value
128
Chapter 3. License
1. Overwrite
framework/extensions/builder/config.php
by
{theme}/framework-customizations/extensions/builder/config.php
creating
$cfg['default_item_widths'] = array(
/**
* Copy/Paste here default columns https://github.com/ThemeFuse/Unyson-Builder-Extension
* and add, remove or change them
*/
);
2. Prevent
default
grid
css
enqueue
and
enqueue
your
own
css.
{theme}/framework-customizations/extensions/builder/static.php
Create
if (!is_admin()) {
wp_register_style(
'fw-ext-builder-frontend-grid',
get_template_directory_uri() .'/framework-customizations/extensions/builder/static/f
array(),
fw()->theme->manifest->get_version()
);
}
The function loads the grid from config, but allows you to change it via this filter. You can use the filter to change the
grid columns for some builder type.
add_filter(
'fw_builder_item_widths:page-builder',
'_filter_theme_custom_page_builder_columns'
);
function _filter_theme_custom_page_builder_columns($columns) {
$columns['3_7'] = array(
'title' => '3/7',
'backend_class' => 'custom-backend-3-7-column', // you must enqueue in backend a css with thi
'frontend_class' => 'frontend-custom-3-7-column', // you must enqueue in frontend a css with
);
return $columns;
}
129
The Builder
The builder is just an option type. But you cant use it right away, because its too abstract and doesnt have any
concrete purpose. You can only extend it and create new builders based on it.
Data Structure
The javascript side of the builder is based on backbone, so it uses collections and models to store the data:
[
{
type: 'foo',
_items: [],
attr_x: 'Hello',
...
},
{
type: 'bar',
_items: [ {type: 'baz', ...}, ... ],
attr_y: 'Hi',
...
},
...
]
Every model (also called item) has a required attribute type. Also it has an attribute _items that is generated
automatically by the backbone-relational plugin, the purpose of which is to make possible to have nested items easier.
There are no rules for other attributes, every item has whatever attributes it wants.
The same data structure is used on the php side, this collection is simply transformed into an array with
json_decode($collection, true).
Creating a Builder
This tutorial will explain you how to create a simple demo builder for html <ul> and <ol> lists. First, create an
option type that extends the builder option type:
// file: theme/inc/includes/option-types/lists-builder/class-fw-option-type-lists-builder.php
class FW_Option_Type_Lists_Builder extends FW_Option_Type_Builder
{
public function get_type() {
return 'lists-builder';
}
}
FW_Option_Type::register('FW_Option_Type_Lists_Builder');
Thats it, the new builder was created. Use it in your post options to see what it shows at this point.
Note: This example assumes that you use in your theme this directory structure.
1. Include the option type:
130
Chapter 3. License
// file: theme/inc/includes/lists-builder.php
/** @internal */
function _action_include_demo_lists_builder() {
if (!fw_ext('builder')) {
/**
* Lists Builder requires the FW_Option_Type_Builder class
* which does not exist if the 'builder' extension is not active.
*
* You can install and activate the 'builder' extension by installing any extension
* for e.g. Page Builder or Learning (which has the Learning Quiz Builder sub-extens
*/
return;
}
3. Go to your.site/wp-admin/edit.php page, open any post edit page and look for the Lists Builder
box.
As you can see, the box is empty. At least youve successfully created the builder, now you can improve it.
Creating Items
To build lists youll need the following elements: <ul>, <ol> and <li>. In builder these elements can be created as
item types. The <ul> and <ol> (containers for <li>) will be created as one item type (with sub types), and <li>
as another item type. To create item types for a builder type you have to:
1. Find out what item types the builder accepts.
That information can be found in the FW_Option_Type_Builder::item_type_is_valid()
method. The builder you created above doesnt have a custom item_type_is_valid() method,
so it is inherited from the extended class, and that method looks like this:
/**
* Overwrite this method to force your builder type items to extend custom class or to have
* @param FW_Option_Type_Builder_Item $item_type_instance
131
* @return bool
*/
protected function item_type_is_valid($item_type_instance)
{
return is_subclass_of($item_type_instance, 'FW_Option_Type_Builder_Item');
}
// file: theme/inc/includes/option-types/lists-builder/item-types/oul/class-fw-lists-builder
class FW_Lists_Builder_Item_Type_OUl extends FW_Option_Type_Builder_Item
{
/**
* Specify which builder type this item type belongs to
* @return string
*/
public function get_builder_type()
{
return 'lists-builder';
}
/**
* The item type
* @return string
*/
public function get_type()
{
return 'oul';
}
/**
* The boxes that appear on top of the builder and can be dragged down or clicked to cre
* @return array
*/
public function get_thumbnails()
{
return array(
array(
'html' =>
'<div class="item-type-icon-title" data-sub-type="ul">'.
'
<div class="item-type-icon"><ul></div>'.
'
<div class="item-type-title">'. __('Unordered List', '{domain}') .'
'</div>',
),
array(
'html' =>
'<div class="item-type-icon-title" data-sub-type="ol">'.
'
<div class="item-type-icon"><ol></div>'.
'
<div class="item-type-title">'. __('Ordered List', '{domain}') .'</
'</div>',
),
);
}
/**
* Enqueue item type scripts and styles
132
Chapter 3. License
*/
public function enqueue_static()
{
}
}
FW_Option_Type_Builder::register_item_type('FW_Lists_Builder_Item_Type_OUl');
Create and register item type that will represent the <li> element:
Refresh the page and you should see three boxes that can be dragged down. Unfortunately you will get an error in
console saying that the item type is not registered. This happens because you also have to register the item type in
javascript and define how it works and looks in builder.
133
Registering
items
in
javascript Registering
builder
items
can
be
done
via
the
builderInstance.registerItemClass(ItemTypeClass) method. Because builderInstance is
created somewhere in builder scripts and its not a global variable, the only way to get it, is to listen special event
fw-builder:{builder-type}:register-items.
1. Create the scripts file that registers the oul item type:
// file:: theme/inc/includes/option-types/lists-builder/item-types/oul/static/scripts.js
fwEvents.one('fw-builder:'+ 'lists-builder' +':register-items', function(builder) {
var ItemClass = builder.classes.Item.extend({
defaults: {
type: 'oul' // the item type is specified here
}
});
builder.registerItemClass(ItemClass);
});
134
Chapter 3. License
array('fw-events')
);
}
}
Refresh the page and try to click or drag down the boxes. The items should appear in the builder, but they are using the
default view and doesnt have any concrete functionality. At this point, you have a working builder. If you add some
items and save the post, after page refresh the builder will recover from the saved json value. Customize the views and
add some functionality to items to be able to build lists with them:
1. Replace the oul item type scripts with:
// file: theme/inc/includes/option-types/lists-builder/item-types/oul/static/scripts.js
135
},
/**
* This method controls which item types are allowed to be added inside this item in
* @param {String} type
* @returns {boolean}
*/
allowIncomingType: function(type) {
if (type == 'li') {
return true;
} else {
return false;
}
}
});
builder.registerItemClass(ItemClass);
});
136
Chapter 3. License
Now the javascript side of the builder has the minimum functionality to be able to build lists. After you build a list and
saved the post, the html of the list needs to be generated so you can display it on the page. To do that, continue to the
next step.
Generate Custom Value
By default the builder saves its value as an array with one key json which stores the original value used in javascript.
From the original value, you can generate any custom values and store them in custom keys. In the case with Lists
Builder, you have to generate the lists html from that original json value to be able to display the list in html. This can
achieved by overwriting the builder _get_value_from_input() method.
class FW_Option_Type_Lists_Builder extends FW_Option_Type_Builder
{
...
/**
* Generate the html of the list
* {@inheritdoc}
*/
protected function _get_value_from_input($option, $input_value)
{
$value = parent::_get_value_from_input($option, $input_value);
$html = '';
foreach (json_decode($value['json'], true) as $list) {
$html .= '<'. $list['list_type'] .'>';
foreach ($list['_items'] as $list_item) {
$html .= '<li>'. $list_item['text'] .'</li>';
}
$html .= '</'. $list['list_type'] .'>';
}
137
$value['html'] = $html;
return $value;
}
}
Now you can use the generated html in post template. Add to theme/single.php:
...
while ( have_posts() ) : the_post();
echo fw_get_db_post_option( null, 'lists-builder/html' );
...
3.7.14 Feedback
The extension adds the possibility for users to leave feedback impressions about a post (product, article, etc). This
system can be activated for some post types, and replaces the default comments system.
Helpers
Views
Hooks
Stars Feedback
Helpers
Views
Helpers
fw_ext_feedback() - displays summary information about the feedback received for a specific post.
Views
reviews.php - the template for displaying reviews.
Hooks
fw_ext_feedback - allows you to add summary information about the feedback received for a specific post.
fw_ext_feedback_listing_walker - provides the ability to send a custom walker class object to use
the when rendering the reviews.
/** @internal */
function _filter_fw_ext_feedback_listing_walker() {
require dirname( __FILE__ ) . '/includes/extends/class-fw-feedback-stars-walker.php';
138
Chapter 3. License
Stars Feedback
The feedback-stars is a child extension that allows visitors to appreciate a post using star rating.
Helpers
3.7.15 Learning
This extension adds a Learning module to your theme. Using this extension you can add courses, lessons and tests for
your users to take.
Config
Views
Helpers
Filters
FW_Extension_Learning class
Methods
Config
From config file you can edit the lesson, course and course category taxonomy slugs.
$cfg['slugs'] = array(
'courses'
=> 'course',
'lessons'
=> 'lesson',
'categories' => 'courses',
);
139
Views
Templates are located in the views/ directory. Here is the list of templates that you can customize:
single-course.php - Learning course single post template.
By default is
used single.php from the theme root directory, you can overwrite it by creating
framework-customizations/extensions/learning/views/single-course.php.
single-lesson.php - Learning lesson single post template.
By default is used
single.php from the theme root directory,
you can overwrite it by creating
framework-customizations/extensions/learning/views/single-lesson.php.
taxonomy.php
Learning
category
template.
By
default
is
used
taxonomy.php from the theme root directory,
you can overwrite it by creating
framework-customizations/extensions/learning/views/taxonomy.php.
content-course.php - Default learning course single page template content. It is loaded if the
framework-customizations/extensions/learning/views/single-course.php doesnt
exist and is used single.php from the theme root directory. The content of this view is rendered using
WordPress the_content filter, when the course single page is loaded.
content-lesson.php - Default learning lesson single page template content. It is loaded if the
framework-customizations/extensions/learning/views/single-lesson.php doesnt
exist and is used single.php from the theme root directory. The content of this view is rendered using
WordPress the_content filter, when the lesson single page is loaded.
Helpers
fw_ext_learning_get_course_lessons() - Returns an array with all course lesson posts.
/**
* @param null|int $post_id The id of the course post
* @return WP_Post[]
*/
$lessons = fw_ext_learning_get_course_lessons( $post_id );
/**
* @param null|int $post_id (optional) The id of the course post
* @return WP_Post[]|null - in case there are no previous posts, or $post_id is not a valid lesson po
*/
$prev = fw_ext_learning_get_previous_lesson( $post_id );
/**
* @param null|int $post_id (optional) The id of the course post
* @return WP_Post[]|null - in case there are no previous posts, or $post_id is not a valid lesson po
140
Chapter 3. License
*/
$prev = fw_ext_learning_get_next_lesson( $post_id );
Usage example
If you edit the lesson template and want to make a pagination to next and previous lessons.
<?php
global $post;
Filters
fw_ext_learning_lessons_label_name - Rename lesson custom post default name ( singular and
plural ).
/** @internal */
function _filter_fw_ext_learning_rename_lesson_custom_post( $names ) {
$names['singular'] = __( 'Singular Name', '{domain}' );
$names['plural'] = __( 'Plural Name', '{domain}' );
return $names;
}
add_filter( 'fw_ext_learning_lessons_label_name', '_filter_fw_ext_learning_rename_lesson_custom_post'
return $names;
}
add_filter( 'fw_ext_learning_courses_label_name', '_filter_fw_ext_learning_rename_course_custom_post'
fw_ext_courses_category_name - Rename course custom post category default name ( singular and
plural ).
/** @internal */
function _filter_fw_ext_learning_rename_course_custom_post_category( $names ) {
$names['singular'] = __( 'Singular Name', '{domain}' );
$names['plural'] = __( 'Plural Name', '{domain}' );
return $names;
}
add_filter( 'fw_ext_courses_category_name', '_filter_fw_ext_learning_rename_course_custom_post_catego
141
FW_Extension_Learning class
The FW_Extension_Learning is the Learning extension base class and in development process it may
offer a lot of great methods to make the development easier. Youll need the current instance of the
FW_Extension_Learning. You can get it using the fw_ext(extension_name) function:
/**
* @var FW_Extension_Learning $learning
*/
$learning = fw_ext('learning');
Do not forget to check the the result is not null, this happens when the extension is not active.
Methods
Learning Quiz
lessons.
A sub-extension of the Learning extension that offers users the possibility to build tests and quiz for
Views
Helpers
Actions
Views Templates are located in the views/ directory. Here is the list of templates that you can customize:
start-quiz.php - Quiz star button from the lesson page.
single.php
Learning
quiz
single
post
template.
By
default
is
used
single.php from the theme root directory,
you can overwrite it by creating
framework-customizations/extensions/learning/extensions/learning-quiz/views/single.php.
content.php - Default learning quiz single page template content.
It is loaded if the
framework-customizations/extensions/learning/extensions/learning-quiz/views/single.php
doesnt exist and is used single.php from the theme root directory. The content of this view is rendered
using WordPress the_content filter, when the lesson single page is loaded.
142
Chapter 3. License
Helpers
fw_ext_learning_quiz_has_quiz( $post_id ) - Check if the post is lesson and if it has a quiz.
if ( fw_ext_learning_quiz_has_quiz( $post_id ) ) { ... }
/**
* @param int $post_id
* @return WP_Post|null - in case the the id is not valid or is not lesson post type, or the lesson d
*/
$quiz = fw_ext_learning_quiz_get_quiz( $post_id );
Actions
fw_ext_learning_quiz_form_process - Action fired when the quiz form was submitted and processed.
/**
* @internal
* @param int $post_id
* @return array(
* questions => FW_Quiz_Question_Process_Response[]
* accumulated => (int|float) The amount of points accumulated
* minimum-pass-mark - (int|float) The minimum pass-mark
* )
*/
function _action_fw_process_quiz_response( $response ) {
// ...
}
add_action( 'fw_ext_learning_quiz_form_process', '_action_fw_process_quiz_response' );
3.7.16 Translation
This extension lets you translate your website in any language or even add multiple languages for your users to change
at their will from the front-end.
Helpers
Filters
143
Helpers
fw_ext_translation_get_frontend_active_language() - Frontend active language.
fw_ext_translation_get_backend_active_language() - Backend active language.
Filters
fw_ext_translation_change_render_language_switcher - Change the view of the language
switcher.
Chapter 3. License
3. Shortocde in wp-editor that is inserted anywhere else (like Theme Settings or any OptionsModal)
use case vs. what you get
1 Post Editor
2 Page Builder Shortcode
3 Any wp-editor
HTML
yes
yes
yes
static.php
yes
yes
no
dynamic css
yes
no
no
By default, youll a get a button in the main post editor with all of the shortcodes that are enabled, except the section
and column ones. This is actually the most simple use-case and you have nothing to do in order to get them working.
Everything should be out of the box here.
You can in fact, customize which shortcodes are showed up using this snippet of code:
<?php if (!defined('FW')) die('Forbidden');
add_filter('fw:ext:wp-shortcodes:default-shortcodes', _set_default_shortcodes);
function _set_default_shortcodes($previous_shortcodes) {
return array( 'button', 'notification' );
}
3.8.1 Actions
General
fw_init - The framework is fully loaded and you can safely access any of its components. Useful when you
need to init some theme components only when the framework is installed.
add_action('fw_init', '_action_theme_fw_init');
function _action_theme_fw_init() {
$value = fw_get_db_customizer_option('hello');
// fw()->...
}
145
add_action('fw_backend_add_custom_settings_menu', '_action_theme_custom_fw_settings_menu');
function _action_theme_custom_fw_settings_menu($data) {
add_menu_page(
__( 'Awesome Settings', '{domain}' ),
__( 'Awesome Settings', '{domain}' ),
$data['capability'],
$data['slug'],
$data['content_callback']
);
}
add_action('fw_admin_enqueue_scripts:settings', '_action_theme_enqueue_scripts_theme_setting
function _action_theme_enqueue_scripts_theme_settings() {
wp_enqueue_script(
'theme-settings-scripts',
get_template_directory_uri() .'/js/admin-theme-settings.js',
array('fw'),
fw()->theme->manifest->get_version(),
true
);
}
146
Chapter 3. License
}
}
Database
fw_post_options_update - After database post option or all options were updated. The description of
parameters can be found here.
3.8.2 Filters
General
fw_framework_customizations_dir_rel_path - Relative path of the customizations directory located in theme. By default it is /framework-customizations.
add_filter(
'fw_framework_customizations_dir_rel_path',
'_filter_theme_fw_customizations_dir_rel_path'
);
function _filter_theme_fw_customizations_dir_rel_path($rel_path) {
/**
* Make the directory name shorter. Instead of
* {theme}/framework-customizations/theme/options/post.php
* will be
* {theme}/fw/theme/options/post.php
*/
return '/fw';
}
Options
fw_settings_options
Theme
Settings
Options,
which
are
{theme}/framework-customizations/theme/options/settings.php
loaded
from
add_filter('fw_settings_options', '_filter_theme_fw_settings_options');
function _filter_theme_fw_settings_options($options) {
$options['extra-tab'] = array(
'type' => 'tab',
'title' => __('Extra Tab', 'domain'),
'options' => array(
'test' => array('type' => 'text'),
),
);
return $options;
}
147
loaded
from
add_filter('fw_customizer_options', '_filter_theme_fw_customizer_options');
function _filter_theme_fw_customizer_options($options) {
$options['extra-option'] = array('type' => 'text');
return $options;
}
fw_taxonomy_options
Taxonomy
Term
Options,
which
are
loaded
from
{theme}/framework-customizations/theme/options/taxonomies/{taxonomy}.php
add_filter('fw_taxonomy_options', '_filter_theme_fw_taxonomy_options', 10, 2);
function _filter_theme_fw_taxonomy_options($options, $taxonomy) {
if ($taxonomy == 'category') {
$options['extra-option'] = array('type' => 'text');
}
return $options;
}
148
Chapter 3. License