function appointments_get_worker_services($worker_id)
{
    $worker = appointments_get_worker($worker_id);
    if ($worker) {
        return $worker->get_services();
    }
    return array();
}
 /**
  * @group cache
  */
 function test_workers_cache()
 {
     $args = $this->factory->post->generate_args();
     $args['post_type'] = 'page';
     $page_id = $this->factory->post->create_object($args);
     $service_id = appointments_insert_service(array('name' => 'My Service'));
     $args = $this->factory->user->generate_args();
     $user_id = $this->factory->user->create_object($args);
     $args = $this->factory->user->generate_args();
     $user_id_2 = $this->factory->user->create_object($args);
     $args = $this->factory->user->generate_args();
     $user_id_3 = $this->factory->user->create_object($args);
     $args = array('ID' => $user_id, 'price' => '19.7', 'page' => $page_id, 'services_provided' => array($service_id), 'dummy' => true);
     appointments_insert_worker($args);
     $worker = wp_cache_get($user_id, 'app_workers');
     $this->assertFalse($worker);
     // This will set the cache
     appointments_get_worker($user_id);
     $worker = wp_cache_get($user_id, 'app_workers');
     $this->assertEquals($worker->ID, $user_id);
     appointments_delete_worker($user_id);
     $worker = wp_cache_get($service_id, 'app_workers');
     $this->assertFalse($worker);
     appointments_insert_worker($args);
     appointments_get_worker($user_id);
     appointments_update_worker($user_id, array('dummy' => false));
     $worker = wp_cache_get($user_id, 'app_workers');
     $this->assertFalse($worker);
     appointments_insert_worker(array('ID' => $user_id_2, 'services_provided' => array($service_id)));
     $workers = appointments_get_workers();
     $this->assertCount(2, $workers);
     $cache = wp_cache_get('app_get_workers');
     $this->assertCount(1, $cache);
     $workers = appointments_get_workers(array('page' => 8888));
     $this->assertCount(0, $workers);
     $cache = wp_cache_get('app_get_workers');
     $this->assertCount(1, $cache);
     $workers = appointments_get_workers(array('page' => $page_id));
     $this->assertCount(1, $workers);
     $cache = wp_cache_get('app_get_workers');
     $this->assertCount(2, $cache);
     $this->assertNotEmpty(wp_cache_get($user_id, 'app_workers'));
     // If we insert another worker, cache sould be cleared
     appointments_insert_worker(array('ID' => $user_id_3, 'services_provided' => array($service_id)));
     $this->assertFalse(wp_cache_get('app_get_workers'));
     // If we select again, cache should be refreshed
     $workers = appointments_get_workers();
     $this->assertCount(3, $workers);
     $cache = wp_cache_get('app_get_workers');
     $this->assertCount(1, $cache);
     wp_cache_flush();
     // If we get all workers, they should be added to workers list cache
     appointments_get_workers();
     $worker = wp_cache_get($user_id, 'app_workers');
     $this->assertEquals($worker->ID, $user_id);
 }
 /**
  *	Admin pages init stuff, save settings
  *
  */
 function admin_init()
 {
     global $appointments;
     if (!session_id()) {
         @session_start();
     }
     $page = add_menu_page('Appointments', __('Appointments', 'appointments'), App_Roles::get_capability('manage_options', App_Roles::CTX_PAGE_APPOINTMENTS), 'appointments', array(&$this, 'appointment_list'), 'dashicons-clock');
     add_submenu_page('appointments', __('Transactions', 'appointments'), __('Transactions', 'appointments'), App_Roles::get_capability('manage_options', App_Roles::CTX_PAGE_TRANSACTIONS), "app_transactions", array(&$this, 'transactions'));
     add_submenu_page('appointments', __('Settings', 'appointments'), __('Settings', 'appointments'), App_Roles::get_capability('manage_options', App_Roles::CTX_PAGE_SETTINGS), "app_settings", array(&$this, 'settings'));
     add_submenu_page('appointments', __('Shortcodes', 'appointments'), __('Shortcodes', 'appointments'), App_Roles::get_capability('manage_options', App_Roles::CTX_PAGE_SHORTCODES), "app_shortcodes", array(&$this, 'shortcodes_page'));
     add_submenu_page('appointments', __('FAQ', 'appointments'), __('FAQ', 'appointments'), App_Roles::get_capability('manage_options', App_Roles::CTX_PAGE_FAQ), "app_faq", array(&$this, 'faq_page'));
     // Add datepicker to appointments page
     add_action("admin_print_scripts-{$page}", array(&$this, 'admin_scripts'));
     do_action('app-admin-admin_pages_added', $page);
     if (isset($_POST["action_app"]) && !wp_verify_nonce($_POST['app_nonce'], 'update_app_settings')) {
         add_action('admin_notices', array(&$this, 'warning'));
         return;
     }
     // Read Location, Service, Worker
     $appointments->get_lsw();
     global $wpdb;
     if (isset($_POST["action_app"]) && 'save_general' == $_POST["action_app"]) {
         $appointments->options["min_time"] = $_POST["min_time"];
         $appointments->options["additional_min_time"] = trim($_POST["additional_min_time"]);
         $appointments->options["admin_min_time"] = $_POST["admin_min_time"];
         $appointments->options["app_lower_limit"] = trim($_POST["app_lower_limit"]);
         $appointments->options["app_limit"] = trim($_POST["app_limit"]);
         $appointments->options["clear_time"] = trim($_POST["clear_time"]);
         $appointments->options["spam_time"] = trim($_POST["spam_time"]);
         $appointments->options["auto_confirm"] = $_POST["auto_confirm"];
         $appointments->options["allow_worker_wh"] = $_POST["allow_worker_wh"];
         $appointments->options["allow_worker_confirm"] = $_POST["allow_worker_confirm"];
         $appointments->options["allow_overwork"] = $_POST["allow_overwork"];
         $appointments->options["allow_overwork_break"] = $_POST["allow_overwork_break"];
         $appointments->options["dummy_assigned_to"] = !$appointments->is_dummy(@$_POST["dummy_assigned_to"]) ? @$_POST["dummy_assigned_to"] : 0;
         $appointments->options["login_required"] = $_POST["login_required"];
         $appointments->options["accept_api_logins"] = isset($_POST["accept_api_logins"]);
         $appointments->options["facebook-no_init"] = isset($_POST["facebook-no_init"]);
         $appointments->options['facebook-app_id'] = trim($_POST['facebook-app_id']);
         $appointments->options['twitter-app_id'] = trim($_POST['twitter-app_id']);
         $appointments->options['twitter-app_secret'] = trim($_POST['twitter-app_secret']);
         $appointments->options['google-client_id'] = trim($_POST['google-client_id']);
         $appointments->options["app_page_type"] = $_POST["app_page_type"];
         $appointments->options["show_legend"] = $_POST["show_legend"];
         $appointments->options["color_set"] = $_POST["color_set"];
         foreach ($appointments->get_classes() as $class => $name) {
             $appointments->options[$class . "_color"] = $_POST[$class . "_color"];
         }
         $appointments->options["ask_name"] = isset($_POST["ask_name"]);
         $appointments->options["ask_email"] = isset($_POST["ask_email"]);
         $appointments->options["ask_phone"] = isset($_POST["ask_phone"]);
         $appointments->options["ask_phone"] = isset($_POST["ask_phone"]);
         $appointments->options["ask_address"] = isset($_POST["ask_address"]);
         $appointments->options["ask_city"] = isset($_POST["ask_city"]);
         $appointments->options["ask_note"] = isset($_POST["ask_note"]);
         $appointments->options["additional_css"] = trim(stripslashes_deep($_POST["additional_css"]));
         $appointments->options["payment_required"] = $_POST["payment_required"];
         $appointments->options["percent_deposit"] = trim(str_replace('%', '', $_POST["percent_deposit"]));
         $appointments->options["fixed_deposit"] = trim(str_replace($appointments->options["currency"], '', $_POST["fixed_deposit"]));
         /*
          * Membership plugin is replaced by Membership2. Old options are
          * only saved when the depreacted Membership plugin is still active.
          */
         if (class_exists('M_Membership')) {
             $appointments->options['members_no_payment'] = isset($_POST['members_no_payment']);
             // not used??
             $appointments->options['members_discount'] = trim(str_replace('%', '', $_POST['members_discount']));
             $appointments->options['members'] = maybe_serialize(@$_POST["members"]);
         }
         $appointments->options['currency'] = $_POST['currency'];
         $appointments->options['mode'] = $_POST['mode'];
         $appointments->options['merchant_email'] = trim($_POST['merchant_email']);
         $appointments->options['return'] = $_POST['return'];
         $appointments->options['allow_free_autoconfirm'] = !empty($_POST['allow_free_autoconfirm']);
         $appointments->options["send_confirmation"] = $_POST["send_confirmation"];
         $appointments->options["send_notification"] = @$_POST["send_notification"];
         $appointments->options["confirmation_subject"] = stripslashes_deep($_POST["confirmation_subject"]);
         $appointments->options["confirmation_message"] = stripslashes_deep($_POST["confirmation_message"]);
         $appointments->options["send_reminder"] = $_POST["send_reminder"];
         $appointments->options["reminder_time"] = str_replace(" ", "", $_POST["reminder_time"]);
         $appointments->options["send_reminder_worker"] = $_POST["send_reminder_worker"];
         $appointments->options["reminder_time_worker"] = str_replace(" ", "", $_POST["reminder_time_worker"]);
         $appointments->options["reminder_subject"] = stripslashes_deep($_POST["reminder_subject"]);
         $appointments->options["reminder_message"] = stripslashes_deep($_POST["reminder_message"]);
         $appointments->options["send_removal_notification"] = $_POST["send_removal_notification"];
         $appointments->options["removal_notification_subject"] = stripslashes_deep($_POST["removal_notification_subject"]);
         $appointments->options["removal_notification_message"] = stripslashes_deep($_POST["removal_notification_message"]);
         $appointments->options["log_emails"] = $_POST["log_emails"];
         $appointments->options['use_cache'] = $_POST['use_cache'];
         $appointments->options['disable_js_check_admin'] = isset($_POST['disable_js_check_admin']);
         $appointments->options['disable_js_check_frontend'] = isset($_POST['disable_js_check_frontend']);
         $appointments->options['use_mp'] = isset($_POST['use_mp']);
         $appointments->options["app_page_type_mp"] = @$_POST["app_page_type_mp"];
         $appointments->options['allow_cancel'] = @$_POST['allow_cancel'];
         $appointments->options['cancel_page'] = @$_POST['cancel_page'];
         $appointments->options["records_per_page"] = (int) trim(@$_POST["records_per_page"]);
         $appointments->options = apply_filters('app-options-before_save', $appointments->options);
         $saved = false;
         if (update_option('appointments_options', $appointments->options)) {
             $saved = true;
             if ('yes' == $appointments->options['use_cache']) {
                 add_action('admin_notices', array(&$appointments, 'saved_cleared'));
             } else {
                 add_action('admin_notices', array(&$appointments, 'saved'));
             }
         }
         // Flush cache
         if (isset($_POST["force_flush"]) || $saved) {
             $appointments->flush_cache();
             appointments_delete_timetables_cache();
             if (isset($_POST["force_flush"])) {
                 add_action('admin_notices', array(&$appointments, 'cleared'));
             }
         }
         if (isset($_POST['make_an_appointment']) || isset($_POST['make_an_appointment_product'])) {
             $this->_create_pages();
         }
         // Redirecting when saving options
         if ($saved) {
             wp_redirect(add_query_arg('saved', 1));
             die;
         }
     }
     $result = $updated = $inserted = false;
     // Save Working Hours
     if (isset($_POST["action_app"]) && 'save_working_hours' == $_POST["action_app"]) {
         $location = (int) $_POST['location'];
         foreach (array('closed', 'open') as $stat) {
             $query = $wpdb->prepare("SELECT COUNT(*) FROM {$appointments->wh_table} WHERE location=%d AND worker=%d AND status=%s", $location, $appointments->worker, $stat);
             $count = $wpdb->get_var($query);
             if ($count > 0) {
                 $r = $wpdb->update($appointments->wh_table, array('hours' => serialize($_POST[$stat]), 'status' => $stat), array('location' => $location, 'worker' => $appointments->worker, 'status' => $stat), array('%s', '%s'), array('%d', '%d', '%s'));
                 if ($r) {
                     $result = true;
                 }
             } else {
                 $r = $wpdb->insert($appointments->wh_table, array('location' => $location, 'worker' => $appointments->worker, 'hours' => serialize($_POST[$stat]), 'status' => $stat), array('%d', '%d', '%s', '%s'));
                 if ($r) {
                     $result = true;
                 }
             }
             if ($result) {
                 add_action('admin_notices', array(&$appointments, 'saved'));
             }
             appointments_delete_work_breaks_cache($location, $appointments->worker);
             appointments_delete_timetables_cache();
         }
     }
     // Save Exceptions
     if (isset($_POST["action_app"]) && 'save_exceptions' == $_POST["action_app"]) {
         $location = (int) $_POST['location'];
         foreach (array('closed', 'open') as $stat) {
             $count = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$appointments->exceptions_table} WHERE location=%d AND worker=%d AND status=%s", $location, $appointments->worker, $stat));
             if ($count > 0) {
                 $r = $wpdb->update($appointments->exceptions_table, array('days' => $this->_sort($_POST[$stat]["exceptional_days"]), 'status' => $stat), array('location' => $location, 'worker' => $appointments->worker, 'status' => $stat), array('%s', '%s'), array('%d', '%d', '%s'));
                 if ($r) {
                     $result = true;
                 }
             } else {
                 $r = $wpdb->insert($appointments->exceptions_table, array('location' => $location, 'worker' => $appointments->worker, 'days' => $this->_sort($_POST[$stat]["exceptional_days"]), 'status' => $stat), array('%d', '%d', '%s', '%s'));
                 if ($r) {
                     $result = true;
                 }
             }
             if ($result) {
                 add_action('admin_notices', array(&$appointments, 'saved'));
             }
             appointments_delete_exceptions_cache($location, $appointments->worker);
         }
     }
     // Save Services
     if (isset($_POST["action_app"]) && 'save_services' == $_POST["action_app"] && is_array($_POST["services"])) {
         do_action('app-services-before_save');
         foreach ($_POST["services"] as $ID => $service) {
             if ('' != trim($service["name"])) {
                 // Update or insert?
                 $_service = appointments_get_service($ID);
                 if ($_service) {
                     $args = array('name' => $service["name"], 'capacity' => (int) $service["capacity"], 'duration' => $service["duration"], 'price' => $service["price"], 'page' => $service["page"]);
                     $result = appointments_update_service($ID, $args);
                 } else {
                     $args = array('ID' => $ID, 'name' => $service["name"], 'capacity' => (int) $service["capacity"], 'duration' => $service["duration"], 'price' => $service["price"], 'page' => $service["page"]);
                     $result = appointments_insert_service($args);
                 }
                 do_action('app-services-service-updated', $ID);
             } else {
                 // Entering an empty name means deleting of a service
                 $r = appointments_delete_service($ID);
                 if ($r) {
                     $result = true;
                 }
             }
         }
         if ($result) {
             add_action('admin_notices', array(&$appointments, 'saved'));
         }
     }
     // Save Workers
     if (isset($_POST["action_app"]) && 'save_workers' == $_POST["action_app"] && is_array($_POST["workers"])) {
         foreach ($_POST["workers"] as $worker_id => $worker) {
             $new_worker_id = absint($worker["user"]);
             $worker_id = absint($worker_id);
             $inserted = false;
             $updated = false;
             $result = false;
             $worker_exists = appointments_get_worker($worker_id);
             if ($worker_exists) {
                 // Update
                 if ($new_worker_id != $worker_id && !empty($worker["services_provided"])) {
                     // We are trying to chage the user ID
                     $count = appointments_get_worker($new_worker_id);
                     // If the new ID already exist, do nothing
                     if (!$count) {
                         // Otherwise, change the ID
                         $args = array('ID' => $new_worker_id, 'price' => $worker["price"], 'services_provided' => $worker["services_provided"], 'dummy' => isset($worker["dummy"]), 'page' => $worker['page']);
                         $updated = appointments_update_worker($worker_id, $args);
                     }
                 } elseif ($new_worker_id == $worker_id && !empty($worker["services_provided"])) {
                     // Do not change user ID but update
                     $args = array('price' => $worker["price"], 'services_provided' => $worker["services_provided"], 'dummy' => isset($worker["dummy"]), 'page' => $worker['page']);
                     $updated = appointments_update_worker($worker_id, $args);
                 } elseif (empty($worker["services_provided"])) {
                     $r = appointments_delete_worker($worker_id);
                     if ($r) {
                         $result = true;
                     }
                 }
             } elseif (!$worker_exists && !empty($worker["services_provided"])) {
                 // Insert
                 $args = array('ID' => $worker["user"], 'price' => $worker["price"], 'services_provided' => $worker["services_provided"], 'page' => $worker["page"], 'dummy' => isset($worker["dummy"]));
                 $inserted = appointments_insert_worker($args);
                 if ($inserted) {
                     do_action('app-workers-worker-updated', $worker_id);
                 }
             }
         }
         if ($result || $updated || $inserted) {
             add_action('admin_notices', array(&$appointments, 'saved'));
         }
     }
     // Delete removed app records
     if (isset($_POST["delete_removed"]) && 'delete_removed' == $_POST["delete_removed"] && isset($_POST["app"]) && is_array($_POST["app"])) {
         $result = 0;
         foreach ($_POST["app"] as $app_id) {
             $result = $result + appointments_delete_appointment($app_id);
         }
         if ($result) {
             global $current_user;
             $userdata = get_userdata($current_user->ID);
             add_action('admin_notices', array(&$appointments, 'deleted'));
             do_action('app_deleted', $_POST["app"]);
             $appointments->log(sprintf(__('Appointment(s) with id(s):%s deleted by user:%s', 'appointments'), implode(', ', $_POST["app"]), $userdata->user_login));
         }
     }
     // Bulk status change
     if (isset($_POST["app_status_change"]) && $_POST["app_new_status"] && isset($_POST["app"]) && is_array($_POST["app"])) {
         $result = 0;
         $new_status = $_POST["app_new_status"];
         foreach ($_POST["app"] as $app_id) {
             $result = $result + (int) appointments_update_appointment_status(absint($app_id), $new_status);
         }
         if ($result) {
             $userdata = get_userdata(get_current_user_id());
             add_action('admin_notices', array(&$appointments, 'updated'));
             do_action('app_bulk_status_change', $_POST["app"]);
             $appointments->log(sprintf(__('Status of Appointment(s) with id(s):%s changed to %s by user:%s', 'appointments'), implode(', ', $_POST["app"]), $new_status, $userdata->user_login));
             if (is_object($appointments->gcal_api)) {
                 // If deleted, remove these from GCal too
                 if ('removed' == $new_status) {
                     foreach ($_POST["app"] as $app_id) {
                         $appointments->gcal_api->delete($app_id);
                         $appointments->send_removal_notification($app_id);
                     }
                 } else {
                     if (is_object($appointments->gcal_api) && $appointments->gcal_api->is_syncable_status($new_status)) {
                         foreach ($_POST["app"] as $app_id) {
                             $appointments->gcal_api->update($app_id);
                             // Also send out an email
                             if (!empty($appointments->options["send_confirmation"]) && 'yes' == $appointments->options["send_confirmation"]) {
                                 appointments_send_confirmation($app_id);
                             }
                         }
                     }
                 }
             }
         }
     }
     // Determine if we shall flush cache
     if (isset($_POST["action_app"]) && ($result || $updated || $inserted) || isset($_POST["delete_removed"]) && 'delete_removed' == $_POST["delete_removed"] || isset($_POST["app_status_change"]) && $_POST["app_new_status"]) {
         // As it means any setting is saved, lets clear cache
         $appointments->flush_cache();
     }
 }
