Current File : /home/aventura/www/site/wp-content/plugins/victheme_core/wordpress/models/shortcodes.php |
<?php
/**
* Base class for bridging VTCore HTML class and it's subclasses
* into WordPress Shortcode API.
*
* Adding a new shortcode class process :
*
* 1. Add the new shortcode base into $shortcode either via the VTCore init shortcodes factory
* or do it via WordPress filter vtcore_register_shortcode.
* 2. If the subclasses have different naming convention other than VTCore_Wordpress_Shortcodes_
* then it must register the overloader naming via $overloaderPrefix or via
* WordPress filter vtcore_register_shortcode_prefix or via VTCore init shortcode factory
* 3. The shortcode subclass must handle its own autoloading mechanism if it is not
* using the VTCore autoloading class.
* 4. Create the subclasses that defines the shortcode (look at the api folder for example)
*
* @author jason.xie@victheme.com
*/
abstract class VTCore_Wordpress_Models_Shortcodes {
/**
* All subclasses must store their object in this variable
*/
protected $object;
/**
* Subclasses can define attributes with camel case that
* should be preserved at all cost.
*
* The convertion will be invoked in the __call() method.
*/
protected $camelcase = array();
/**
* Variable for storing shortcode attributes
*/
protected $atts = array();
/**
* Subclass should store their default data here
*/
protected $data = array();
/**
* Subclass can define settings arrays
* this is usefull for jQuery plugin that has
* camel case for the settings which cannot
* use data- method
*/
protected $settings = array();
/**
* Subclass can defined the attributes key
* that must be converted to booleans by
* supplying the key of the attributes array
* in this array.
*/
protected $booleans = array();
/**
* Subclass can defined the attributes key
* that must be converted to integer by
* supplying the key of the attributes array
* in this array.
*/
protected $int = array();
/**
* Marker for auto processing tags, set this to
* false in the child element if it doesn't want
* auto tag processing.
*/
protected $processTag = true;
/**
* Marker for auto converting data into attributes
* data with key prefix.
*/
protected $convertDataToAttributes = true;
/**
* Marker for converting attributes from invalid
* shortcode due to use of dotted notation into
* a valid shortcode attributes array.
*
* Dotted notation support . and # as the array
* breaker.
*/
protected $processDottedNotation = false;
/**
* Marker for allowing plugin to convert bootstrap
* grid classes into Visual composer grids
* @var bool
*/
protected $convertVCGrid = false;
/**
* Storing the shortcode calling name
*/
protected $shortcode = false;
/**
* Constructor method, this is important because
* we need to preprocess the atts to match HtmL object
* and its subclasses context rules
*
* @param array $atts raw attributes provided by WordPress Shortcode API
* @param string $content raw content provided by WordPress Shortcode API
* @param string $shortcode the shortcode name
*/
public function __construct($atts = array(), $content = NULL, $shortcode = NULL) {
$this->content = $content;
$this->shortcode = $shortcode;
$this->preprocessAtts($atts);
}
/**
* Returning the markup for the stored object
*/
public function getMarkup() {
// @hook allow other class or plugin to do final alteration
// before the object is rendered.
do_action('vtcore_wordpress_shortcode_object_alter', $this->object);
$markup = (is_object($this->object)) ? $this->object->__toString() : $this->object;
if ($this->convertVCGrid) {
VTCore_Wordpress_Utility::loadAsset('wp-visualcomposer-extra');
// Extract the class="*" string from markup
$regex = '/class="([^"]*+)"/m';
preg_match_all($regex, $markup, $classes, PREG_PATTERN_ORDER);
if (isset($classes[0]) && !empty($classes[0])) {
foreach ($classes[0] as $class) {
$class = ' ' . str_replace('class="', 'class=" ', $class);
$newstring = str_replace(
array(
' row',
' col-',
' push-left',
' push-right',
' pull-left',
' pull-right',
' pull-center',
' text-center',
' text-left',
' text-right',
' hidden-',
' visibile-',
),
array(
' vc_row',
' vc_col-',
' vc_push-left',
' vc_push-right',
' vc_pull-left',
' vc_pull-right',
' vc_pull-center',
' vc_txt_align_center',
' vc_txt_align_left',
' vc_txt_align_right',
' vc_hidden-',
' vc_visible-'
),
$class);
$markup = str_replace(
trim(str_replace('class=" ', 'class="', $class)),
trim(str_replace('class=" ', 'class="', $newstring)),
$markup);
}
}
}
return $markup;
}
/**
* Some jQuery plugin is very picky about camelcase data
* attributes. this function will preserve the original
* camelcase taken from shortcode atts.
*
* Sub class must define the protected $camelcase variable
* to let the base class undertand on what to preserve.
*
* @param array $atts
* @return array
*/
protected function preserveCamelCase() {
$object = new VTCore_Wordpress_Objects_Array((array)$this->atts);
foreach ($this->camelcase as $case) {
$name = strtolower($case);
if ($object->get($name)) {
$object->add($case, $object->get($name));
$object->remove($name);
}
}
$this->atts = $object->extract();
unset($object);
$object = null;
}
/**
* VTCore don't allow string as class
* convert them into arrays.
*/
protected function processClass() {
if (isset($this->atts['class'])) {
$classes = explode(' ', $this->atts['class']);
if (is_array($classes)) {
foreach ($classes as $class) {
$this->atts['attributes']['class']['custom-' . $class] = $class;
if ($class == 'equalheightRow') {
VTCore_Wordpress_Utility::loadAsset('jquery-equalheight');
}
}
}
unset($this->atts['class']);
}
}
/**
* Process attributes ID
*/
protected function processID() {
if (isset($this->atts['id'])) {
$this->atts['attributes']['id'] = str_replace(' ', '-', $this->atts['id']);
unset($this->atts['id']);
}
}
/**
* Preprocess bootstrap grids
*/
protected function processGrid() {
$gridKeys = array(
'columns',
'push',
'pull',
'offset',
'hidden',
'visible',
);
if (!empty($this->atts) && is_array($this->atts)) {
foreach ($this->atts as $key => $value) {
if (strpos($key, '_') === false) {
continue;
}
list($type, $name) = explode('_', $key);
// @bugfix hidden and visible always printed
if (($type == 'hidden' || $type == 'visible') && empty($value)) {
continue;
}
if (in_array($type, $gridKeys)) {
$this->atts['grids'][$type][$name] = $value;
}
}
}
}
/**
* Preprocess data attributes, this
* method is useful for creating data-x attributes
* as defined in the $data and $atts.
*/
protected function processData() {
foreach ($this->data as $key => $value) {
if (isset($this->atts[$key])) {
$this->atts['data'][$key] = $this->atts[$key];
if ($this->convertDataToAttributes) {
$this->atts['attributes']['data-' . $key] = $this->atts[$key];
}
unset($this->atts[$key]);
}
}
}
/**
* Preprocess Settings
* The markup will be data-settings and in json format
*/
protected function processSetting() {
$settings = array();
foreach ($this->settings as $key) {
if (isset($this->atts[$key])) {
$settings[$key] = $this->atts[$key];
}
}
if (!empty($settings)) {
$this->atts['data']['settings'] = json_encode($settings);
}
}
/**
* Preprocess shortcode tag attributes and assign
* the value as the object tags.
*/
protected function processTag() {
if (isset($this->atts['tag']) && !empty($this->atts['tag']) && $this->processTag == true) {
$this->atts['type'] = $this->atts['tag'];
}
}
/**
* Preprocess inline styles
* shortcode must utilize style params with normal inline
* style in the format of style:value; to utilize this
* methor properly.
*/
protected function processInlineStyle() {
if (isset($this->atts['style']) && !empty($this->atts['style'])) {
$styles = explode(';', $this->atts['style']);
foreach ($styles as $style) {
if (strpos($style, ':') !== false) {
list($key, $value) = explode(':', $style);
$this->atts['styles'][trim($key)] = trim($value);
}
}
}
}
/**
* Process dotted notation and explode them
* into real arrays
*
* Wordpress by default will failed to parse
* the attributes due to the character used
* as dotted notation breaker (. or #) is treated
* as invalid character.
*
* The original array will be a numeric keyed
* array with the verbatim attributes string, example
*
* array(
* 0 => 'somekey#somechildkey#somegranchildkey="somevalue"',
* );
*
* Visual Composer as of 4.4.3 doesn't support . or # as
* the breaker, use ___ instead (triple underscore).
*
* This method supports both . and # as the breaker.
*/
protected function processDottedNotation() {
if ($this->processDottedNotation && is_array($this->atts)) {
foreach ($this->atts as $key => $value) {
// Wordpress convert dotted to numerical array, convert
// it to dotted key and value
if (is_numeric($key)) {
list($dottedKey, $dottedValue) = explode('=', $value);
// Visual composer can't handle anything except underscore.
if (strpos($dottedKey, '___') !== false) {
$dottedKey = str_replace('___', '.', $dottedKey);
}
$this->remove($key);
$this->add($dottedKey, substr($dottedValue, 1, -1));
}
// Normal dotted, direct injecting
elseif (strpos($key, '#') !== false || strpos($key, '.') !== false) {
$this->remove($key);
$this->add($key, $value);
}
// Handling VisualComposer, only underscore allowed
elseif (strpos($key, '___') !== false) {
$dottedKey = str_replace('___', '.', $key);
$this->remove($key);
$this->add($dottedKey, $value);
}
}
}
}
/**
* Preprocess booleans
* This method will search for attributes as specified
* in the $booleans class variables and attempt to
* convert value such as "true", "false", 0, 1 into
* the corresponding booleans
*/
protected function processBooleans() {
foreach ($this->booleans as $key) {
if ($this->get($key)) {
$this->add($key, filter_var($this->get($key), FILTER_VALIDATE_BOOLEAN));
}
}
}
/**
* Preprocess Int
* This method will search for attributes as specified
* in the $int class variables and attempt to
* convert value to true integers
*/
protected function processInt() {
foreach ($this->int as $key) {
if ($this->get($key)) {
$this->add($key, (int) $this->get($key));
}
}
}
/**
* Custom preprocess function, this is meant to be
* extended by subclass if needed.
*/
protected function processCustomRules() {}
/**
* Preprocess shortcode attributes first
* as many jQuery plugin is very picky about the data structure.
*/
protected function preprocessAtts($atts) {
$this->atts = (array) $atts;
$this->atts['shortcode'] = strtolower($this->shortcode);
$this->processDottedNotation();
$this->preserveCamelCase();
$this->processBooleans();
$this->processInt();
$this->processID();
$this->processClass();
$this->processData();
$this->processGrid();
$this->processSetting();
$this->processTag();
$this->processInlineStyle();
// Sub class can override this to provide
// their own attributes preprocess function.
$this->processCustomRules();
$this->atts = apply_filters('vtcore_wordpress_shortcode_attributes_alter', $this->atts);
return $this->atts;
}
public function extract() {
return $this->atts;
}
public function set(array $value) {
$this->atts = $value;
return $this;
}
public function reset() {
$this->atts = array();
return $this;
}
public function add($keys, $value) {
if (!is_array($this->atts)) {
$this->atts = (array) $this->atts;
}
VTCore_Utility::setArrayValueKeys($this->atts, $keys, $value);
return $this;
}
public function get($keys) {
if (!is_array($this->atts)) {
$this->atts = (array) $this->atts;
}
return VTCore_Utility::getArrayValueKeys($this->atts, $keys);
}
public function remove($keys) {
if (!is_array($this->atts)) {
$this->atts = (array) $this->atts;
}
VTCore_Utility::removeArrayValueKeys($this->atts, $keys);
return $this;
}
public function merge(array $options) {
if (!is_array($this->atts)) {
$this->atts = (array) $this->atts;
}
if (!is_array($options)) {
$options = (array) $options;
}
$this->atts = VTCore_Utility::arrayMergeRecursiveDistinct($options, $this->atts);
return $this;
}
/**
* Removing single shortcode
* @param string $code name of the shortcode
* @param string $content
* @return string content with shortcode striped
*/
protected function stripShortcode($code, $content) {
global $shortcode_tags;
$stack = $shortcode_tags;
$shortcode_tags = array($code => 1);
$content = strip_shortcodes($content);
$shortcode_tags = $stack;
return $content;
}
}