PageRenderTime 24ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/wr-pagebuilder/core/converter/converter.php

https://gitlab.com/hunt9310/ras
PHP | 438 lines | 203 code | 68 blank | 167 comment | 48 complexity | 6b0966852bbce6549def3e918ed3c4da MD5 | raw file
  1. <?php
  2. /**
  3. * @version $Id$
  4. * @package WR_PageBuilder
  5. * @author WooRockets Team <support@woorockets.com>
  6. * @copyright Copyright (C) 2012 WooRockets.com. All Rights Reserved.
  7. * @license GNU/GPL v2 or later http://www.gnu.org/licenses/gpl-2.0.html
  8. *
  9. * Websites: http://www.woorockets.com
  10. */
  11. /**
  12. * Base data converter class.
  13. *
  14. * Base class for converting data from other page builder plugin to WR PageBuilder.
  15. *
  16. * @since 2.3.0
  17. */
  18. class WR_Pb_Converter {
  19. /**
  20. * An array to hold instantiated converter object.
  21. *
  22. * @var array
  23. */
  24. private static $_instance = array();
  25. /**
  26. * Converter file name without extension.
  27. *
  28. * @var string
  29. */
  30. protected $converter = '';
  31. /**
  32. * Pattern to match shortcode tag.
  33. *
  34. * @var string
  35. */
  36. protected static $pattern = '\[(\[?)([a-zA-Z0-9\-_]+)(?![\w-])([^\]\/]*(?:\/(?!\])[^\]\/]*)*?)(?:(\/)\]|\](?:([^\[]*+(?:\[(?!\/\2\])[^\[]*+)*+)\[\/\2\])?)(\]?)';
  37. /**
  38. * Data mapping.
  39. *
  40. * Must be declared in following format:
  41. *
  42. * $mapping = array(
  43. * 'row' => array(
  44. * 'tag' => wr_row',
  45. * 'attributes' => array(
  46. * 'width' => 'width',
  47. * ),
  48. * ),
  49. * 'col' => array(
  50. * 'tag' => 'wr_column',
  51. * 'attributes' => array(
  52. * 'weight' => 'span',
  53. * ),
  54. * ),
  55. * 'widget' => array(
  56. * 'tag' => 'wr_widget',
  57. * 'attributes' => array(
  58. * 'title' => 'el_title',
  59. * 'class' => 'widget_id',
  60. * ),
  61. * ),
  62. * );
  63. *
  64. * @var array
  65. */
  66. protected $mapping = array();
  67. /**
  68. * Get an instance of specified converter class.
  69. *
  70. * @param string $converter Converter to instantiate.
  71. * @param WP_Post $post WordPress's post object.
  72. *
  73. * @return object
  74. */
  75. public static function get_converter( $converter, $post ) {
  76. // Instantiate converter class only if not already instantiated
  77. if ( ! isset( self::$_instance[ $converter ] ) ) {
  78. // Preset variable
  79. self::$_instance[ $converter ] = false;
  80. // Check if converter class exists
  81. $class = explode( '-', $converter );
  82. $class = array_map( 'ucfirst', $class );
  83. $class = 'WR_Pb_Converter_' . implode( '_', $class );
  84. // Try to autoload converter class
  85. if ( ! class_exists( $class, true ) ) {
  86. $class = __CLASS__;
  87. }
  88. // Instantiate converter class
  89. self::$_instance[ $converter ] = new $class( $post );
  90. // Set converter name if class constructor forgot doing this
  91. if ( empty( self::$_instance[ $converter ]->converter ) ) {
  92. self::$_instance[ $converter ]->converter = $converter;
  93. }
  94. // Store post to converter object if class constructor forgot doing this
  95. if ( ! isset( self::$_instance[ $converter ]->post ) ) {
  96. self::$_instance[ $converter ]->post = $post;
  97. }
  98. }
  99. return self::$_instance[ $converter ];
  100. }
  101. /**
  102. * Get all available data converters.
  103. *
  104. * @return array
  105. */
  106. public static function get_converters() {
  107. global $post;
  108. // Initialize WordPress Filesystem Abstraction
  109. $wp_filesystem = WR_Pb_Init_File_System::get_instance();
  110. // Get available data converter
  111. $files = $wp_filesystem->dirlist( dirname( __FILE__ ) );
  112. $converters = array();
  113. foreach ( $files as $file ) {
  114. if ( 'converter.php' != $file['name'] ) {
  115. $converter = substr( $file['name'], 0, -4 );
  116. // Generate data converter class name
  117. $class = explode( '-', $converter );
  118. $class = array_map( 'ucfirst', $class );
  119. $class = 'WR_Pb_Converter_' . implode( '_', $class );
  120. if ( class_exists( $class, true ) ) {
  121. // Check if there is data to convert
  122. if ( call_user_func( array( $class, 'check' ), $post ) ) {
  123. $converters[ $converter ] = ucwords( str_replace( '-', ' ', substr( $file['name'], 0, -4 ) ) );
  124. }
  125. }
  126. }
  127. }
  128. // Allow 3rd-party plugin to hook into data conversion
  129. $converters = apply_filters( 'wr_pb_get_data_converters', $converters );
  130. return $converters;
  131. }
  132. /**
  133. * Constructor
  134. *
  135. * @param WP_Post $post WordPress's post object.
  136. *
  137. * @return void
  138. */
  139. public function __construct( $post ) {
  140. // Store post to this object
  141. $this->post = $post;
  142. }
  143. /**
  144. * Parse data of other page builder from given post then convert to WR PageBuilder data.
  145. *
  146. * @return mixed Boolean TRUE on success, error message on failure.
  147. */
  148. public function convert() {
  149. // Preset parsed data and mapping array
  150. $data = array();
  151. $mapping = $this->mapping;
  152. // Trigger filters to parse other page builder data from given post and get mapping array
  153. $data = apply_filters( "wr_pb_parse_data_{$this->converter}", $data , $this );
  154. $mapping = apply_filters( "wr_pb_map_data_{$this->converter}" , $mapping, $this );
  155. // Check if we have data to do conversion
  156. if ( is_string( $data ) && empty( $data ) ) {
  157. return sprintf(
  158. __( 'Not found any %s data.', WR_PBL ),
  159. ucwords( str_replace( '-', ' ', $this->converter ) )
  160. );
  161. }
  162. // If data is not affected by any filter, process normally
  163. if ( ! count( $data ) ) {
  164. // Find all shortcodes
  165. if ( preg_match_all( '/' . self::$pattern . '/s', $this->post->post_content, $matches, PREG_SET_ORDER ) && count( $matches ) ) {
  166. foreach ( $matches as $match ) {
  167. $data[] = $this->process_shortcode( $match );
  168. }
  169. }
  170. }
  171. // Convert parsed data to WR PageBuilder data
  172. $data = $this->convert_elements( $data, $mapping );
  173. if ( empty( $data ) ) {
  174. return sprintf(
  175. __( 'Not found any %s data.', WR_PBL ),
  176. ucwords( str_replace( '-', ' ', $this->converter ) )
  177. );
  178. }
  179. // Check if we should backup current post
  180. if ( isset( $_REQUEST['backup_data'] ) && $_REQUEST['backup_data'] ) {
  181. $backup = (array) $this->post;
  182. $backup['ID' ] = null;
  183. $backup['post_title' ] = "{$this->post->post_title} (backup)";
  184. $backup['post_status'] = 'pending';
  185. $backup['post_name' ] = "{$this->post->post_name}-backup";
  186. // Create new post
  187. if ( ! ( $id = wp_insert_post( $backup ) ) ) {
  188. return __( 'Cannot backup current post.', WR_PBL );
  189. }
  190. // Update new post GUID
  191. $backup['ID' ] = $id;
  192. $backup['guid'] = preg_replace( '/\?page_id=\d+/', "?page_id={$id}", $backup['guid'] );
  193. wp_insert_post( $backup );
  194. // Duplicate post meta also
  195. global $wpdb, $table_prefix;
  196. $post_meta = $wpdb->get_results( "SELECT * FROM {$table_prefix}postmeta WHERE post_id = {$this->post->ID};" );
  197. $ignore = array( '_edit_last', '_edit_lock', '_post_restored_from' );
  198. foreach ( $post_meta as $meta ) {
  199. if ( ! in_array( $meta->meta_key, $ignore ) ) {
  200. $wpdb->query( "INSERT INTO {$table_prefix}postmeta (post_id, meta_key, meta_value) VALUES ({$id}, '{$meta->meta_key}', '{$meta->meta_value}');" );
  201. }
  202. }
  203. }
  204. // Prepare to save post with new data
  205. $post = (array) $this->post;
  206. $post['post_content'] = $data;
  207. // Check if we should unpublish current post
  208. if ( isset( $_REQUEST['do'] ) && 'convert-and-publish' != $_REQUEST['do'] ) {
  209. $post['post_status'] = 'pending';
  210. }
  211. // Update current post
  212. if ( ! wp_insert_post( $post ) ) {
  213. return __( 'Cannot update current post.', WR_PBL );
  214. }
  215. // Update post meta to activate WR PageBuilder tab
  216. update_post_meta( $this->post->ID, '_wr_page_active_tab', 1 );
  217. // Store WR PageBuilder data to post meta also
  218. if ( ! update_post_meta( $this->post->ID, '_wr_page_builder_content', $data ) ) {
  219. return __( 'Cannot store WR PageBuilder data to post meta.', WR_PBL );
  220. }
  221. // Trigger action to finalize data conversion
  222. do_action( "wr_pb_after_convert_{$this->converter}_data", $post, $this->post );
  223. return $post['ID'];
  224. }
  225. /**
  226. * Process matched shortcode to array of data that is able to convert to WR PageBuilder shortcode later.
  227. *
  228. * @param array $match Data of matched shortcode.
  229. *
  230. * @return array
  231. */
  232. protected function process_shortcode( $match ) {
  233. // Prepare shortcode data
  234. $tag = $match[2];
  235. $attrs = shortcode_parse_atts( $match[3] );
  236. $children = $match[5];
  237. if ( ! empty( $children ) ) {
  238. // Process all nested shortcode
  239. if ( preg_match_all( '/' . self::$pattern . '/s', $children, $matches, PREG_SET_ORDER ) && count( $matches ) ) {
  240. $children = array();
  241. foreach ( $matches as $match ) {
  242. $children[] = $this->process_shortcode( $match );
  243. }
  244. }
  245. }
  246. return array(
  247. 'tag' => $tag,
  248. 'attributes' => empty( $attrs ) ? array() : $attrs,
  249. 'children' => $children,
  250. );
  251. }
  252. /**
  253. * Convert parsed data of other page builder elements to WR PageBuilder elements.
  254. *
  255. * @param array $elements Other page builder elements.
  256. * @param array $mapping Data mapping array.
  257. *
  258. * @return string
  259. */
  260. protected function convert_elements( $elements, $mapping ) {
  261. $result = '';
  262. if ( is_array( $elements ) && is_array( $mapping ) ) {
  263. // Map parsed data to WR PageBuilder data
  264. foreach ( $elements as $element ) {
  265. // Allow shortcode filterable before doing conversion
  266. $element = apply_filters( "wr_pb_convert_{$element['tag']}_shortcode", $element );
  267. // Prepare shortcode children
  268. $children = '';
  269. if ( isset( $element['children'] ) ) {
  270. if ( is_string( $element['children'] ) ) {
  271. $children = $element['children'];
  272. } elseif ( is_array( $element['children'] ) && count( $element['children'] ) ) {
  273. // Map children recursively
  274. $children = $this->convert_elements( $element['children'], $mapping );
  275. }
  276. }
  277. // Process shortcode data
  278. if ( isset( $mapping[ $element['tag'] ] ) && is_array( $mapping[ $element['tag'] ] ) ) {
  279. // Map shortcode tag
  280. $name = isset( $mapping[ $element['tag'] ]['tag'] ) ? $mapping[ $element['tag'] ]['tag'] : $element['tag'];
  281. // Map shortcode parameters
  282. $params = array();
  283. if ( isset( $mapping[ $element['tag'] ]['attributes'] ) && is_array( $mapping[ $element['tag'] ]['attributes'] ) ) {
  284. if ( isset( $element['attributes'] ) && is_array( $element['attributes'] ) ) {
  285. foreach ( $element['attributes'] as $k => $v ) {
  286. if ( isset( $mapping[ $element['tag'] ]['attributes'][ $k ] ) ) {
  287. $params[ $mapping[ $element['tag'] ]['attributes'][ $k ] ] = $v;
  288. }
  289. }
  290. }
  291. }
  292. // Prepare children for WR Widget shortcode
  293. if ( 'wr_widget' == $name ) {
  294. $children = $this->to_query_string( $element['attributes'], '', array( 'widget_id' ) );
  295. }
  296. } else {
  297. // There is no mapping available for this shortcode, wrap it inside an WR Text element
  298. $name = 'wr_text';
  299. $params = array(
  300. 'el_title' => isset( $element['attributes']['title' ] ) ? $element['attributes']['title' ] : $element['tag'],
  301. 'css_suffix' => isset( $element['attributes']['el_class'] ) ? $element['attributes']['el_class'] : '',
  302. );
  303. // Generate nested shortcode
  304. $nested_shortcode = $this->to_shortcode_tag( $element['tag'], isset( $element['attributes'] ) ? $element['attributes'] : array(), $children );
  305. // Do nested shortcode
  306. $children = do_shortcode( $nested_shortcode );
  307. $children = trim( $children );
  308. if ( empty( $children ) ) {
  309. $children = $nested_shortcode;
  310. }
  311. }
  312. // Generate shortcode tag
  313. $result .= $this->to_shortcode_tag( $name, $params, $children );
  314. }
  315. }
  316. return $result;
  317. }
  318. /**
  319. * Convert associative array to query string.
  320. *
  321. * @param array $params An associative array of parameters.
  322. * @param string $namespace A namespace.
  323. * @param array $exclude Array of key should be ignored.
  324. *
  325. * @return string
  326. */
  327. protected function to_query_string( $params, $namespace = '', $exclude = array() ) {
  328. // Preset array of name/value pairs
  329. $pairs = array();
  330. if ( ! is_array( $params ) ) {
  331. return;
  332. }
  333. foreach ( $params as $k => $v ) {
  334. if ( in_array( $k, $exclude ) ) {
  335. continue;
  336. }
  337. // Generate variable name
  338. $name = empty( $namespace ) ? $k : "{$namespace}[{$k}]";
  339. // Generate name/value pair
  340. if ( is_array( $v ) ) {
  341. $pairs[] = $this->to_query_string( $v, $name );
  342. } else {
  343. $pairs[] = "{$name}={$v}";
  344. }
  345. }
  346. return implode( '&', $pairs );
  347. }
  348. /**
  349. * Generate shortcode tag.
  350. *
  351. * @param string $name Shortcode tag name.
  352. * @param array $params Shortcode parameters.
  353. * @param string $child Shortcode children.
  354. *
  355. * @return string
  356. */
  357. protected function to_shortcode_tag( $name, $params = array(), $child = '' ) {
  358. // Generate shortcode tag
  359. $shortcode = '[' . esc_attr( $name );
  360. // Generate shortcode parameters
  361. foreach ( $params as $k => $v ) {
  362. if ( is_string( $v ) && ! empty( $v ) ) {
  363. $shortcode .= ' ' . esc_attr( $k ) . '="' . esc_attr( $v ) . '"';
  364. }
  365. }
  366. // Finalize shortcode tag
  367. $shortcode .= ']' . $child . '[/' . $name . ']';
  368. return $shortcode;
  369. }
  370. }