/**
 * Update an appointment data
 *
 * @param $app_id
 * @param $args
 * @param bool $resend
 */
function appointments_update_appointment($app_id, $args)
{
    global $wpdb, $appointments;
    $old_appointment = appointments_get_appointment($app_id);
    if (!$old_appointment) {
        return false;
    }
    $fields = array('user' => '%d', 'email' => '%s', 'name' => '%s', 'phone' => '%s', 'address' => '%s', 'city' => '%s', 'service' => '%d', 'worker' => '%d', 'price' => '%s', 'note' => '%s', 'status' => false, 'location' => '%d', 'date' => false, 'time' => false, 'gcal_updated' => '%s', 'gcal_ID' => '%s');
    $update = array();
    $update_wildcards = array();
    foreach ($fields as $field => $wildcard) {
        if (isset($args[$field]) && $wildcard) {
            $update[$field] = $args[$field];
            $update_wildcards[] = $wildcard;
        }
    }
    if (isset($update['service'])) {
        $service = appointments_get_service($update['service']);
        if (!$service) {
            return false;
        }
    }
    if (isset($update['worker'])) {
        $worker = appointments_get_worker($update['worker']);
        if (!$worker) {
            $update['worker'] = 0;
        }
    }
    if (isset($update['price'])) {
        $update['price'] = preg_replace("/[^0-9,.]/", "", $update['price']);
    }
    if (isset($update['user'])) {
        $user = get_userdata($update['user']);
        if (!$user) {
            $update['user'] = 0;
        }
    }
    if (!empty($args['date']) && !empty($args['time'])) {
        $time = date('H:i', strtotime($args['time']));
        $datetime = strtotime(str_replace(',', '', $appointments->to_us($args['date'])) . " " . $time);
        $update['start'] = date('Y-m-d H:i:s', $datetime);
        $update_wildcards[] = '%s';
        $update['end'] = date('Y-m-d H:i:s', $datetime + $service->duration * 60);
        $update_wildcards[] = '%s';
    }
    // Change status?
    $updated_status = false;
    if (!empty($args['status'])) {
        // Yeah, maybe change status
        $updated_status = appointments_update_appointment_status($app_id, $args['status']);
    }
    if (empty($update) && empty($updated_status)) {
        return false;
    }
    $result = false;
    if (!empty($update)) {
        $table = appointments_get_table('appointments');
        $result = $wpdb->update($table, $update, array('ID' => $app_id), $update_wildcards, array('%d'));
    }
    if (!$result && !$updated_status) {
        // Nothing has changed
        return false;
    }
    $app = appointments_get_appointment($app_id);
    appointments_update_user_appointment_data($app_id);
    appointments_clear_appointment_cache($app_id);
    do_action('wpmudev_appointments_update_appointment', $app_id, $args, $old_appointment);
    return true;
}
 /**
  * Get price for the current service and worker
  * If worker has additional price (optional), it is added to the service price
  * @param paypal: If set true, deposit price is calculated
  * @return string
  */
 function get_price($paypal = false)
 {
     global $current_user;
     $this->get_lsw();
     $service_obj = $this->get_service($this->service);
     $worker_obj = appointments_get_worker($this->worker);
     if ($worker_obj && $worker_obj->price) {
         $worker_price = $worker_obj->price;
     } else {
         $worker_price = 0;
     }
     $price = $service_obj->price + $worker_price;
     /**
      * Filter allows other plugins or integrations to apply a discount to
      * the price.
      */
     $price = apply_filters('app_get_price_prepare', $price, $paypal, $this);
     // Discount
     if ($this->is_member() && isset($this->options["members_discount"]) && $this->options["members_discount"]) {
         // Special condition: Free for members
         if (100 == $this->options["members_discount"]) {
             $price = 0;
         } else {
             $price = $price * (100 - $this->options["members_discount"]) / 100;
         }
     }
     if ($paypal) {
         // Deposit
         if (isset($this->options["percent_deposit"]) && $this->options["percent_deposit"]) {
             $price = $price * $this->options["percent_deposit"] / 100;
         }
         if (isset($this->options["fixed_deposit"]) && $this->options["fixed_deposit"]) {
             $price = $this->options["fixed_deposit"];
         }
         // It is possible to ask special amounts to be paid
         $price = apply_filters('app_paypal_amount', $price, $this->service, $this->worker, $current_user->ID);
     } else {
         $price = apply_filters('app_get_price', $price, $this->service, $this->worker, $current_user->ID);
     }
     // Use number_format right at the end, cause it converts the number to a string.
     $price = number_format($price, 2);
     return $price;
 }