ots)); } public function ajax_appointments() { $this->validate_admin_nonce(); $data = $this->parse_input_data(); $response = array(); if ($this->type === 'GET') { $response = $this->models->get_all_appointments($data); } die(json_encode($response)); } public function ajax_appointment() { $this->validate_admin_nonce(); $response = $this->parse_appointment(false); if ($response == false) { $this->send_err_json_result('err'); } if ($this->type != 'NEW' && $this->type != 'UPDATE') { $this->send_ok_json_result($response); } if (isset($this->data['_mail'])) { $this->mail->send_status_change_mail($response->id); $this->mail->send_admin_email_notification_action($response->id); } $this->send_ok_json_result($response); } /** * Service model */ public function ajax_service() { $this->validate_admin_nonce(); $this->validate_access_rights('services'); $this->parse_single_model('ea_services'); } /** * Service model */ public function ajax_update_order() { $this->validate_admin_nonce(); $raw_data = file_get_contents('php://input'); $data = json_decode($raw_data, true); if (isset($data['sequence_data']) && !empty($data['sequence_data'])) { $this->update_multiple_service_sequences($data['sequence_data']); die(json_encode(['status' => true])); } die(json_encode(['status' => false])); } public function update_multiple_service_sequences($data) { global $wpdb; $table_name = $wpdb->prefix . 'ea_services'; foreach ($data as $row) { if (!isset($row['id']) || !isset($row['sequence'])) { continue; } $id = $row['id']; $sequence = $row['sequence']; $update_data = array( 'sequence' => $sequence ); $where = array( 'id' => $id ); $updated = $wpdb->update($table_name, $update_data, $where); } } /** * Services collection */ public function ajax_services() { $this->validate_admin_nonce(); $this->validate_access_rights('services'); $this->parse_input_data(); $response = array(); $orderPart = $this->models->get_order_by_part('ea_services'); if ($this->type === 'GET') { $response = $this->models->get_all_rows('ea_services', array(), $orderPart); } die(json_encode($response)); } /** * Locations collection */ public function ajax_locations() { $this->validate_admin_nonce(); $this->validate_access_rights('locations'); $this->parse_input_data(); $response = array(); $orderPart = $this->models->get_order_by_part('ea_locations'); if ($this->type === 'GET') { $response = $this->models->get_all_rows('ea_locations', array(), $orderPart); } header("Content-Type: application/json"); die(json_encode($response)); } /** * Single location */ public function ajax_location() { $this->validate_admin_nonce(); $this->validate_access_rights('locations'); $this->parse_single_model('ea_locations'); } /** * Workers collection */ public function ajax_workers() { $this->validate_admin_nonce(); $this->validate_access_rights('workers'); $this->parse_input_data(); $response = array(); $orderPart = $this->models->get_order_by_part('ea_workers'); if ($this->type === 'GET') { $response = $this->models->get_all_rows('ea_staff', array(), $orderPart); } header("Content-Type: application/json"); die(json_encode($response)); } /** * Single worker */ public function ajax_worker() { $this->validate_admin_nonce(); $this->validate_access_rights('workers'); $this->parse_single_model('ea_staff'); } /** * Workers collection */ public function ajax_connections() { $this->validate_admin_nonce(); $this->validate_access_rights('connections'); $this->parse_input_data(); $response = array(); if ($this->type === 'GET') { $response = $this->models->get_all_rows('ea_connections'); } header("Content-Type: application/json"); die(json_encode($response)); } /** * Single connection */ public function ajax_connection() { $this->validate_admin_nonce(); $this->validate_access_rights('connections'); $this->parse_single_model('ea_connections'); } /** * Get list of free days inside month */ public function ajax_month_status() { $this->validate_nonce('reports'); $data = $this->parse_input_data(); $response = $this->report->get_available_dates($data['location'], $data['service'], $data['worker'], $data['month'], $data['year']); $this->send_ok_json_result($response); } public function ajax_field() { $this->validate_admin_nonce(); $this->validate_access_rights('settings'); // we need to add slug $data = $this->parse_input_data(); $table = 'ea_meta_fields'; // we need to parse new and update case if ($this->type == 'NEW' || $this->type == 'UPDATE') { $data['slug'] = EAMetaFields::parse_field_slug_name($data, $this->models->get_next_meta_field_id()); $response = $this->models->replace($table, $data, true); if ($response == false) { $this->send_err_json_result('{"err":true}'); } $this->send_ok_json_result($response); } $this->parse_single_model($table); } public function ajax_fields() { $this->validate_admin_nonce(); $this->validate_access_rights('settings'); $data = $this->parse_input_data(); $response = array(); if ($this->type === 'GET') { // $response = $this->models->get_all_rows('ea_meta_fields', $data); $response = $this->models->get_all_rows('ea_meta_fields'); } die(json_encode($response)); } public function ajax_default_template() { $this->validate_admin_nonce(); $this->validate_access_rights('settings'); $content = $this->mail->get_default_admin_template(); wp_die($content); } /** * Errors for tools page */ public function ajax_errors() { $this->validate_admin_nonce(); $this->validate_access_rights('tools'); $this->parse_input_data(); $response = array(); if ($this->type === 'GET') { $response = $this->models->get_all_rows('ea_error_logs'); } die(json_encode($response)); } public function ajax_test_mail() { $this->validate_admin_nonce(); $this->validate_access_rights('tools'); $address = $_POST['address']; $native = $_POST['native']; if (!filter_var($address, FILTER_VALIDATE_EMAIL)) { die(__('Invalid email address!', 'easy-appointments')); } if (!current_user_can('install_plugins')) { die(__('Only admin user can test mail!', 'easy-appointments')); } $headers = array('Content-Type: text/html; charset=UTF-8'); $send_from = $this->options->get_option_value('send.from.email', ''); if (!empty($send_from)) { $headers[] = 'From: ' . $send_from; } $files = array(); $subject = 'Test mail'; $body = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; if ($native) { mail($address, $subject, $body, implode("\n", $headers)); } else { wp_mail($address, $subject, $body, $headers, $files); } die(__('Request completed, please check email.', 'easy-appointments')); } /** * REST enter point */ private function parse_input_data() { $method = $_SERVER['REQUEST_METHOD']; if (!empty($_REQUEST['_method'])) { $method = strtoupper($_REQUEST['_method']); unset($_REQUEST['_method']); } $data = array(); $local_type = $this->type; switch ($method) { case 'POST': $data = json_decode(file_get_contents("php://input"), true); $this->type = 'NEW'; break; case 'PUT': $data = json_decode(file_get_contents("php://input"), true); $this->type = 'UPDATE'; break; case 'GET': $data = $_REQUEST; $this->type = 'GET'; break; case 'DELETE': $data = $_REQUEST; $this->type = 'DELETE'; break; } // sometimes this method is called more then once and in compatibility mode it is removing type value if ($local_type) { $this->type = $local_type; } return $data; } /** * Ajax call for report data */ public function ajax_report() { $this->validate_admin_nonce(); $this->validate_access_rights('reports'); $data = $this->parse_input_data(); $type = $data['report']; $response = $this->report->get($type, $data); $this->send_ok_json_result($response); } public function ajax_export() { $this->validate_admin_nonce(); $this->validate_access_rights('reports'); $data = $this->parse_input_data(); $workersTmp = $response = $this->models->get_all_rows('ea_staff'); $locationsTmp = $response = $this->models->get_all_rows('ea_locations'); $servicesTmp = $response = $this->models->get_all_rows('ea_services'); $app_fields = array('id', 'location', 'service', 'worker', 'date', 'start', 'end', 'end_date', 'status', 'user', 'price', 'ip', 'created', 'session'); $meta_fields_tmp = $this->models->get_all_rows('ea_meta_fields'); $workers = array(); $locations = array(); $services = array(); foreach ($workersTmp as $w) { $workers[$w->id] = $w->name; } foreach ($locationsTmp as $l) { $locations[$l->id] = $l->name; } foreach ($servicesTmp as $s) { $services[$s->id] = $s->name; } foreach ($meta_fields_tmp as $item) { $app_fields[] = $item->slug; } $fields_from_option = get_option('ea_excel_columns', ''); if (!empty($fields_from_option)) { $app_fields = explode(',', $fields_from_option); } header('Content-Encoding: UTF-8'); header('Content-type: text/csv; charset=UTF-8'); header('Content-Disposition: attachment; filename=Customers_Export.csv'); echo "\xEF\xBB\xBF"; // UTF-8 BOM // set_time_limit(0); $params = array( 'from' => $data['ea-export-from'], 'to' => $data['ea-export-to'] ); $rows = $this->models->get_all_appointments($params); $out = fopen('php://output', 'w'); if (count($rows) > 0) { fputcsv($out, $app_fields); } foreach ($rows as $row) { $arr = get_object_vars($row); $app = array(); foreach ($app_fields as $field) { // if key is not existing if (!array_key_exists($field, $arr)) { $app[] = ''; continue; } if ($field == 'worker') { $app[] = $workers[$arr['worker']]; continue; } if ($field == 'location') { $app[] = $locations[$arr['location']]; continue; } if ($field == 'service') { $app[] = $services[$arr['service']]; continue; } $app[] = $arr[$field]; } fputcsv($out, $app); } fclose($out); die; } /** * @param $table * @param bool $end * @return array|bool|false|int|null|object|stdClass|void */ private function parse_single_model($table, $end = true) { $data = $this->parse_input_data(); if (!$end) { $this->data = $data; } $response = array(); switch ($this->type) { case 'GET': $id = (int)$_GET['id']; $response = $this->models->get_row($table, $id); break; case 'UPDATE': case 'NEW': $response = $this->models->replace($table, $data, true); break; case 'DELETE': $data = $_GET; $response = $this->models->delete($table, $data, true); break; } if ($response == false) { $this->send_err_json_result('{"err":true}'); } if ($end) { $this->send_ok_json_result($response); } else { return $response; } } /** * @param bool $end * @return array|bool|false|int|null|object|stdClass|void */ private function parse_appointment($end = true) { $data = $this->parse_input_data(); if (!$end) { $this->data = $data; } $table = 'ea_appointments'; $fields = 'ea_fields'; $app_fields = array('id', 'location', 'service', 'worker', 'date', 'start', 'end', 'end_date', 'status', 'user', 'price'); $app_data = array(); foreach ($app_fields as $value) { if (array_key_exists($value, $data)) { $app_data[$value] = $data[$value]; } } // set end data $service = $this->models->get_row('ea_services', $app_data['service']); $end_time = strtotime("{$data['start']} + {$service->duration} minute"); $app_data['end'] = date('H:i', $end_time); $meta_fields = $this->models->get_all_rows('ea_meta_fields'); $meta_data = array(); foreach ($meta_fields as $value) { if (array_key_exists($value->slug, $data)) { $meta_data[] = array( 'app_id' => null, 'field_id' => $value->id, 'value' => $data[$value->slug] ); } } $response = array(); switch ($this->type) { case 'GET': $id = (int)$_GET['id']; $response = $this->models->get_row($table, $id); break; case 'UPDATE': $response = $this->models->replace($table, $app_data, true); $this->models->delete($fields, array('app_id' => $app_data['id']), true); foreach ($meta_data as $value) { $value['app_id'] = $app_data['id']; $this->models->replace($fields, $value, true, true); } // edit app do_action('ea_edit_app', $app_data['id']); break; case 'NEW': $response = $this->models->replace($table, $app_data, true); foreach ($meta_data as $value) { $value['app_id'] = $response->id; $this->models->replace($fields, $value, true, true); } // trigger new appointment do_action('ea_new_app', $response->id, $app_data, false); break; case 'DELETE': $data = $_GET; $response = $this->models->delete($table, $data, true); $this->models->delete($fields, array('app_id' => $app_data['id']), true); break; } if ($response == false) { $this->send_err_json_result('{"err":true}'); } if ($end) { $this->send_ok_json_result($response); } else { return $response; } } private function send_ok_json_result($result) { header("Content-Type: application/json"); die(json_encode($result)); } private function send_err_json_result($message) { header('HTTP/1.1 400 BAD REQUEST'); die($message); } private function validate_access_rights($resource) { $capability = apply_filters('easy-appointments-user-ajax-capabilities', 'manage_options', $resource); if (!current_user_can( $capability ) && !current_user_can('manage_options')) { header('HTTP/1.1 403 Forbidden'); die('You don\'t have rights for this action'); } } /** * Sometimes users want to skip nonce validation because of caching that is making it impossible to have valid one */ private function validate_nonce() { // we need to unset check value unset($_GET['check']); $value = $this->options->get_option_value('nonce.off'); if (empty($value)) { return; } check_ajax_referer('ea-bootstrap-form', 'check'); } private function validate_admin_nonce() { $value = $this->options->get_option_value('nonce.off', null); if (!empty($value)) { return; } check_ajax_referer('wp_rest'); } public function save_custom_columns() { $this->validate_admin_nonce(); $raw_fields = $_POST['fields']; $fields = explode(',', $raw_fields); $columns = array_map(function($element) { return trim($element); }, $fields); $all_columns = $this->models->get_all_tags_for_template(); $result = array(); foreach ($columns as $column) { if (in_array($column, $all_columns)) { $result[] = $column; } } update_option('ea_excel_columns', implode(',', $result)); die; } private function validate_captcha() { $site_key = $this->options->get_option_value('captcha.site-key'); $secret = $this->options->get_option_value('captcha.secret-key'); $site_key3 = $this->options->get_option_value('captcha3.site-key'); $secret3 = $this->options->get_option_value('captcha3.secret-key'); $captcha = array_key_exists('captcha', $_REQUEST) ? $_REQUEST['captcha'] : ''; if (empty($site_key3) && empty($site_key)) { return; } if (!empty($site_key3)) { $secret = $secret3; } // check if curl extension is loaded $curl_enabled = extension_loaded('curl'); // Try first curl if ($curl_enabled) { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => 'https://www.google.com/recaptcha/api/siteverify', CURLOPT_POST => true, CURLOPT_POSTFIELDS => [ 'secret' => $secret, 'response' => $captcha, 'remoteip' => $_SERVER['REMOTE_ADDR'] ], CURLOPT_RETURNTRANSFER => true ]); $response = curl_exec($ch); curl_close($ch); } else { // if not use regular remote file open $post_data = http_build_query( array( 'secret' => $secret, 'response' => $captcha, 'remoteip' => $_SERVER['REMOTE_ADDR'] ) ); $opts = array('http' => array( 'method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => $post_data ) ); $context = stream_context_create($opts); $response = file_get_contents('https://www.google.com/recaptcha/api/siteverify', false, $context); } $result = json_decode($response); if (!$result->success) { $message = __('Invalid captcha!', 'easy-appointments'); $this->send_err_json_result('{"message":"' . $message . '"}'); } } }