class-static-functions.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. <?php
  2. class Booked_WC_Functions {
  3. private static $calls = array();
  4. private function __construct() {
  5. }
  6. public static function get_products() {
  7. // check if the products has been already retrieved
  8. // dont make additional requests
  9. if (
  10. !isset(self::$calls['products'])
  11. || empty(self::$calls['products']['objects'])
  12. ) {
  13. $products = get_posts(array(
  14. 'posts_per_page' => 200, // there shouldn't be more than 200 product
  15. 'post_type' => 'product',
  16. 'meta_query' => array(
  17. array(
  18. 'key' => '_booked_appointment',
  19. 'compare' => '=',
  20. 'value' => 'yes'
  21. )
  22. ),
  23. 'suppress_filters' => false
  24. ));
  25. $options = array();
  26. foreach ($products as $product) {
  27. $options[$product->ID] = apply_filters('the_title', $product->post_title);
  28. }
  29. self::$calls['products']['objects'] = $products;
  30. self::$calls['products']['options'] = $options;
  31. }
  32. return self::$calls['products'];
  33. }
  34. public static function get_calendar_page_info( $calendar_id = null ) {
  35. global $wpdb;
  36. $pages = false;
  37. $shortcode = '[booked-calendar';
  38. if ( $calendar_id ) {
  39. $calendar_id = intval($calendar_id);
  40. $search_query = "`post_type` = 'page' AND `post_content` LIKE '%calendar={$calendar_id}%'";
  41. $search_query .= " OR `post_type` = 'page' AND `post_content` LIKE '%calendar=\"{$calendar_id}\"%'";
  42. $search_query .= ' OR `post_type` = "page" AND `post_content` LIKE "%calendar=\'{'.$calendar_id.'}\'%"';
  43. $query = "SELECT * FROM `{$wpdb->posts}` WHERE ".$search_query;
  44. $pages = $wpdb->get_results($query);
  45. }
  46. if ( !$pages ) {
  47. $shortcode = '[booked-calendar';
  48. $query = "SELECT * FROM `{$wpdb->posts}` WHERE `post_type` = 'page' AND `post_content` LIKE '{$shortcode}'";
  49. $pages = $wpdb->get_results($query);
  50. }
  51. if (!$pages){
  52. return;
  53. }
  54. // return the first found calendar
  55. return $pages[0];
  56. }
  57. public static function get_login_page_info() {
  58. global $wpdb;
  59. $query = "SELECT * FROM `{$wpdb->posts}`
  60. WHERE `post_type` = 'page'
  61. AND `post_content` LIKE '%[booked-login]%'";
  62. $pages = $wpdb->get_results($query);
  63. if ( !$pages ) {
  64. return;
  65. }
  66. // return the first found calendar
  67. return $pages[0];
  68. }
  69. public static function get_appointments_page_info() {
  70. global $wpdb;
  71. $query = "SELECT * FROM `{$wpdb->posts}`
  72. WHERE `post_type` = 'page'
  73. AND `post_content` LIKE '%[booked-appointments]%'";
  74. $pages = $wpdb->get_results($query);
  75. if ( !$pages ) {
  76. return;
  77. }
  78. // return the first found calendar
  79. return $pages[0];
  80. }
  81. public static function booked_new_appointment_form() {
  82. if (
  83. !empty($_POST['action'])
  84. && $_POST['action']==='booked_new_appointment_form'
  85. && !empty($_POST['source'])
  86. && $_POST['source']==='booked_wc_extension'
  87. ) {
  88. $booking_form = Booked_WC_Fragments::get_path('ajax-loaded/appointment', 'change-date');
  89. include($booking_form);
  90. exit;
  91. }
  92. }
  93. public static function get_calendar_id_from_post_request() {
  94. $calendar_id = (isset($_POST['calendar_id']) ? $_POST['calendar_id'] : false);
  95. $calendar_id = array($calendar_id);
  96. $calendar_id = array_map( 'intval', $calendar_id );
  97. $calendar_id = array_unique( $calendar_id );
  98. if ( !empty($calendar_id) && is_numeric($calendar_id[0]) ) {
  99. return $calendar_id[0];
  100. }
  101. return false;
  102. }
  103. public static function get_custom_fields() {
  104. $calendar_id = self::get_calendar_id_from_post_request();
  105. $custom_fields = array();
  106. if ( $calendar_id ) {
  107. $custom_fields_option_name = 'booked_custom_fields_' . $calendar_id;
  108. $custom_fields = json_decode(stripslashes(get_option($custom_fields_option_name)),true);
  109. }
  110. if ( !$custom_fields ) {
  111. $custom_fields = json_decode(stripslashes(get_option('booked_custom_fields')),true);
  112. }
  113. return $custom_fields;
  114. }
  115. // filtrates the custom field values before creating a new appointment
  116. public static function booked_custom_field_data( $custom_field_data = array() ) {
  117. // get custom fields
  118. $custom_fields = self::get_custom_fields();
  119. if ( empty($custom_fields) ) {
  120. return $custom_field_data;
  121. }
  122. // handle the custom fields data
  123. $has_product = false;
  124. foreach($custom_fields as $key => $field) {
  125. $field_name = $field['name'];
  126. $field_parts = explode('---',$field_name);
  127. $field_type = $field_parts[0];
  128. $field_marker = '';
  129. if ( $field_type !== 'paid-service-label' ) {
  130. continue;
  131. }
  132. if ( !isset($_POST[$field_name]) || empty($_POST[$field_name]) ) {
  133. continue;
  134. }
  135. $field_title = $field['value'];
  136. $product_id = intval($_POST[$field_name]);
  137. $product = Booked_WC_Product::get($product_id);
  138. $end_of_string = explode('___',$field_parts[1]);
  139. $numbers_only = $end_of_string[0];
  140. $is_required = (isset($end_of_string[1]) ? true : false);
  141. // set product title
  142. $custom_field_data[$key]['value'] = esc_html($product->title);
  143. $field_marker .= 'product_id::' . $product_id;
  144. $option_name = str_replace($field_type, 'paid-service-variation', $field_name);
  145. if ( isset($_POST[$option_name]) && !empty($_POST[$option_name]) ) {
  146. $variation_id = intval($_POST[$option_name]);
  147. if ( isset($product->variations[$variation_id]) ) {
  148. $variation_details = $product->variations[$variation_id];
  149. $variation_title = $variation_details['variation_title'];
  150. // add variation value
  151. $custom_field_data[$key]['value'] .= '<br />[ ' . esc_html($variation_title) . ' ]';
  152. $field_marker .= '|variation_id::' . $variation_id;
  153. }
  154. }
  155. // add a marker containing the product ID
  156. // simple-product -> product_id::ID
  157. // variable-product -> product_id::ID|variation_id::ID
  158. $custom_field_data[$key]['value'] .= "<!-- {$field_marker} -->";
  159. $has_product = true;
  160. }
  161. if ( $has_product ) {
  162. // check for a calendar assignee
  163. $calendar_id = self::get_calendar_id_from_post_request();
  164. if ( $calendar_id ) {
  165. $term_meta = get_option( "taxonomy_{$calendar_id}" );
  166. $assignee_email = $term_meta['notifications_user_id'];
  167. if ( $assignee_email && ($usr=get_user_by('email', $assignee_email)) ) {
  168. $custom_field_data['booking-agent'] = array(
  169. 'label' => esc_attr__('Booking Agent', 'booked-woocommerce-payments'),
  170. 'value' => $usr->display_name
  171. );
  172. }
  173. }
  174. add_filter('booked_new_appointment_args', array('Booked_WC_Functions', 'booked_new_appointment_args_on_create'), 10, 1);
  175. }
  176. return $custom_field_data;
  177. }
  178. // filtrates the custom field values before creating a new appointment
  179. public static function booked_prepare_sending_reminder( $send = true, $appt_id = false ) {
  180. if ( empty( $appt_id ) ):
  181. return false;
  182. endif;
  183. if ( is_array( $appt_id ) ):
  184. return false;
  185. else:
  186. $custom_field_val = get_post_meta( $appt_id, '_cf_meta_value', true );
  187. $is_wc_order = strpos( $custom_field_val, '<!-- product_id' );
  188. if ( $is_wc_order ):
  189. $order = new Booked_WC_Appointment_Payment_Status( $appt_id );
  190. if ( !empty($order) && isset($order->is_paid) && !$order->is_paid ):
  191. return false;
  192. else:
  193. return true;
  194. endif;
  195. else:
  196. return true;
  197. endif;
  198. endif;
  199. return true;
  200. }
  201. // filtrates the appointment data before creating it
  202. // this hook should be called only in Booked_WC_Functions::booked_custom_field_data() only if product option is available
  203. public static function booked_new_appointment_args_on_create( $post_params=array() ) {
  204. // just in case, remove the filter
  205. remove_filter('booked_new_appointment_args', array('Booked_WC_Functions', 'booked_new_appointment_args_on_create'), 10, 1);
  206. // set post status to awaiting payment
  207. $post_params['post_status'] = BOOKED_WC_PLUGIN_PREFIX . 'awaiting';
  208. return $post_params;
  209. }
  210. // filtered the appointment information before populating to the calendar
  211. public static function booked_appointments_array( $appointments_array ) {
  212. return $appointments_array;
  213. }
  214. public static function booked_store_appointment_creation_date( $appointment_id=null ) {
  215. if ( !$appointment_id ) {
  216. return;
  217. }
  218. $current_time = current_time('timestamp');
  219. update_post_meta($appointment_id, '_' . BOOKED_WC_PLUGIN_PREFIX . 'time_created', $current_time);
  220. update_post_meta($appointment_id, '_' . BOOKED_WC_PLUGIN_PREFIX . 'date_created', date('Y-m-d H:i:s', $current_time));
  221. }
  222. public static function booked_get_custom_fields_information() {
  223. $custom_fields = self::get_custom_fields();
  224. $submission_values = array();
  225. $previous_field = false;
  226. $is_product = false;
  227. foreach($custom_fields as $field) {
  228. $field_name = $field['name'];
  229. $field_title = $field['value'];
  230. $field_parts = explode('---',$field_name);
  231. $field_type = $field_parts[0];
  232. switch ($field_type) {
  233. case 'paid-service-label':
  234. $is_product = true;
  235. $current_group_name = $field_title;
  236. break;
  237. case 'checkboxes-label':
  238. case 'radio-buttons-label':
  239. $is_product = false;
  240. $current_group_name = $field_title;
  241. break;
  242. case 'single-checkbox':
  243. case 'single-radio-button':
  244. $is_product = false;
  245. // Don't change the group name yet
  246. break;
  247. default:
  248. $is_product = false;
  249. $current_group_name = $field_title;
  250. break;
  251. }
  252. if ( $field_name===$previous_field ) {
  253. continue;
  254. }
  255. $previous_field = $field_name;
  256. if ( !isset($_POST[$field_name]) || empty($_POST[$field_name]) ) {
  257. continue;
  258. }
  259. // set regular field data and conitnue
  260. if ( !$is_product ) {
  261. $field_value = $_POST[$field_name];
  262. if (is_array($field_value)){
  263. $field_value = implode(', ',$field_value);
  264. }
  265. $submission_values[$current_group_name] = $field_value;
  266. continue;
  267. }
  268. // if the field is a product
  269. $product_id = intval($_POST[$field_name]);
  270. $product = Booked_WC_Product::get($product_id);
  271. $end_of_string = explode('___',$field_parts[1]);
  272. $numbers_only = $end_of_string[0];
  273. $is_required = (isset($end_of_string[1]) ? true : false);
  274. // set product title
  275. $submission_values[$current_group_name] = esc_html($product->title);
  276. $option_name = str_replace($field_type, 'paid-service-variation', $field_name);
  277. if ( isset($_POST[$option_name]) && !empty($_POST[$option_name]) ) {
  278. $variation_id = intval($_POST[$option_name]);
  279. if ( isset($product->variations[$variation_id]) ) {
  280. $variation_details = $product->variations[$variation_id];
  281. $variation_title = $variation_details['variation_title'];
  282. // add variation value
  283. $submission_values[$current_group_name] .= '<br />[ ' . esc_html($variation_title) . ' ]';
  284. }
  285. }
  286. }
  287. return $submission_values;
  288. }
  289. public static function booked_store_custom_fields_information( $appointment_id = null ) {
  290. if ( !$appointment_id ) {
  291. return;
  292. }
  293. $custom_fields = self::booked_get_custom_fields_information();
  294. $i = 0;
  295. $separator = '--SEP--';
  296. $meta_key = '_' . BOOKED_WC_PLUGIN_PREFIX . 'cfield_';
  297. foreach ($custom_fields as $field_label => $field_value) {
  298. $this_meta_key = $meta_key . strval($i);
  299. $value_to_store = $field_label . $separator . $field_value;
  300. // save the value
  301. update_post_meta($appointment_id, $this_meta_key, $value_to_store);
  302. $i++;
  303. }
  304. }
  305. public static function booked_new_appointment_created( $appointment_id = null ) {
  306. if ( is_admin() && isset($_POST['booked_form_type']) && $_POST['booked_form_type'] == 'admin' ):
  307. return;
  308. endif;
  309. $appointment_id = intval($appointment_id);
  310. try {
  311. $appointment = Booked_WC_Appointment::get($appointment_id);
  312. } catch (Exception $e) {
  313. return;
  314. }
  315. // check if the appointment has assigned products to it
  316. if ( !$appointment->products ) {
  317. return;
  318. }
  319. self::booked_store_custom_fields_information($appointment_id);
  320. $added_to_cart = Booked_WC_Cart::add_appointment($appointment_id);
  321. if ( !$added_to_cart ) {
  322. return;
  323. }
  324. }
  325. // add edit button to the front front end appointments listing
  326. public static function booked_shortcode_appointments_buttons( $appointment_id=null ) {
  327. // add change date and pay buttons
  328. $edit_button = Booked_WC_Fragments::get_path('appointment', 'buttons');
  329. include($edit_button);
  330. }
  331. public static function booked_shortcode_appointments_additional_information( $appointment_id=null ) {
  332. $additional_info = Booked_WC_Fragments::get_path('appointment', 'additional-info');
  333. include($additional_info);
  334. }
  335. // Don't show cancel button on appointment with products
  336. public static function booked_shortcode_appointments_allow_cancel($allow_cancel, $app_id) {
  337. $appointment = Booked_WC_Appointment::get(intval($app_id));
  338. return !$appointment->products;
  339. }
  340. // Add "booked_wc_awaiting" to the "Pending" status types
  341. public static function booked_admin_pending_post_status($statuses) {
  342. $statuses = array('draft','booked_wc_awaiting');
  343. return $statuses;
  344. }
  345. // remove delete button if order attached
  346. public static function booked_fea_shortcode_appointments_buttons($button_html, $appt_id) {
  347. $appointment = Booked_WC_Appointment::get(intval($appt_id));
  348. $status = get_post_status(intval($appt_id));
  349. if ($appointment->order_id):
  350. $status_class = $status !== 'publish' && $status !== 'future' ? 'pending' : 'approved';
  351. $button_html = '<div class="booked-fea-buttons">';
  352. $button_html .= ($status_class == 'pending' ? '<button data-appt-id="'.$appt_id.'" class="approve button button-primary">'.__('Approve','booked-frontend-agents').'</button>' : '');
  353. $button_html .= '</div>';
  354. return $button_html;
  355. else:
  356. return $button_html;
  357. endif;
  358. }
  359. public static function booked_button_book_appointment( $label ) {
  360. if (
  361. empty($_POST['app_id'])
  362. || empty($_POST['app_action'])
  363. || $_POST['app_action']!=='edit'
  364. || empty($_POST['source'])
  365. || $_POST['source']!=='booked_wc_extension'
  366. ) {
  367. return $label;
  368. }
  369. $appointment = Booked_WC_Appointment::get(intval($_POST['app_id']));
  370. $current_time = current_time('timestamp');
  371. // check if the date has been passed
  372. if ( $current_time > $appointment->timestamp ) {
  373. return $label;
  374. }
  375. return '<span class="button-text" >' . __('Choose New Date', 'booked-woocommerce-payments') . '</span></button></span>';
  376. }
  377. public static function booked_before_creating_appointment() {
  378. if ( is_admin() && isset($_POST['booked_form_type']) && $_POST['booked_form_type'] == 'admin' ):
  379. return;
  380. endif;
  381. $action = (isset($_POST['action']) &&!empty($_POST['action'])) ? $_POST['action'] : false;
  382. $app_id = (isset($_POST['app_id']) &&!empty($_POST['app_id'])) ? $_POST['app_id'] : false;
  383. $app_action = (isset($_POST['app_action']) &&!empty($_POST['app_action'])) ? $_POST['app_action'] : false;
  384. $source = (isset($_POST['source']) &&!empty($_POST['source'])) ? $_POST['source'] : false;
  385. if (
  386. (!$action || $action !== 'booked_add_appt')
  387. || (!$app_id || !intval($app_id))
  388. || (!$app_action || $app_action!=='edit')
  389. || (!$source || $source!=='booked_wc_extension')
  390. ) {
  391. return;
  392. }
  393. $appointment = Booked_WC_Appointment::get(intval($app_id));
  394. $current_time = current_time('timestamp');
  395. // check if the date has been passed
  396. if ( $current_time > $appointment->timestamp ) {
  397. return;
  398. }
  399. // remove cart functionality
  400. remove_action('booked_new_appointment_created', array('Booked_WC_Functions', 'booked_new_appointment_created'), 10, 1);
  401. // turn the wp_insert_post to act as wp_update_post
  402. add_filter('booked_new_appointment_args', array('Booked_WC_Functions', 'booked_new_appointment_args_on_date_change'), 10, 1);
  403. // dont allow to update the metas and the calendar term
  404. add_filter('booked_update_cf_meta_value', array('Booked_WC_Functions', 'return_false'), 10);
  405. add_filter('booked_update_appointment_calendar', array('Booked_WC_Functions', 'return_false'), 10);
  406. }
  407. public static function booked_wc_mailer_actions( $mailer_actions ){
  408. $send_upon_completion = self::booked_disable_confirmation_emails();
  409. if ( $send_upon_completion ):
  410. $mailer_actions[] = 'booked_wc_confirmation_email';
  411. $mailer_actions[] = 'booked_wc_admin_confirmation_email';
  412. return $mailer_actions;
  413. endif;
  414. return $mailer_actions;
  415. }
  416. public static function booked_disable_confirmation_emails(){
  417. if ( Booked_WC_Settings::get_option('email_confirmations') === 'after_complete' || Booked_WC_Settings::get_option('email_confirmations') === false ):
  418. return apply_filters( 'booked_disable_confirmation_emails', true );
  419. else:
  420. return apply_filters( 'booked_disable_confirmation_emails', false );
  421. endif;
  422. }
  423. public static function remove_default_emails() {
  424. $booked_wc_product = false;
  425. if (isset($_POST)):
  426. foreach($_POST as $var => $val):
  427. $var_parts = explode('---',$var);
  428. if (isset($var_parts[0]) && $var_parts[0] == 'paid-service-label'):
  429. $booked_wc_product = true;
  430. break;
  431. endif;
  432. endforeach;
  433. $booked_disable_confirmation_emails = self::booked_disable_confirmation_emails();
  434. if ( $booked_wc_product && $booked_disable_confirmation_emails ):
  435. remove_action( 'booked_confirmation_email', 'booked_mailer', 10 );
  436. remove_action( 'booked_admin_confirmation_email', 'booked_mailer', 10 );
  437. endif;
  438. else:
  439. return;
  440. endif;
  441. return;
  442. }
  443. // see wp_update_post()
  444. public static function booked_new_appointment_args_on_date_change( $post_args ) {
  445. $appointment_id = $_POST['app_id'];
  446. $appointment_obj = get_post($appointment_id, ARRAY_A);
  447. if ( !$appointment_obj ) {
  448. return 0;
  449. }
  450. $default_post_status = get_option('booked_new_appointment_default','draft');
  451. $post_args['ID'] = $_POST['app_id'];
  452. // Escape data pulled from DB.
  453. $appointment_obj = wp_slash($appointment_obj);
  454. // Passed post category list overwrites existing category list if not empty.
  455. $post_cats = $appointment_obj['post_category'];
  456. $post_args = array_merge($appointment_obj, $post_args);
  457. $post_args['post_category'] = $post_cats;
  458. // Drafts shouldn't be assigned a date unless explicitly done so by the user.
  459. $post_args['post_date'] = current_time('mysql');
  460. $post_args['post_date_gmt'] = '';
  461. // keep the awaiting payment status if the appointment is awaiting payment
  462. $app_detailed_obj = Booked_WC_Appointment::get(intval($appointment_id));
  463. $awaiting_payment_status = BOOKED_WC_PLUGIN_PREFIX . 'awaiting';
  464. if ($app_detailed_obj->products && !$app_detailed_obj->is_paid) {
  465. $post_args['post_status'] = $awaiting_payment_status;
  466. } else {
  467. $post_args['post_status'] = $default_post_status;
  468. }
  469. return $post_args;
  470. }
  471. public static function return_false() {
  472. return false;
  473. }
  474. public static function booked_admin_calendar_buttons_before($calendar_id, $appt_id, $status) {
  475. $appt_id = intval($appt_id);
  476. $appointment = Booked_WC_Appointment::get($appt_id);
  477. if ( !$appointment->products ) {
  478. return;
  479. }
  480. add_filter('booked_admin_show_calendar_buttons', array('Booked_WC_Functions', 'return_false'), 10);
  481. $edit_button = Booked_WC_Fragments::get_path('admin-calendar', 'app-buttons');
  482. include($edit_button);
  483. }
  484. public static function booked_admin_calendar_buttons_after($calendar_id, $appt_id, $status) {
  485. remove_filter('booked_admin_show_calendar_buttons', array('Booked_WC_Functions', 'return_false'), 10);
  486. }
  487. }