loop.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962
  1. <?php
  2. if ( ! defined( 'ABSPATH' ) ) {
  3. die( '-1' );
  4. }
  5. /**
  6. * @param $settings
  7. * @param $value
  8. *
  9. * @return string
  10. * @since 4.2
  11. */
  12. function vc_loop_form_field( $settings, $value ) {
  13. $query_builder = new VcLoopSettings( $value );
  14. $params = $query_builder->getContent();
  15. $loop_info = '';
  16. $parsed_value = array();
  17. if ( is_array( $params ) ) {
  18. foreach ( $params as $key => $param ) {
  19. $param_value_render = vc_loop_get_value( $param );
  20. if ( ! empty( $param_value_render ) ) {
  21. $parsed_value[] = $key . ':' . ( is_array( $param['value'] ) ? implode( ',', $param['value'] ) : $param['value'] );
  22. $loop_info .= ' <b>' . $query_builder->getLabel( $key ) . '</b>: ' . $param_value_render . ';';
  23. }
  24. }
  25. }
  26. if ( ! isset( $settings['settings'] ) ) {
  27. $settings['settings'] = array();
  28. }
  29. return '<div class="vc_loop">' . '<input name="' . esc_attr( $settings['param_name'] ) . '" class="wpb_vc_param_value ' . esc_attr( $settings['param_name'] . ' ' . $settings['type'] ) . '_field" type="hidden" value="' . esc_attr( join( '|', $parsed_value ) ) . '"/>' . '<a href="javascript:;" class="button vc_loop-build ' . esc_attr( $settings['param_name'] ) . '_button" data-settings="' . rawurlencode( wp_json_encode( $settings['settings'] ) ) . '">' . esc_html__( 'Build query', 'js_composer' ) . '</a>' . '<div class="vc_loop-info">' . $loop_info . '</div>' . '</div>';
  30. }
  31. /**
  32. * @param $param
  33. *
  34. * @return string
  35. * @since 4.2
  36. */
  37. function vc_loop_get_value( $param ) {
  38. $value = array();
  39. $selected_values = (array) $param['value'];
  40. if ( isset( $param['options'] ) && is_array( $param['options'] ) ) {
  41. foreach ( $param['options'] as $option ) {
  42. if ( is_array( $option ) && isset( $option['value'] ) ) {
  43. if ( in_array( ( ( '-' === $option['action'] ? '-' : '' ) . $option['value'] ), $selected_values, true ) ) {
  44. $value[] = $option['action'] . $option['name'];
  45. }
  46. } elseif ( is_array( $option ) && isset( $option[0] ) ) {
  47. if ( in_array( $option[0], $selected_values, true ) ) {
  48. $value[] = $option[1];
  49. }
  50. } elseif ( in_array( $option, $selected_values, true ) ) {
  51. $value[] = $option;
  52. }
  53. }
  54. } else {
  55. $value[] = $param['value'];
  56. }
  57. return implode( ', ', $value );
  58. }
  59. /**
  60. * Parses loop settings and creates WP_Query according to manual
  61. * @since 4.2
  62. * @link http://codex.wordpress.org/Class_Reference/WP_Query
  63. */
  64. class VcLoopQueryBuilder {
  65. /**
  66. * @since 4.2
  67. * @var array
  68. */
  69. protected $args = array(
  70. 'post_status' => 'publish',
  71. // show only published posts #1098
  72. );
  73. /**
  74. * @param $data
  75. * @since 4.2
  76. *
  77. */
  78. public function __construct( $data ) {
  79. foreach ( $data as $key => $value ) {
  80. $method = 'parse_' . $key;
  81. if ( method_exists( $this, $method ) ) {
  82. $this->$method( $value );
  83. }
  84. }
  85. }
  86. /**
  87. * Pages count
  88. * @param $value
  89. * @since 4.2
  90. *
  91. */
  92. protected function parse_size( $value ) {
  93. $this->args['posts_per_page'] = 'All' === $value ? - 1 : (int) $value;
  94. }
  95. /**
  96. * Sorting field
  97. * @param $value
  98. * @since 4.2
  99. *
  100. */
  101. protected function parse_order_by( $value ) {
  102. $this->args['orderby'] = $value;
  103. }
  104. /**
  105. * Sorting order
  106. * @param $value
  107. * @since 4.2
  108. *
  109. */
  110. protected function parse_order( $value ) {
  111. $this->args['order'] = $value;
  112. }
  113. /**
  114. * By post types
  115. * @param $value
  116. * @since 4.2
  117. *
  118. */
  119. protected function parse_post_type( $value ) {
  120. $this->args['post_type'] = $this->stringToArray( $value );
  121. }
  122. /**
  123. * By author
  124. * @param $value
  125. * @since 4.2
  126. *
  127. */
  128. protected function parse_authors( $value ) {
  129. $this->args['author'] = $value;
  130. }
  131. /**
  132. * By categories
  133. * @param $value
  134. * @since 4.2
  135. *
  136. */
  137. protected function parse_categories( $value ) {
  138. $this->args['cat'] = $value;
  139. }
  140. /**
  141. * By taxonomies
  142. * @param $value
  143. * @since 4.2
  144. *
  145. */
  146. protected function parse_tax_query( $value ) {
  147. $terms = $this->stringToArray( $value );
  148. if ( empty( $this->args['tax_query'] ) ) {
  149. $this->args['tax_query'] = array( 'relation' => 'AND' );
  150. }
  151. $negative_term_list = array();
  152. foreach ( $terms as $term ) {
  153. if ( (int) $term < 0 ) {
  154. $negative_term_list[] = abs( $term );
  155. }
  156. }
  157. $not_in = array();
  158. $in = array();
  159. $terms = get_terms( VcLoopSettings::getTaxonomies(), array( 'include' => array_map( 'abs', $terms ) ) );
  160. foreach ( $terms as $t ) {
  161. if ( in_array( (int) $t->term_id, $negative_term_list, true ) ) {
  162. $not_in[ $t->taxonomy ][] = $t->term_id;
  163. } else {
  164. $in[ $t->taxonomy ][] = $t->term_id;
  165. }
  166. }
  167. foreach ( $in as $taxonomy => $terms ) {
  168. $this->args['tax_query'][] = array(
  169. 'field' => 'term_id',
  170. 'taxonomy' => $taxonomy,
  171. 'terms' => $terms,
  172. 'operator' => 'IN',
  173. );
  174. }
  175. foreach ( $not_in as $taxonomy => $terms ) {
  176. $this->args['tax_query'][] = array(
  177. 'field' => 'term_id',
  178. 'taxonomy' => $taxonomy,
  179. 'terms' => $terms,
  180. 'operator' => 'NOT IN',
  181. );
  182. }
  183. }
  184. /**
  185. * By tags ids
  186. * @param $value
  187. * @since 4.2
  188. *
  189. */
  190. protected function parse_tags( $value ) {
  191. $in = $not_in = array();
  192. $tags_ids = $this->stringToArray( $value );
  193. foreach ( $tags_ids as $tag ) {
  194. $tag = (int) $tag;
  195. if ( $tag < 0 ) {
  196. $not_in[] = abs( $tag );
  197. } else {
  198. $in[] = $tag;
  199. }
  200. }
  201. $this->args['tag__in'] = $in;
  202. $this->args['tag__not_in'] = $not_in;
  203. }
  204. /**
  205. * By posts ids
  206. * @param $value
  207. * @since 4.2
  208. *
  209. */
  210. protected function parse_by_id( $value ) {
  211. $in = $not_in = array();
  212. $ids = $this->stringToArray( $value );
  213. foreach ( $ids as $id ) {
  214. $id = (int) $id;
  215. if ( $id < 0 ) {
  216. $not_in[] = abs( $id );
  217. } else {
  218. $in[] = $id;
  219. }
  220. }
  221. $this->args['post__in'] = $in;
  222. $this->args['post__not_in'] = $not_in;
  223. }
  224. /**
  225. * @param $id
  226. * @since 4.2
  227. *
  228. */
  229. public function excludeId( $id ) {
  230. if ( ! isset( $this->args['post__not_in'] ) ) {
  231. $this->args['post__not_in'] = array();
  232. }
  233. if ( is_array( $id ) ) {
  234. $this->args['post__not_in'] = array_merge( $this->args['post__not_in'], $id );
  235. } else {
  236. $this->args['post__not_in'][] = $id;
  237. }
  238. }
  239. /**
  240. * Converts string to array. Filters empty arrays values
  241. * @param $value
  242. *
  243. * @return array
  244. * @since 4.2
  245. *
  246. */
  247. protected function stringToArray( $value ) {
  248. $valid_values = array();
  249. $list = preg_split( '/\,[\s]*/', $value );
  250. foreach ( $list as $v ) {
  251. if ( strlen( $v ) > 0 ) {
  252. $valid_values[] = $v;
  253. }
  254. }
  255. return $valid_values;
  256. }
  257. /**
  258. * @return array
  259. */
  260. public function build() {
  261. return array(
  262. $this->args,
  263. new WP_Query( $this->args ),
  264. );
  265. }
  266. }
  267. /**
  268. * Class VcLoopSettings
  269. * @since 4.2
  270. */
  271. class VcLoopSettings {
  272. // Available parts of loop for WP_Query object.
  273. /**
  274. * @since 4.2
  275. * @var array
  276. */
  277. protected $content = array();
  278. /**
  279. * @since 4.2
  280. * @var array
  281. */
  282. protected $parts;
  283. /**
  284. * @since 4.2
  285. * @var array
  286. */
  287. protected $query_parts = array(
  288. 'size',
  289. 'order_by',
  290. 'order',
  291. 'post_type',
  292. 'authors',
  293. 'categories',
  294. 'tags',
  295. 'tax_query',
  296. 'by_id',
  297. );
  298. public $settings = array();
  299. /**
  300. * @param $value
  301. * @param array $settings
  302. * @since 4.2
  303. *
  304. */
  305. public function __construct( $value, $settings = array() ) {
  306. $this->parts = array(
  307. 'size' => esc_html__( 'Post count', 'js_composer' ),
  308. 'order_by' => esc_html__( 'Order by', 'js_composer' ),
  309. 'order' => esc_html__( 'Sort order', 'js_composer' ),
  310. 'post_type' => esc_html__( 'Post types', 'js_composer' ),
  311. 'authors' => esc_html__( 'Author', 'js_composer' ),
  312. 'categories' => esc_html__( 'Categories', 'js_composer' ),
  313. 'tags' => esc_html__( 'Tags', 'js_composer' ),
  314. 'tax_query' => esc_html__( 'Taxonomies', 'js_composer' ),
  315. 'by_id' => esc_html__( 'Individual posts/pages', 'js_composer' ),
  316. );
  317. $this->settings = $settings;
  318. // Parse loop string
  319. $data = $this->parseData( $value );
  320. foreach ( $this->query_parts as $part ) {
  321. $value = isset( $data[ $part ] ) ? $data[ $part ] : '';
  322. $locked = 'true' === $this->getSettings( $part, 'locked' );
  323. // Predefined value check.
  324. if ( ! is_null( $this->getSettings( $part, 'value' ) ) && $this->replaceLockedValue( $part ) && ( true === $locked || 0 === strlen( (string) $value ) ) ) {
  325. $value = $this->settings[ $part ]['value'];
  326. } elseif ( ! is_null( $this->getSettings( $part, 'value' ) ) && ! $this->replaceLockedValue( $part ) && ( true === $locked || 0 === strlen( (string) $value ) ) ) {
  327. $value = implode( ',', array_unique( explode( ',', $value . ',' . $this->settings[ $part ]['value'] ) ) );
  328. }
  329. // Find custom method for parsing
  330. if ( method_exists( $this, 'parse_' . $part ) ) {
  331. $method = 'parse_' . $part;
  332. $this->content[ $part ] = $this->$method( $value );
  333. } else {
  334. $this->content[ $part ] = $this->parseString( $value );
  335. }
  336. // Set locked if value is locked by settings
  337. if ( $locked ) {
  338. $this->content[ $part ]['locked'] = true;
  339. }
  340. if ( 'true' === $this->getSettings( $part, 'hidden' ) ) {
  341. $this->content[ $part ]['hidden'] = true;
  342. }
  343. }
  344. }
  345. /**
  346. * @param $part
  347. *
  348. * @return bool
  349. * @since 4.2
  350. */
  351. protected function replaceLockedValue( $part ) {
  352. return in_array( $part, array(
  353. 'size',
  354. 'order_by',
  355. 'order',
  356. ), true );
  357. }
  358. /**
  359. * @param $key
  360. *
  361. * @return mixed
  362. * @since 4.2
  363. */
  364. public function getLabel( $key ) {
  365. return isset( $this->parts[ $key ] ) ? $this->parts[ $key ] : $key;
  366. }
  367. /**
  368. * @param $part
  369. * @param $name
  370. *
  371. * @return null
  372. * @since 4.2
  373. */
  374. public function getSettings( $part, $name ) {
  375. $settings_exists = isset( $this->settings[ $part ] ) && is_array( $this->settings[ $part ] );
  376. return $settings_exists && isset( $this->settings[ $part ][ $name ] ) ? $this->settings[ $part ][ $name ] : null;
  377. }
  378. /**
  379. * @param $value
  380. *
  381. * @return array
  382. * @since 4.2
  383. */
  384. public function parseString( $value ) {
  385. return array( 'value' => $value );
  386. }
  387. /**
  388. * @param $value
  389. * @param array $options
  390. *
  391. * @return array
  392. * @since 4.2
  393. */
  394. protected function parseDropDown( $value, $options = array() ) {
  395. return array(
  396. 'value' => $value,
  397. 'options' => $options,
  398. );
  399. }
  400. /**
  401. * @param $value
  402. * @param array $options
  403. *
  404. * @return array
  405. * @since 4.2
  406. */
  407. protected function parseMultiSelect( $value, $options = array() ) {
  408. return array(
  409. 'value' => explode( ',', trim( $value, ',' ) ),
  410. 'options' => $options,
  411. );
  412. }
  413. /**
  414. * @param $value
  415. *
  416. * @return array
  417. * @since 4.2
  418. */
  419. public function parse_order_by( $value ) {
  420. return $this->parseDropDown( $value, array(
  421. array(
  422. 'date',
  423. esc_html__( 'Date', 'js_composer' ),
  424. ),
  425. 'ID',
  426. array(
  427. 'author',
  428. esc_html__( 'Author', 'js_composer' ),
  429. ),
  430. array(
  431. 'title',
  432. esc_html__( 'Title', 'js_composer' ),
  433. ),
  434. array(
  435. 'modified',
  436. esc_html__( 'Modified', 'js_composer' ),
  437. ),
  438. array(
  439. 'rand',
  440. esc_html__( 'Random', 'js_composer' ),
  441. ),
  442. array(
  443. 'comment_count',
  444. esc_html__( 'Comment count', 'js_composer' ),
  445. ),
  446. array(
  447. 'menu_order',
  448. esc_html__( 'Menu order', 'js_composer' ),
  449. ),
  450. ) );
  451. }
  452. /**
  453. * @param $value
  454. *
  455. * @return array
  456. * @since 4.2
  457. */
  458. public function parse_order( $value ) {
  459. return $this->parseDropDown( $value, array(
  460. array(
  461. 'ASC',
  462. esc_html__( 'Ascending', 'js_composer' ),
  463. ),
  464. array(
  465. 'DESC',
  466. esc_html__( 'Descending', 'js_composer' ),
  467. ),
  468. ) );
  469. }
  470. /**
  471. * @param $value
  472. *
  473. * @return array
  474. * @since 4.2
  475. */
  476. public function parse_post_type( $value ) {
  477. $options = array();
  478. $args = array(
  479. 'public' => true,
  480. );
  481. $post_types = get_post_types( $args );
  482. foreach ( $post_types as $post_type ) {
  483. if ( 'attachment' !== $post_type ) {
  484. $options[] = $post_type;
  485. }
  486. }
  487. return $this->parseMultiSelect( $value, $options );
  488. }
  489. /**
  490. * @param $value
  491. *
  492. * @return array
  493. * @since 4.2
  494. */
  495. public function parse_authors( $value ) {
  496. $options = $not_in = array();
  497. if ( empty( $value ) ) {
  498. return $this->parseMultiSelect( $value, $options );
  499. }
  500. $list = explode( ',', $value );
  501. foreach ( $list as $id ) {
  502. if ( (int) $id < 0 ) {
  503. $not_in[] = abs( $id );
  504. }
  505. }
  506. $users = get_users( array( 'include' => array_map( 'abs', $list ) ) );
  507. foreach ( $users as $user ) {
  508. $options[] = array(
  509. 'value' => (string) $user->ID,
  510. 'name' => $user->data->user_nicename,
  511. 'action' => in_array( (int) $user->ID, $not_in, true ) ? '-' : '+',
  512. );
  513. }
  514. return $this->parseMultiSelect( $value, $options );
  515. }
  516. /**
  517. * @param $value
  518. *
  519. * @return array
  520. * @since 4.2
  521. */
  522. public function parse_categories( $value ) {
  523. $options = $not_in = array();
  524. if ( empty( $value ) ) {
  525. return $this->parseMultiSelect( $value, $options );
  526. }
  527. $list = explode( ',', $value );
  528. foreach ( $list as $id ) {
  529. if ( (int) $id < 0 ) {
  530. $not_in[] = abs( $id );
  531. }
  532. }
  533. $list = get_categories( array( 'include' => array_map( 'abs', $list ) ) );
  534. foreach ( $list as $obj ) {
  535. $options[] = array(
  536. 'value' => (string) $obj->cat_ID,
  537. 'name' => $obj->cat_name,
  538. 'action' => in_array( (int) $obj->cat_ID, $not_in, true ) ? '-' : '+',
  539. );
  540. }
  541. if ( empty( $list ) ) {
  542. $value = '';
  543. }
  544. return $this->parseMultiSelect( $value, $options );
  545. }
  546. /**
  547. * @param $value
  548. *
  549. * @return array
  550. * @since 4.2
  551. */
  552. public function parse_tags( $value ) {
  553. $options = $not_in = array();
  554. if ( empty( $value ) ) {
  555. return $this->parseMultiSelect( $value, $options );
  556. }
  557. $list = explode( ',', $value );
  558. foreach ( $list as $id ) {
  559. if ( (int) $id < 0 ) {
  560. $not_in[] = abs( $id );
  561. }
  562. }
  563. $list = get_tags( array( 'include' => array_map( 'abs', $list ) ) );
  564. foreach ( $list as $obj ) {
  565. $options[] = array(
  566. 'value' => (string) $obj->term_id,
  567. 'name' => $obj->name,
  568. 'action' => in_array( (int) $obj->term_id, $not_in, true ) ? '-' : '+',
  569. );
  570. }
  571. return $this->parseMultiSelect( $value, $options );
  572. }
  573. /**
  574. * @param $value
  575. *
  576. * @return array
  577. * @since 4.2
  578. */
  579. public function parse_tax_query( $value ) {
  580. $options = $not_in = array();
  581. if ( empty( $value ) ) {
  582. return $this->parseMultiSelect( $value, $options );
  583. }
  584. $list = explode( ',', $value );
  585. foreach ( $list as $id ) {
  586. if ( (int) $id < 0 ) {
  587. $not_in[] = abs( $id );
  588. }
  589. }
  590. $list = get_terms( self::getTaxonomies(), array( 'include' => array_map( 'abs', $list ) ) );
  591. foreach ( $list as $obj ) {
  592. $options[] = array(
  593. 'value' => (string) $obj->term_id,
  594. 'name' => $obj->name,
  595. 'action' => in_array( (int) $obj->term_id, $not_in, true ) ? '-' : '+',
  596. );
  597. }
  598. return $this->parseMultiSelect( $value, $options );
  599. }
  600. /**
  601. * @param $value
  602. *
  603. * @return array
  604. * @since 4.2
  605. */
  606. public function parse_by_id( $value ) {
  607. $options = $not_in = array();
  608. if ( empty( $value ) ) {
  609. return $this->parseMultiSelect( $value, $options );
  610. }
  611. $list = explode( ',', $value );
  612. foreach ( $list as $id ) {
  613. if ( (int) $id < 0 ) {
  614. $not_in[] = abs( $id );
  615. }
  616. }
  617. $list = get_posts( array(
  618. 'post_type' => 'any',
  619. 'include' => array_map( 'abs', $list ),
  620. ) );
  621. foreach ( $list as $obj ) {
  622. $options[] = array(
  623. 'value' => (string) $obj->ID,
  624. 'name' => $obj->post_title,
  625. 'action' => in_array( (int) $obj->ID, $not_in, true ) ? '-' : '+',
  626. );
  627. }
  628. return $this->parseMultiSelect( $value, $options );
  629. }
  630. /**
  631. * @since 4.2
  632. */
  633. public function render() {
  634. echo wp_json_encode( $this->content );
  635. }
  636. /**
  637. * @return array
  638. * @since 4.2
  639. */
  640. public function getContent() {
  641. return $this->content;
  642. }
  643. /**
  644. * get list of taxonomies which has no tags and categories items.
  645. * @return array
  646. * @since 4.2
  647. * @static
  648. */
  649. public static function getTaxonomies() {
  650. $taxonomy_exclude = (array) apply_filters( 'get_categories_taxonomy', 'category' );
  651. $taxonomy_exclude[] = 'post_tag';
  652. $taxonomies = array();
  653. foreach ( get_taxonomies() as $taxonomy ) {
  654. if ( ! in_array( $taxonomy, $taxonomy_exclude, true ) ) {
  655. $taxonomies[] = $taxonomy;
  656. }
  657. }
  658. return $taxonomies;
  659. }
  660. /**
  661. * @param $settings
  662. *
  663. * @return string
  664. * @since 4.2
  665. */
  666. public static function buildDefault( $settings ) {
  667. if ( ! isset( $settings['settings'] ) || ! is_array( $settings['settings'] ) ) {
  668. return '';
  669. }
  670. $value = '';
  671. foreach ( $settings['settings'] as $key => $val ) {
  672. if ( isset( $val['value'] ) ) {
  673. $value .= ( empty( $value ) ? '' : '|' ) . $key . ':' . $val['value'];
  674. }
  675. }
  676. return $value;
  677. }
  678. /**
  679. * @param $query
  680. * @param bool $exclude_id
  681. *
  682. * @return array
  683. * @since 4.2
  684. */
  685. public static function buildWpQuery( $query, $exclude_id = false ) {
  686. $data = self::parseData( $query );
  687. $query_builder = new VcLoopQueryBuilder( $data );
  688. if ( $exclude_id ) {
  689. $query_builder->excludeId( $exclude_id );
  690. }
  691. return $query_builder->build();
  692. }
  693. /**
  694. * @param $value
  695. *
  696. * @return array
  697. * @since 4.2
  698. */
  699. public static function parseData( $value ) {
  700. $data = array();
  701. $values_pairs = preg_split( '/\|/', $value );
  702. foreach ( $values_pairs as $pair ) {
  703. if ( ! empty( $pair ) ) {
  704. list( $key, $value ) = preg_split( '/\:/', $pair );
  705. $data[ $key ] = $value;
  706. }
  707. }
  708. return $data;
  709. }
  710. }
  711. /**
  712. * Suggestion list for wp_query field
  713. * Class VcLoopSuggestions
  714. * @since 4.2
  715. */
  716. class VcLoopSuggestions {
  717. /**
  718. * @since 4.2
  719. * @var array
  720. */
  721. protected $content = array();
  722. /**
  723. * @since 4.2
  724. * @var array
  725. */
  726. protected $exclude = array();
  727. /**
  728. * @since 4.2
  729. * @var
  730. */
  731. protected $field;
  732. /**
  733. * @param $field
  734. * @param $query
  735. * @param $exclude
  736. *
  737. * @since 4.2
  738. */
  739. public function __construct( $field, $query, $exclude ) {
  740. $this->exclude = explode( ',', $exclude );
  741. $method_name = 'get_' . preg_replace( '/_out$/', '', $field );
  742. if ( method_exists( $this, $method_name ) ) {
  743. $this->$method_name( $query );
  744. }
  745. }
  746. /**
  747. * @param $query
  748. *
  749. * @since 4.2
  750. */
  751. public function get_authors( $query ) {
  752. $args = ! empty( $query ) ? array(
  753. 'search' => '*' . $query . '*',
  754. 'search_columns' => array( 'user_nicename' ),
  755. ) : array();
  756. if ( ! empty( $this->exclude ) ) {
  757. $args['exclude'] = $this->exclude;
  758. }
  759. $users = get_users( $args );
  760. foreach ( $users as $user ) {
  761. $this->content[] = array(
  762. 'value' => (string) $user->ID,
  763. 'name' => (string) $user->data->user_nicename,
  764. );
  765. }
  766. }
  767. /**
  768. * @param $query
  769. *
  770. * @since 4.2
  771. */
  772. public function get_categories( $query ) {
  773. $args = ! empty( $query ) ? array( 'search' => $query ) : array();
  774. if ( ! empty( $this->exclude ) ) {
  775. $args['exclude'] = $this->exclude;
  776. }
  777. $categories = get_categories( $args );
  778. foreach ( $categories as $cat ) {
  779. $this->content[] = array(
  780. 'value' => (string) $cat->cat_ID,
  781. 'name' => $cat->cat_name,
  782. );
  783. }
  784. }
  785. /**
  786. * @param $query
  787. *
  788. * @since 4.2
  789. */
  790. public function get_tags( $query ) {
  791. $args = ! empty( $query ) ? array( 'search' => $query ) : array();
  792. if ( ! empty( $this->exclude ) ) {
  793. $args['exclude'] = $this->exclude;
  794. }
  795. $tags = get_tags( $args );
  796. foreach ( $tags as $tag ) {
  797. $this->content[] = array(
  798. 'value' => (string) $tag->term_id,
  799. 'name' => $tag->name,
  800. );
  801. }
  802. }
  803. /**
  804. * @param $query
  805. *
  806. * @since 4.2
  807. */
  808. public function get_tax_query( $query ) {
  809. $args = ! empty( $query ) ? array( 'search' => $query ) : array();
  810. if ( ! empty( $this->exclude ) ) {
  811. $args['exclude'] = $this->exclude;
  812. }
  813. $tags = get_terms( VcLoopSettings::getTaxonomies(), $args );
  814. foreach ( $tags as $tag ) {
  815. $this->content[] = array(
  816. 'value' => $tag->term_id,
  817. 'name' => $tag->name . ' (' . $tag->taxonomy . ')',
  818. );
  819. }
  820. }
  821. /**
  822. * @param $query
  823. *
  824. * @since 4.2
  825. */
  826. public function get_by_id( $query ) {
  827. $args = ! empty( $query ) ? array(
  828. 's' => $query,
  829. 'post_type' => 'any',
  830. ) : array( 'post_type' => 'any' );
  831. if ( ! empty( $this->exclude ) ) {
  832. $args['exclude'] = $this->exclude;
  833. }
  834. $args['ignore_sticky_posts'] = true;
  835. $posts = get_posts( $args );
  836. foreach ( $posts as $post ) {
  837. $this->content[] = array(
  838. 'value' => $post->ID,
  839. 'name' => $post->post_title,
  840. );
  841. }
  842. }
  843. /**
  844. * @since 4.2
  845. */
  846. public function render() {
  847. echo wp_json_encode( $this->content );
  848. }
  849. }
  850. /**
  851. * Build WP_Query object from query string.
  852. * String created by loop controllers
  853. *
  854. * @param $query
  855. * @param bool $exclude_id
  856. *
  857. * @return array
  858. * @since 4.2
  859. */
  860. function vc_build_loop_query( $query, $exclude_id = false ) {
  861. return VcLoopSettings::buildWpQuery( $query, $exclude_id );
  862. }
  863. /**
  864. * @since 4.2
  865. */
  866. function vc_get_loop_suggestion() {
  867. vc_user_access()->checkAdminNonce()->validateDie()->wpAny( 'edit_posts', 'edit_pages' )->validateDie();
  868. $loop_suggestions = new VcLoopSuggestions( vc_post_param( 'field' ), vc_post_param( 'query' ), vc_post_param( 'exclude' ) );
  869. $loop_suggestions->render();
  870. die();
  871. }
  872. /**
  873. * @since 4.2
  874. */
  875. function vc_get_loop_settings_json() {
  876. vc_user_access()->checkAdminNonce()->validateDie()->wpAny( 'edit_posts', 'edit_pages' )->validateDie();
  877. $loop_settings = new VcLoopSettings( vc_post_param( 'value' ), vc_post_param( 'settings' ) );
  878. $loop_settings->render();
  879. die();
  880. }
  881. add_action( 'wp_ajax_wpb_get_loop_suggestion', 'vc_get_loop_suggestion' );
  882. add_action( 'wp_ajax_wpb_get_loop_settings', 'vc_get_loop_settings_json' );
  883. /**
  884. * @since 4.2
  885. */
  886. function vc_loop_include_templates() {
  887. require_once vc_path_dir( 'TEMPLATES_DIR', 'params/loop/templates.html' );
  888. }
  889. add_action( 'admin_footer', 'vc_loop_include_templates' );