Guest User

sfds

a guest
Feb 24th, 2019
358
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 37.54 KB | None | 0 0
  1. <?php
  2. /**
  3. * Central Shortcode Template Class
  4. */
  5.  
  6. // Don't load directly
  7. if ( !defined('ABSPATH') ) { die('-1'); }
  8.  
  9. if ( !class_exists( 'aviaShortcodeTemplate' ) ) {
  10.  
  11. abstract class aviaShortcodeTemplate
  12. {
  13. /**
  14. *
  15. * @var AviaBuilder
  16. */
  17. public $builder;
  18.  
  19. /**
  20. *
  21. * @var array
  22. */
  23. public $config;
  24.  
  25. /**
  26. *
  27. * @var array
  28. */
  29. public $elements;
  30.  
  31. /**
  32. *
  33. * @param AviaBuilder $builder
  34. */
  35. public function __construct( $builder )
  36. {
  37. $this->builder = $builder;
  38. $this->elements = array();
  39.  
  40. /**
  41. * Needed to check and repair shortcode structure
  42. * We define all defaults for content elements here, which are the most common elements.
  43. *
  44. * first_in_row: set a string that is added/removed from the element attributes if the element is the first/not the first in a line, e.g. first or foo='bar'
  45. * layout_children: shortcodes that must be included to have a valid element when we need to repair the content of a page.
  46. * Nested shortcodes must be added, if they are part of the empty element.
  47. * E.g. tab_section need tab_sub_sections but these are not nested !
  48. * forced_load_objects array of string: e.g. layerslider must be loaded right after init hook, but when we cannot know if we need it because an element is loaded dynamically
  49. * (e.g. postcontent) we can add a unique string to tell layerslider to load, when this shortcode is found in content
  50. */
  51. $this->config = array(
  52. 'type' => 'content', // 'layout' | 'content' needed in syntax error correction
  53. 'self_closing' => '', // 'yes' | 'no' if empty base class scans for id="content" in first level of $elements (a fallback for third party elements)
  54. 'contains_text' => 'yes', // 'yes' | 'no' is plain text allowed in content area of shortcode
  55. 'contains_layout' => 'no', // 'yes' | 'no' are layout elements allowed in content area of shortcode
  56. 'contains_content' => 'no', // 'yes' | 'no' are content elements allowed in content area of shortcode
  57. 'first_in_row' => '', // '' | attribute to add/remove if first element in a layout line
  58. 'auto_repair' => 'yes', // 'yes' | 'no' disable for nested parent element if structure of element complex (more than 1 subelement and nested again like av_table)
  59. 'layout_children' => array(),
  60. 'shortcode_nested' => array(),
  61. 'forced_load_objects' => array() // "name" of external objects that must be included when we find this shortcode in content
  62. );
  63.  
  64.  
  65. $this->shortcode_insert_button();
  66. $this->extra_config();
  67.  
  68. /**
  69. * Add the unique ID field
  70. */
  71. $this->elements[] = array(
  72. "name" => __("Unique ID for ALB element",'avia_framework' ),
  73. "desc" => __("stores the unique ID for the element",'avia_framework' ),
  74. "id" => aviaElementManager::ELEMENT_UID,
  75. "type" => "hidden",
  76. "std" => ""
  77. );
  78.  
  79. }
  80.  
  81. /**
  82. * @since 4.2
  83. */
  84. public function __destruct()
  85. {
  86. unset( $this->builder );
  87. unset( $this->config );
  88. unset( $this->elements );
  89. }
  90.  
  91. //init function is executed in AviaBuilder::createShortcode if the shortcode is allowed
  92. public function init()
  93. {
  94. $this->create_asset_array();
  95. $this->actions_and_filters();
  96.  
  97. if(is_admin() || AviaHelper::is_ajax())
  98. {
  99. $this->admin_assets();
  100. }
  101.  
  102. $this->register_shortcodes();
  103.  
  104. //set up loading of assets. wait until post id is known
  105. add_action('wp', array(&$this, 'extra_asset_check') , 10 );
  106. }
  107.  
  108.  
  109. /**
  110. * shortcode_insert_button: creates the shortcode button for the backend canvas
  111.  
  112. * create the config array. eg:
  113.  
  114. * $this->config['name'] = __('Text', 'avia_framework' ); //defines the name of the button that is displayed below the icon
  115.  
  116. * $this->config['tab'] = __('Layout Elements', 'avia_framework' ); //tab that should hold the button
  117.  
  118. * $this->config['icon'] = $this->builder->imagesURL."full.png"; //icon for the button
  119.  
  120. * $this->config['shortcode'] = 'one_full'; //the shortcode name. this would be the [one_full] shortcode
  121.  
  122. * $this->config['tooltip'] = __('This is a tooltip', 'avia_framework' ); //the tooltip that appears when hovering above the shortcode icon
  123.  
  124. * $this->config['order'] = 40; //order of the button. higher numbers are displayed first
  125.  
  126. * $this->config['target'] = "avia-target-insert"; //if target mode is "avia-target-insert" item will not be added instantly when clicked. other option is avia-section-drop which allos dropping on sections
  127.  
  128. * $this->config['modal_data'] = array('modal_class' => 'mediumscreen'); // data that gets passed to the modal window. eg the class that controlls the modal size
  129.  
  130. * $this->config['modal_on_load'] = array("js", "functions"); //javascript function that should be executed once the modal window has opened
  131.  
  132. * $this->config['shortcode_nested'] = array('av_tab'); // nested shortcodes. needs to be defined if a modal group is used as popup element
  133.  
  134. * $this->config['tinyMCE'] = array('tiny_only'=>true,'instantInsert' => "[asdf]1[/asdf]", 'disable' => true); // show only in tiny mce / do an instant insert instead of modal / disable element in tinymce
  135.  
  136. * $this->config['invisible'] = true; // used to hide the element in builder tab. used for columns eg: 2/5, 3/5 etc
  137.  
  138. * $this->config['html_renderer'] = false; //function that renderes the backend editor element.
  139. //if set to false no function is used and the output has to be passed by the
  140. //"editor_element" function. if not set at all the default function
  141. //"create_sortable_editor_element" is used
  142.  
  143. * $this->config['drag-level'] = 2; // sets the drag level for an element. drag level must be higher than the drop level of the target, otherwise you cant drop the element onto the other
  144.  
  145. * $this->config['drop-level'] = 2; // set the drop level for an element. set drop level to -1 if element shouldnt be dropable
  146.  
  147.  
  148. */
  149.  
  150. abstract function shortcode_insert_button();
  151.  
  152.  
  153.  
  154.  
  155.  
  156. /**
  157. * holds the function that generates the html code for the frontend
  158. */
  159. abstract function shortcode_handler($atts, $content = "", $shortcodename = "", $meta = "");
  160.  
  161.  
  162.  
  163.  
  164.  
  165. /**
  166. * function that gets executed if the shortcode is allowed. allows shortcode to load extra assets like css or javascript files in the admin area
  167. */
  168.  
  169. public function admin_assets(){}
  170.  
  171.  
  172. /**
  173. * function that checks if an asset is disabled and if not loads all extra assets
  174. */
  175.  
  176. public function extra_asset_check()
  177. {
  178. //generic check if the assets for this element should be loaded
  179. if(!is_admin() && ( empty( $this->builder->disabled_assets[ $this->config['shortcode'] ]) || empty( $this->config['disabling_allowed'] ) ) )
  180. {
  181. $this->extra_assets();
  182. }
  183. }
  184.  
  185.  
  186.  
  187. /**
  188. * function that gets executed if the shortcode is allowed. allows shortcode to load extra assets like css or javascript files
  189. */
  190.  
  191. public function extra_assets(){}
  192.  
  193.  
  194. /**
  195. * Scans the $this->elements array recursivly and returns the first element where key = "$element_id"
  196. *
  197. * @param string $element_id
  198. * @param array $source
  199. * @return array|false
  200. * @since 4.1.3
  201. */
  202. public function get_popup_element_by_id( $element_id, array &$source = null )
  203. {
  204. if( empty( $source ) )
  205. {
  206. $source = &$this->elements;
  207. }
  208.  
  209. foreach( $source as &$element )
  210. {
  211. if( isset( $element['id'] ) && ( $element_id == $element['id'] ) )
  212. {
  213. return $element;
  214. }
  215.  
  216. if( isset( $element['subelements'] ) && ! empty( $element['subelements'] ) )
  217. {
  218. $found = $this->get_popup_element_by_id( $element_id, $element['subelements'] );
  219. if( false !== $found )
  220. {
  221. return $found;
  222. }
  223. }
  224. }
  225.  
  226. return false;
  227. }
  228.  
  229.  
  230. /**
  231. * Editor Element - this function defines the visual appearance of an element on the AviaBuilder Canvas
  232. * Most common usage is to define some markup in the $params['innerHtml'] which is then inserted into the drag and drop container
  233. * Less often used: $params['data'] to add data attributes, $params['class'] to modify the className
  234. *
  235. *
  236. * @param array $params this array holds the default values for $content and $args.
  237. * @return $params the return array usually holds an innerHtml key that holds item specific markup.
  238. */
  239. public function editor_element($params)
  240. {
  241. $params['innerHtml'] = "";
  242. if(isset($this->config['icon']))
  243. {
  244. $params['innerHtml'] .= "<img src='".$this->config['icon']."' title='".$this->config['name']."' alt='' />";
  245. }
  246. $params['innerHtml'].= "<div class='avia-element-label'>".$this->config['name']."</div>";
  247. return $params;
  248. }
  249.  
  250.  
  251. /**
  252. * function that creates the sets the elements for the popup editor. Not defined by this class but executed if the child class got it
  253. */
  254.  
  255. # function popup_elements(){}
  256.  
  257.  
  258. /**
  259. * function that creates the popup editor. only used in classes that have a config array defined by the set_elements class
  260. * a child class that has the function declared automaticaly gets an edit button in the admin section
  261. */
  262. public function popup_editor($var)
  263. {
  264.  
  265. if( empty( $this->elements ) ) { die(); }
  266.  
  267. if( ( 1 == count( $this->elements ) ) && isset( $this->elements[0]['id'] ) && ( aviaElementManager::ELEMENT_UID == $this->elements[0]['id'] ) )
  268. {
  269. die();
  270. }
  271.  
  272. if(current_theme_supports('avia_template_builder_custom_css'))
  273. {
  274. $this->elements = $this->avia_custom_class_for_element($this->elements);
  275. }
  276.  
  277. if( !empty($this->config['preview']) )
  278. {
  279. $this->elements = $this->avia_custom_preview_bg($this->elements);
  280. }
  281.  
  282. $elements = apply_filters('avf_template_builder_shortcode_elements', $this->elements);
  283.  
  284. //if the ajax request told us that we are fetching the subfunction iterate over the array elements and
  285. if(!empty($_POST['params']['subelement']))
  286. {
  287. foreach($elements as $element)
  288. {
  289. if(isset($element['subelements']))
  290. {
  291. $elements = $element['subelements'];
  292. break;
  293. }
  294. }
  295. }
  296.  
  297.  
  298. $elements = $this->set_default_values($elements);
  299. echo AviaHtmlHelper::render_multiple_elements($elements, $this);
  300.  
  301.  
  302. die();
  303. }
  304.  
  305. /**
  306. * Sets some internal variables and counters, then calls the actual shortcode handling function
  307. *
  308. * @since < 4.0
  309. * @param array $atts
  310. * @param string $content
  311. * @param string $shortcodename
  312. * @param boolean $fake true if called directly
  313. * @return string
  314. */
  315. public function shortcode_handler_prepare( $atts, $content = '', $shortcodename = '', $fake = false )
  316. {
  317. global $post;
  318.  
  319.  
  320. /**
  321. *
  322. * @used_by currently unused
  323. * @since 4.5.1
  324. * @return array
  325. */
  326. $args = array( true, $this, $atts, $content, $shortcodename, $fake );
  327. $continue = apply_filters_ref_array( 'avf_in_shortcode_handler_prepare_start', array( &$args ) );
  328. if( ! $continue[0] )
  329. {
  330. return '';
  331. }
  332.  
  333. /**
  334. * In modal popup preview mode in backend we only need to execute the shortcode
  335. */
  336. if( ! Avia_Builder()->in_text_to_preview_mode() )
  337. {
  338. /**
  339. * In frontend we ignore requests to shortcodes before header is finished
  340. * Fixes problems with plugins that run shortcodes in header (like All In One SEO)
  341. * Running shortcodes twice might break the behaviour and layout.
  342. */
  343. if( ! is_admin() && ! Avia_Builder()->wp_head_done && ( ! ( defined( 'REST_REQUEST' ) && true === REST_REQUEST ) ) )
  344. {
  345. /**
  346. *
  347. * @since 4.5.5
  348. * @param string
  349. * @param array $atts
  350. * @param string $content
  351. * @param string $shortcodename
  352. * @param boolean $fake
  353. * @return string
  354. */
  355. return apply_filters( 'avf_shortcode_handler_prepare_in_wp_head', '', $atts, $content, $shortcodename, $fake );
  356. }
  357.  
  358. /**
  359. * First we have to check if post for shortcode tree has changed (happens in loops like archive, REST API calls, ...).
  360. * In this case we invalidate the tree and reset index.
  361. * Only needed for frontend.
  362. */
  363. if( ! is_admin() && $post instanceof WP_Post )
  364. {
  365. if( ! ShortcodeHelper::$current_post_in_tree instanceof WP_Post )
  366. {
  367. ShortcodeHelper::$current_post_in_tree = $post;
  368. }
  369. else if( $post->ID != ShortcodeHelper::$current_post_in_tree->ID )
  370. {
  371. /**
  372. * When finished a loop posts might have been reset to 1st post
  373. */
  374. if( ! ( Avia_Builder()->wp_sidebar_started || Avia_Builder()->wp_footer_started ) )
  375. {
  376. ShortcodeHelper::$current_post_in_tree = $post;
  377. ShortcodeHelper::$tree = array();
  378. ShortcodeHelper::$shortcode_index = 0;
  379. }
  380. }
  381. }
  382.  
  383. /**
  384. * Fixes problems with $meta['index] = undefined notices because shortcode tree is not initialised
  385. *
  386. * - WP5.0 with Gutenberg make REST_API calls to save post content and activates shortcodes (via content filter)
  387. * - A fallback situation for 3-rd party plugins like YOAST which call shortcodes to execute for analysis and
  388. * no shortcode tree has been initialised
  389. * - API calls to fetch content like REST API, GraphQL
  390. *
  391. * @since 4.5.4
  392. */
  393. if( ! is_array( ShortcodeHelper::$tree ) || empty( ShortcodeHelper::$tree ) )
  394. {
  395. $return = true;
  396.  
  397. if( ! is_admin() )
  398. {
  399. $out = '';
  400.  
  401. /**
  402. * In front we try to init if we have a post id
  403. */
  404. if( $post instanceof WP_Post )
  405. {
  406. $tree = Avia_Builder()->get_shortcode_tree( $post->ID );
  407. if( ! empty( $tree ) )
  408. {
  409. ShortcodeHelper::$tree = $tree;
  410. ShortcodeHelper::$shortcode_index = 0;
  411. $return = false;
  412. }
  413. }
  414. else
  415. {
  416. /**
  417. * return nothing if we have not finished header - should allow plugins to preprocess page content in header
  418. * (without executing shortcodes - might not work for some shortcodes like codeblock).
  419. */
  420. if( Avia_Builder()->wp_head_done )
  421. {
  422. $return = false;
  423. }
  424. }
  425. }
  426. else
  427. {
  428. /**
  429. * In backend we return the shortcode as self closing shortcode only as we might have no reliable info about content
  430. * (e.g. YOAST calls shortcodes without content in ajax call wpseo_filter_shortcodes)
  431. */
  432. $args = array();
  433. if( is_array( $atts ) )
  434. {
  435. foreach( $atts as $key => $value )
  436. {
  437. $args[] = is_numeric( $key ) ? $value : "{$key}='{$value}'";
  438. }
  439. }
  440.  
  441. $args = ! empty( $args ) ? ' ' . implode( ' ', $args ) : '';
  442. $out = "[{$shortcodename}{$args}]";
  443. }
  444.  
  445. if( $return )
  446. {
  447. /**
  448. *
  449. * @since 4.5.4
  450. * @param string
  451. * @param array $atts
  452. * @param string $content
  453. * @param string $shortcodename
  454. * @param boolean $fake
  455. * @return string
  456. */
  457. return apply_filters( 'avf_shortcode_handler_prepare_fallback', $out, $atts, $content, $shortcodename, $fake );
  458. }
  459. }
  460. }
  461.  
  462. //dont use any shortcodes in backend
  463. $meta = array(
  464. 'el_class' => '',
  465. 'index' => 0,
  466. 'this' => array(),
  467. 'siblings' => array(
  468. 'next' => array(),
  469. 'prev' => array()
  470. )
  471. );
  472.  
  473. $tree_item = ShortcodeHelper::find_tree_item( ShortcodeHelper::$shortcode_index );
  474.  
  475. $is_valid = false;
  476. if( is_array( $tree_item ) && isset( $tree_item['tag'] ) && $tree_item['tag'] == $shortcodename && ! ShortcodeHelper::$is_direct_call && ! $fake )
  477. {
  478. $is_valid = true;
  479. }
  480.  
  481. /**
  482. * inline shortcodes like dropcaps are basically nested shortcodes and should therefore not be counted
  483. * Also shortcodes that are before or after content and when called directly (e.g. codeblock)
  484. */
  485. if( empty( $this->config['inline'] ) )
  486. {
  487. if( $is_valid )
  488. {
  489. $meta = array(
  490. 'el_class' => ' avia-builder-el-' . ShortcodeHelper::$shortcode_index . ' ',
  491. 'index' => ShortcodeHelper::$shortcode_index,
  492. 'this' => $tree_item,
  493. 'siblings' => array(
  494. 'next' => ShortcodeHelper::find_tree_item( ShortcodeHelper::$shortcode_index, 1 ),
  495. 'prev' => ShortcodeHelper::find_tree_item( ShortcodeHelper::$shortcode_index, -1 )
  496. )
  497. );
  498. }
  499.  
  500. if(!empty($meta['siblings']['prev']['tag']))
  501. {
  502. $meta['el_class'] .= " el_after_".$meta['siblings']['prev']['tag']." ";
  503. }
  504.  
  505. if(!empty($meta['siblings']['next']['tag']))
  506. {
  507. $meta['el_class'] .= " el_before_".$meta['siblings']['next']['tag']." ";
  508. }
  509.  
  510.  
  511. $fullwidth = AviaBuilder::$full_el;
  512.  
  513. if(!empty($meta['this']['tag']) && !in_array( $meta['this']['tag'] , $fullwidth))
  514. {
  515. if(!empty( $meta['siblings']['next']['tag']) && in_array( $meta['siblings']['next']['tag'] , $fullwidth) )
  516. {
  517. $meta['siblings']['next'] = false;
  518. }
  519.  
  520. if(!empty( $meta['siblings']['prev']['tag']) && in_array( $meta['siblings']['prev']['tag'] , $fullwidth) )
  521. {
  522. $meta['siblings']['prev'] = false;
  523. }
  524. }
  525.  
  526.  
  527. //single element without siblings
  528.  
  529. if(empty($meta['siblings']['next']) && empty($meta['siblings']['prev']))
  530. {
  531. $meta['el_class'] .= " avia-builder-el-no-sibling ";
  532. }
  533. else if(empty($meta['siblings']['next'])) //last element within section, column or page
  534. {
  535. $meta['el_class'] .= " avia-builder-el-last ";
  536. }
  537. else if(empty($meta['siblings']['prev'])) //first element within section, column or page
  538. {
  539. $meta['el_class'] .= " avia-builder-el-first ";
  540. }
  541.  
  542. //if the shortcode was added without beeing a builder element (eg button within layerslider) reset all styles for that shortcode and make sure it is marked as a fake element
  543. if( empty($meta['this']['tag'] ) || $shortcodename != $meta['this']['tag'] || ShortcodeHelper::$is_direct_call || $fake )
  544. {
  545. // increment theme shortcodes only, because these are in the shorcode tree
  546. if( in_array( $shortcodename, ShortcodeHelper::$allowed_shortcodes ) && ShortcodeHelper::$is_direct_call )
  547. {
  548. ShortcodeHelper::$direct_calls++;
  549. }
  550.  
  551. $fake = true;
  552. $meta['el_class'] = '';
  553. }
  554.  
  555. //fake is set when we manually call one shortcode inside another
  556. if( ! $fake )
  557. {
  558. ShortcodeHelper::$shortcode_index ++;
  559. }
  560. }
  561.  
  562. if( isset( $atts['custom_class'] ) )
  563. {
  564. $meta['el_class'] .= ' ' . $atts['custom_class'];
  565. $meta['custom_class'] = $atts['custom_class'];
  566. }
  567.  
  568. if( ! isset( $meta['custom_markup'] ) )
  569. {
  570. $meta['custom_markup'] = '';
  571. }
  572.  
  573. /**
  574. * a fallback only to avoid undefined index notice
  575. */
  576. if( ! isset( $meta['index'] ) )
  577. {
  578. $meta['index'] = ( $fake || ! is_valid ) ? 0 : ShortcodeHelper::$shortcode_index;
  579. }
  580.  
  581. $meta = apply_filters( 'avf_template_builder_shortcode_meta', $meta, $atts, $content, $shortcodename );
  582.  
  583.  
  584. /**
  585. * if the element is disabled do load a notice for admins but do not show the info for other visitors)
  586. */
  587. if( empty( $this->builder->disabled_assets[ $this->config['shortcode'] ] ) || empty( $this->config['disabling_allowed'] ) )
  588. {
  589. $out = $this->shortcode_handler($atts, $content, $shortcodename, $meta);
  590. }
  591. else if( current_user_can( 'edit_posts' ) )
  592. {
  593. $default_msg = '<strong>'.__( 'Admin notice for:', 'avia_framework' )."</strong><br>".
  594. $this->config['name']."<br><br>".
  595. __( 'This element was disabled in your theme settings. You can activate it here:', 'avia_framework' )."<br>".
  596. '<a target="_blank" href="'.admin_url( 'admin.php?page=avia#goto_performance' ).'">'.__( 'Performance Settings', 'avia_framework' )."</a>";
  597.  
  598. $msg = isset( $this->config['shortcode_disabled_msg'] ) ? $this->config['shortcode_disabled_msg'] : $default_msg;
  599. $out = "<span class='av-shortcode-disabled-notice'>{$msg}</span>";
  600. }
  601. else
  602. {
  603. $out = '';
  604. }
  605.  
  606. /**
  607. * Also allows to manipulate internal variables if necessary
  608. *
  609. * @since 4.5.4
  610. * @return array
  611. */
  612. $args = array( $out, $this, $atts, $content, $shortcodename, $fake );
  613. $out = apply_filters_ref_array( 'avf_in_shortcode_handler_prepare_content', array( &$args ) );
  614.  
  615. return $out[0];
  616. }
  617.  
  618.  
  619.  
  620. /**
  621. * additional config vars that are set automatically
  622. */
  623. protected function extra_config()
  624. {
  625. $this->config['php_class'] = get_class($this);
  626.  
  627. if(empty($this->config['drag-level'])) $this->config['drag-level'] = 3;
  628. if(empty($this->config['drop-level'])) $this->config['drop-level'] = -1;
  629.  
  630.  
  631. //if we got elements for the popup editor activate it
  632. if(method_exists($this, 'popup_elements') && is_admin())
  633. {
  634. $this->popup_elements();
  635.  
  636. if(!empty($this->elements))
  637. {
  638. $this->config['popup_editor'] = true;
  639.  
  640. $this->extra_config_element_iterator($this->elements);
  641.  
  642. if(!empty($this->config['modal_on_load']))
  643. {
  644. //remove any duplicate values
  645. $this->config['modal_on_load'] = array_unique($this->config['modal_on_load']);
  646. }
  647. }
  648. }
  649.  
  650. }
  651.  
  652.  
  653. /**
  654. * register shortcode and if available nested shortcode
  655. */
  656. protected function register_shortcodes()
  657. {
  658. if(isset($_REQUEST['params']['_ajax_nonce'])) $_REQUEST['_ajax_nonce'] = $_REQUEST['params']['_ajax_nonce'];
  659.  
  660. //the check is only necessary when $_REQUEST['text'] is set which means we want to show a preview that could be manipulated from outside
  661. if(!is_admin() || empty($_REQUEST['text']) || ( !empty($_POST['avia_request']) && check_ajax_referer('avia_nonce_loader', '_ajax_nonce' ) ) )
  662. {
  663. add_shortcode( $this->config['shortcode'], array(&$this, 'shortcode_handler_prepare'));
  664.  
  665. if(!empty($this->config['shortcode_nested']))
  666. {
  667. foreach($this->config['shortcode_nested'] as $nested)
  668. {
  669. if( method_exists($this, $nested) )
  670. {
  671. add_shortcode( $nested, array(&$this, $nested));
  672. }
  673. else if(!shortcode_exists($nested))
  674. {
  675. add_shortcode( $nested, '__return_false'); /*wordpress 4.0.1 fix that. without the shortcode registered to a function the attributes get messed up*/
  676. }
  677.  
  678. }
  679. }
  680. }
  681. }
  682.  
  683.  
  684.  
  685. /**
  686. * helper function to iterate recursively over element and subelement trees.
  687. */
  688. protected function extra_config_element_iterator($elements)
  689. {
  690. //check for js functions that need to be executed on popup window load
  691. foreach($elements as $element)
  692. {
  693. switch($element['type'])
  694. {
  695. case "mailchimp_list": $this->config['modal_on_load'][] = 'modal_load_mailchimp'; break;
  696. case "multi_input": $this->config['modal_on_load'][] = 'modal_load_multi_input'; break;
  697. case "tab_container": $this->config['modal_on_load'][] = 'modal_load_tabs'; break;
  698. case "tiny_mce": $this->config['modal_on_load'][] = 'modal_load_tiny_mce'; break;
  699. case "colorpicker": $this->config['modal_on_load'][] = 'modal_load_colorpicker'; break;
  700. case "datepicker": $this->config['modal_on_load'][] = 'modal_load_datepicker'; break;
  701. case "table": $this->config['modal_on_load'][] = 'modal_load_tablebuilder'; break;
  702. case "modal_group":
  703. $this->config['modal_on_load'][] = 'modal_start_sorting';
  704. $this->config['modal_on_load'][] = 'modal_tab_functions';
  705. $this->config['modal_on_load'][] = 'modal_hotspot_helper';
  706. $this->extra_config_element_iterator($element['subelements']);
  707. break;
  708. }
  709.  
  710. if(!empty($element['modal_on_load'])) //manually load a script
  711. {
  712. $this->config['modal_on_load'][] = $element['modal_on_load'];
  713. }
  714. }
  715. }
  716.  
  717.  
  718.  
  719. /**
  720. * filter and action hooks
  721. */
  722. protected function actions_and_filters()
  723. {
  724. add_filter('avia_show_shortcode_button', array($this,'add_backend_button'));
  725.  
  726. //ajax action for elements with modal window editor
  727. if(!empty($this->config['popup_editor']))
  728. {
  729. add_action('wp_ajax_avia_ajax_'.$this->config['shortcode'], array($this,'popup_editor'));
  730.  
  731. if(!empty($this->config['shortcode_nested']))
  732. {
  733. foreach($this->config['shortcode_nested'] as $sc)
  734. {
  735. add_action('wp_ajax_avia_ajax_'.$sc, array($this,'popup_editor'));
  736. }
  737. }
  738. }
  739.  
  740.  
  741. }
  742.  
  743.  
  744. /**
  745. * function that checks the popup_elements configuration array of a shortcode and sets an array that tells the builder class which resources to load
  746. */
  747. protected function create_asset_array()
  748. {
  749. if(!empty($this->elements))
  750. {
  751. foreach ($this->elements as $element)
  752. {
  753. if( $element['type'] == 'iconfont')
  754. {
  755. AviaBuilder::$resources_to_load['font'] = $element;
  756. }
  757. }
  758. }
  759. }
  760.  
  761. /**
  762. * add buttons for the backend
  763. */
  764. public function add_backend_button($buttons)
  765. {
  766. $buttons[] = $this->config;
  767. return $buttons;
  768. }
  769.  
  770.  
  771. /**
  772. * function that sets the default values and passes them to the user defined editor element
  773. * which in turn returns the array with the properties to render a new AviaBuilder Canvas Element
  774. */
  775. public function prepare_editor_element($content = false, $args = array())
  776. {
  777. //set the default content unless it was already passed
  778. if($content === false)
  779. {
  780. $content = $this->get_default_content($content);
  781. }
  782.  
  783. //set the default arguments unless they were already passed
  784. if(empty($args))
  785. {
  786. $args = $this->get_default_args($args);
  787. }
  788.  
  789. if(isset($args['content'])) unset($args['content']);
  790.  
  791. $params['content'] = $content;
  792. $params['args'] = $args;
  793. $params['data'] = isset($this->config['modal_data']) ? $this->config['modal_data'] : array();
  794.  
  795.  
  796. /**
  797. * Fetch the parameter array from the child classes editor_element function which should describe the html code.
  798. * Some elements can return a string value
  799. */
  800. $params = $this->editor_element($params);
  801.  
  802. /**
  803. * Since 4.2.1 we have $this->config['self_closing'] = 'yes'|'no'
  804. * Now we can use this to remove any content here and do not need to do this in each element seperatly in $this->editor_element
  805. */
  806. if( is_array( $params ) && $this->is_self_closing() )
  807. {
  808. $params['content'] = null;
  809. }
  810.  
  811. // pass the parameters to the create_sortable_editor_element unless a different function for execution was set.
  812. // if the function is set to "false" we asume that the output is final
  813. if(!isset($this->config['html_renderer']))
  814. {
  815. $this->config['html_renderer'] = "create_sortable_editor_element";
  816. }
  817.  
  818. if($this->config['html_renderer'] != false)
  819. {
  820. $output = call_user_func(array($this, $this->config['html_renderer']) , $params );
  821. }
  822. else
  823. {
  824. $output = $params;
  825. }
  826.  
  827. return $output;
  828. }
  829.  
  830. /**
  831. * add a custom css class to each element
  832. */
  833. public function avia_custom_class_for_element($elements)
  834. {
  835. $elements[] = array(
  836. "name" => __("Custom Css Class",'avia_framework' ),
  837. "desc" => __("Add a custom css class for the element here. Make sure to only use allowed characters (latin characters, underscores, dashes and numbers)",'avia_framework' ),
  838. "id" => "custom_class",
  839. "type" => "input",
  840. "std" => "");
  841.  
  842. return $elements;
  843. }
  844.  
  845. /**
  846. * add a custom field for the background of the preview
  847. */
  848. public function avia_custom_preview_bg($elements)
  849. {
  850. $elements[] = array(
  851. "id" => "admin_preview_bg",
  852. "type" => "hidden",
  853. "std" => "");
  854.  
  855. return $elements;
  856. }
  857.  
  858.  
  859.  
  860.  
  861.  
  862.  
  863. /**
  864. * default code to create a sortable item for your editor
  865. */
  866. public function create_sortable_editor_element($params)
  867. {
  868.  
  869. $extraClass = "";
  870. $defaults = array('class'=>'avia_default_container', 'innerHtml'=>'');
  871. $params = array_merge($defaults, $params);
  872. extract($params);
  873.  
  874. if(empty($data)) $data = array();
  875.  
  876. $data['shortcodehandler'] = $this->config['shortcode'];
  877. $data['modal_title'] = $this->config['name'];
  878. $data['modal_ajax_hook'] = $this->config['shortcode'];
  879. $data['dragdrop-level'] = $this->config['drag-level'];
  880. $data['allowed-shortcodes'] = $this->config['shortcode'];
  881. $data['preview'] = ! empty( $this->config['preview'] ) ? $this->config['preview'] : 0;
  882. $data['preview_scale'] = ! empty( $this->config['preview_scale'] ) ? $this->config['preview_scale'] : 'noscale';
  883. $data['closing_tag'] = $this->is_self_closing() ? 'no' : 'yes';
  884.  
  885. if(isset($this->config['shortcode_nested']))
  886. {
  887. $data['allowed-shortcodes'] = $this->config['shortcode_nested'];
  888. $data['allowed-shortcodes'][] = $this->config['shortcode'];
  889. $data['allowed-shortcodes'] = implode(",",$data['allowed-shortcodes']);
  890. }
  891.  
  892. if(!empty($this->config['modal_on_load']))
  893. {
  894. $data['modal_on_load'] = $this->config['modal_on_load'];
  895. }
  896.  
  897. $data = apply_filters('avb_backend_editor_element_data_filter', $data);
  898. $dataString = AviaHelper::create_data_string($data);
  899.  
  900. $output = "<div class='avia_sortable_element avia_pop_class ".$class." ".$this->config['shortcode']." av_drag' ".$dataString.">";
  901. $output .= "<div class='avia_sorthandle menu-item-handle'>";
  902.  
  903. if(!empty($this->config['popup_editor']))
  904. {
  905. $extraClass = 'avia-edit-element';
  906. $output .= "<a class='$extraClass' href='#edit-element' title='".__('Edit Element','avia_framework' )."'>edit</a>";
  907. }
  908.  
  909. $output .= "<a class='avia-save-element' href='#save-element' title='".__('Save Element as Template','avia_framework' )."'>+</a>";
  910. $output .= "<a class='avia-delete' href='#delete' title='".__('Delete Element','avia_framework' )."'>x</a>";
  911. $output .= "<a class='avia-clone' href='#clone' title='".__('Clone Element','avia_framework' )."' >".__('Clone Element','avia_framework' )."</a></div>";
  912.  
  913. $output .= "<div class='avia_inner_shortcode $extraClass'>";
  914. $output .= $innerHtml;
  915.  
  916.  
  917.  
  918. $output .= "<textarea data-name='text-shortcode' cols='20' rows='4'>".ShortcodeHelper::create_shortcode_by_array($this->config['shortcode'], $content, $args)."</textarea>";
  919. $output .= "</div></div>";
  920.  
  921. return $output;
  922. }
  923.  
  924.  
  925.  
  926. /**
  927. * helper function executed by aviaShortcodeTemplate::popup_editor that extracts the attributes from the shortcode and then merges the values into the options array
  928. *
  929. * @param array $elements
  930. * @return array $elements
  931. */
  932. public function set_default_values($elements)
  933. {
  934. $shortcode = !empty($_POST['params']['shortcode']) ? $_POST['params']['shortcode'] : "";
  935.  
  936.  
  937. if($shortcode)
  938. {
  939. //will extract the shortcode into $_POST['extracted_shortcode']
  940. $this->builder->text_to_interface($shortcode);
  941.  
  942. //the main shortcode (which is always the last array item) will be stored in $extracted_shortcode
  943. $extracted_shortcode = end($_POST['extracted_shortcode']);
  944.  
  945. //if the $_POST['extracted_shortcode'] has more than one items we are dealing with nested shortcodes
  946. $multi_content = count($_POST['extracted_shortcode']);
  947.  
  948. //proceed if the main shortcode has either arguments or content
  949. if(!empty($extracted_shortcode['attr']) || !empty($extracted_shortcode['content']))
  950. {
  951. if(empty($extracted_shortcode['attr'])) $extracted_shortcode['attr'] = array();
  952. if(isset($extracted_shortcode['content'])) $extracted_shortcode['attr']['content'] = $extracted_shortcode['content'];
  953.  
  954. //iterate over each array item and check if we already got a value
  955. foreach($elements as &$element)
  956. {
  957. if(isset($element['id']) && isset($extracted_shortcode['attr'][$element['id']]))
  958. {
  959. //make sure that each element of the popup can access the other values of the shortcode. necessary for hidden elements
  960. $element['shortcode_data'] = $extracted_shortcode['attr'];
  961.  
  962. //if the item has subelements the std value has to be an array
  963. if(isset($element['subelements']))
  964. {
  965. $element['std'] = array();
  966.  
  967. for ($i = 0; $i < $multi_content - 1; $i++)
  968. {
  969. $element['std'][$i] = $_POST['extracted_shortcode'][$i]['attr'];
  970. $element['std'][$i]['content'] = $_POST['extracted_shortcode'][$i]['content'];
  971. }
  972. }
  973. else
  974. {
  975. $element['std'] = stripslashes($extracted_shortcode['attr'][$element['id']]);
  976. }
  977.  
  978.  
  979.  
  980. }
  981. else
  982. {
  983. if($element['type'] == "checkbox") $element['std'] = "";
  984. }
  985. }
  986. }
  987. }
  988.  
  989. return $elements;
  990. }
  991.  
  992.  
  993. /**
  994. * helper function executed that extracts the std values from the options array and creates a shortcode argument array
  995. *
  996. * @param array $args
  997. * @return array $args
  998. */
  999. public function get_default_args($args = array())
  1000. {
  1001. /**
  1002. * PHP 7.0 fix: ensure we have an array, otherwise we recieve notices
  1003. * if shortcode is used without params (e.g. for a fallback situation) we get an empty string and not an array
  1004. */
  1005. if( ! is_array( $args) )
  1006. {
  1007. $args = array();
  1008. }
  1009.  
  1010. if(!empty($this->elements))
  1011. {
  1012. foreach($this->elements as $element)
  1013. {
  1014. if(isset($element['std']) && isset($element['id']))
  1015. {
  1016. $args[$element['id']] = $element['std'];
  1017. }
  1018. }
  1019.  
  1020. $this->default_args = $args;
  1021. }
  1022. return $args;
  1023. }
  1024.  
  1025. /**
  1026. * helper function that gets the default value of the content element
  1027. *
  1028. * @param array $content
  1029. * @return array $args
  1030. */
  1031. public function get_default_content($content = "")
  1032. {
  1033. if(!empty($this->elements))
  1034. {
  1035. //if we didnt iterate over the arguments array yet do it now
  1036. if(empty($this->default_args))
  1037. {
  1038. $this->get_default_args();
  1039. }
  1040.  
  1041. //if there is a content element already thats the value. if not try to fetch the std value
  1042. if(!isset($this->default_args['content']))
  1043. {
  1044. foreach($this->elements as $element)
  1045. {
  1046. if(isset($element['std']) && isset($element['id']) && $element['id'] == "content")
  1047. {
  1048. $content = $element['std'];
  1049. }
  1050. }
  1051. }
  1052. else
  1053. {
  1054. $content = $this->default_args['content'];
  1055. }
  1056. }
  1057.  
  1058. //if the content is an array we got a nested shortcode
  1059. if(is_array($content))
  1060. {
  1061. $string_content = "";
  1062.  
  1063. foreach($content as $c)
  1064. {
  1065. $content = $this->is_nested_self_closing( $this->config['shortcode_nested'][0] ) ? null : '';
  1066. $string_content .= trim( ShortcodeHelper::create_shortcode_by_array( $this->config['shortcode_nested'][0], $content, $c ) )."\n";
  1067. }
  1068.  
  1069. $content = $string_content;
  1070. }
  1071.  
  1072.  
  1073. return $content;
  1074. }
  1075.  
  1076. /**
  1077. * Returns the width of the element.
  1078. * Override in derived class if the element does not have fullwidth (currently only supported for layout elements avia_sc_columns and avia_sc_cell.
  1079. * All other elements are full screen.
  1080. *
  1081. * @since 4.2.1
  1082. * @return float
  1083. */
  1084. public function get_element_width()
  1085. {
  1086. return 1.0;
  1087. }
  1088.  
  1089.  
  1090. /**
  1091. * Returns if an element needs a closing tag or is self closing.
  1092. * The implementation is for backwards compatibility and also for third party elements.
  1093. *
  1094. * @since 4.2.1
  1095. * @return boolean
  1096. */
  1097. public function is_self_closing()
  1098. {
  1099. /**
  1100. * If property is set return this value
  1101. */
  1102. if( ! empty( $this->config['self_closing'] ) && in_array( $this->config['self_closing'], array( 'yes', 'no' ) ) )
  1103. {
  1104. return $this->config['self_closing'] == 'yes';
  1105. }
  1106.  
  1107. /**
  1108. * Elements should return NULL for content when self closing
  1109. */
  1110. $params = $this->editor_element( array() );
  1111. $this->config['self_closing'] = array_key_exists( 'content', $params ) && is_null( $params['content'] ) ? 'yes' : 'no';
  1112.  
  1113. return $this->config['self_closing'] == 'yes';
  1114. }
  1115.  
  1116. /**
  1117. * Returns false by default.
  1118. * Override in a child class if you need to change this behaviour.
  1119. *
  1120. * @since 4.2.1
  1121. * @param string $shortcode
  1122. * @return boolean
  1123. */
  1124. public function is_nested_self_closing( $shortcode )
  1125. {
  1126. return false;
  1127. }
  1128.  
  1129. /**
  1130. * helper function for the editor_element function that creates the correct classnames
  1131. * and data attributes for an AviaBuilder Canvas element in your backend
  1132. *
  1133. * @param string $classNames a string with classnames separated by coma
  1134. * @param array $args
  1135. * @return string
  1136. */
  1137. public function class_by_arguments($classNames, $args, $classNamesOnly = false)
  1138. {
  1139. $classNames = str_replace(" ","",$classNames);
  1140. $dataString = "data-update_class_with='$classNames' ";
  1141. $classNames = explode(',',$classNames);
  1142. $classString = "class='";
  1143. $classes = "";
  1144.  
  1145. foreach($classNames as $class)
  1146. {
  1147. $replace = is_array($args) ? $args[$class] : $args;
  1148. $classes .= "avia-$class-".str_replace(" ","_",$replace)." ";
  1149. }
  1150.  
  1151. if($classNamesOnly) return $classes;
  1152. return $classString .$classes."' ".$dataString;
  1153. }
  1154.  
  1155.  
  1156.  
  1157. /**
  1158. * helper function for the editor_element function that tells the javascript were to insert the returned content
  1159. * you need to provide a "key" and a template
  1160. *
  1161. * @param string $key a string with argument or content key eg: img_src
  1162. * @param string $template a template that tells which content to insert. eg: <img src='{{img_src}}' />
  1163. * @return string
  1164. */
  1165.  
  1166. function update_template($key, $template)
  1167. {
  1168. $data = "data-update_with='$key' data-update_template='".htmlentities($template, ENT_QUOTES, get_bloginfo( 'charset' ))."'";
  1169. return $data;
  1170. }
  1171.  
  1172.  
  1173.  
  1174.  
  1175.  
  1176. } // end class
  1177.  
  1178. } // end if !class_exists
Advertisement
Add Comment
Please, Sign In to add comment