731 lines
21 KiB
PHP
731 lines
21 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Created by MailChimp.
|
|
*
|
|
* Name: Ryan Hungate
|
|
* Email: ryan@vextras.com
|
|
* Date: 2/17/16
|
|
* Time: 12:03 PM
|
|
*/
|
|
class MailChimp_Service extends MailChimp_WooCommerce_Options
|
|
{
|
|
protected static $pushed_orders = array();
|
|
|
|
protected $user_email = null;
|
|
protected $previous_email = null;
|
|
protected $force_cart_post = false;
|
|
protected $cart_was_submitted = false;
|
|
protected $cart = array();
|
|
protected $validated_cart_db = false;
|
|
|
|
/**
|
|
* hook fired when we know everything is booted
|
|
*/
|
|
public function wooIsRunning()
|
|
{
|
|
// make sure the site option for setting the mailchimp_carts has been saved.
|
|
$this->validated_cart_db = get_site_option('mailchimp_woocommerce_db_mailchimp_carts', false);
|
|
$this->is_admin = current_user_can('administrator');
|
|
}
|
|
|
|
/**
|
|
* @param $r
|
|
* @param $url
|
|
* @return mixed
|
|
*/
|
|
public function addHttpRequestArgs( $r, $url ) {
|
|
// not sure whether or not we need to implement something like this yet.
|
|
//$r['headers']['Authorization'] = 'Basic ' . base64_encode('username:password');
|
|
return $r;
|
|
}
|
|
|
|
/**
|
|
* @param $key
|
|
* @param $default
|
|
* @return mixed
|
|
*/
|
|
protected function cookie($key, $default = null)
|
|
{
|
|
if ($this->is_admin) {
|
|
return $default;
|
|
}
|
|
|
|
return isset($_COOKIE[$key]) ? $_COOKIE[$key] : $default;
|
|
}
|
|
|
|
/**
|
|
* @param WC_Order $order
|
|
*/
|
|
public function onNewPayPalOrder($order)
|
|
{
|
|
$this->onNewOrder($order->get_id());
|
|
}
|
|
|
|
/**
|
|
* This should only fire on a web based order so we can do real campaign tracking here.
|
|
*
|
|
* @param $order_id
|
|
*/
|
|
public function onNewOrder($order_id)
|
|
{
|
|
if (!mailchimp_is_configured()) return;
|
|
|
|
// register this order is already in process..
|
|
static::$pushed_orders[$order_id] = true;
|
|
|
|
// see if we have a session id and a campaign id, also only do this when this user is not the admin.
|
|
$campaign_id = $this->getCampaignTrackingID();
|
|
|
|
// grab the landing site cookie if we have one here.
|
|
$landing_site = $this->getLandingSiteCookie();
|
|
|
|
// expire the landing site cookie so we can rinse and repeat tracking
|
|
$this->expireLandingSiteCookie();
|
|
|
|
// remove this record from the db.
|
|
$this->clearCartData();
|
|
|
|
// queue up the single order to be processed.
|
|
$handler = new MailChimp_WooCommerce_Single_Order($order_id, null, $campaign_id, $landing_site);
|
|
wp_queue($handler, 60);
|
|
}
|
|
|
|
/**
|
|
* @param $order_id
|
|
* @param bool $is_admin
|
|
*/
|
|
public function handleOrderStatusChanged($order_id, $is_admin = false)
|
|
{
|
|
if (!mailchimp_is_configured()) return;
|
|
|
|
// register this order is already in process..
|
|
static::$pushed_orders[$order_id] = true;
|
|
// queue up the single order to be processed.
|
|
$handler = new MailChimp_WooCommerce_Single_Order($order_id, null, null, null);
|
|
$handler->is_update = true;
|
|
$handler->is_admin_save = $is_admin;
|
|
wp_queue($handler, 90);
|
|
}
|
|
|
|
/**
|
|
* @param $order_id
|
|
*/
|
|
public function onPartiallyRefunded($order_id)
|
|
{
|
|
if (!mailchimp_is_configured()) return;
|
|
|
|
$handler = new MailChimp_WooCommerce_Single_Order($order_id, null, null, null);
|
|
$handler->partially_refunded = true;
|
|
wp_queue($handler);
|
|
}
|
|
|
|
/**
|
|
* Clear the card data for a user.
|
|
*/
|
|
public function clearCartData()
|
|
{
|
|
if ($user_email = $this->getCurrentUserEmail()) {
|
|
$this->deleteCart($user_email);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param null $updated
|
|
* @return bool|null
|
|
*/
|
|
public function handleCartUpdated($updated = null)
|
|
{
|
|
if ($updated === false || $this->is_admin || $this->cart_was_submitted || !mailchimp_is_configured()) {
|
|
return !is_null($updated) ? $updated : false;
|
|
}
|
|
|
|
if (empty($this->cart)) {
|
|
$this->cart = $this->getCartItems();
|
|
}
|
|
|
|
if (($user_email = $this->getCurrentUserEmail())) {
|
|
|
|
$previous = $this->getPreviousEmailFromSession();
|
|
|
|
$uid = md5(trim(strtolower($user_email)));
|
|
|
|
// delete the previous records.
|
|
if (!empty($previous) && $previous !== $user_email) {
|
|
|
|
if ($this->api()->deleteCartByID($this->getUniqueStoreID(), $previous_email = md5(trim(strtolower($previous))))) {
|
|
mailchimp_log('ac.cart_swap', "Deleted cart [$previous] :: ID [$previous_email]");
|
|
}
|
|
|
|
// going to delete the cart because we are switching.
|
|
$this->deleteCart($previous_email);
|
|
}
|
|
|
|
if ($this->cart && !empty($this->cart)) {
|
|
|
|
// track the cart locally so we can repopulate things for cross device compatibility.
|
|
$this->trackCart($uid, $user_email);
|
|
|
|
$this->cart_was_submitted = true;
|
|
|
|
// grab the cookie data that could play important roles in the submission
|
|
$campaign = $this->getCampaignTrackingID();
|
|
|
|
// fire up the job handler
|
|
$handler = new MailChimp_WooCommerce_Cart_Update($uid, $user_email, $campaign, $this->cart);
|
|
wp_queue($handler);
|
|
}
|
|
|
|
return !is_null($updated) ? $updated : true;
|
|
}
|
|
|
|
return !is_null($updated) ? $updated : false;
|
|
}
|
|
|
|
/**
|
|
* @param $post_id
|
|
*/
|
|
public function handleNewCoupon($post_id)
|
|
{
|
|
$this->handleCouponSaved($post_id, new WC_Coupon($post_id));
|
|
}
|
|
|
|
/**
|
|
* @param $post_id
|
|
* @param null $coupon
|
|
*/
|
|
public function handleCouponSaved($post_id, $coupon = null)
|
|
{
|
|
if (!mailchimp_is_configured()) return;
|
|
|
|
if ($coupon instanceof WC_Coupon) {
|
|
wp_queue(new MailChimp_WooCommerce_SingleCoupon($post_id));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $post_id
|
|
*/
|
|
public function handleCouponRestored($post_id)
|
|
{
|
|
$this->handleCouponSaved($post_id, new WC_Coupon($post_id));
|
|
}
|
|
|
|
/**
|
|
* Save post metadata when a post is saved.
|
|
*
|
|
* @param int $post_id The post ID.
|
|
* @param WP_Post $post The post object.
|
|
* @param bool $update Whether this is an existing post being updated or not.
|
|
*/
|
|
public function handlePostSaved($post_id, $post, $update)
|
|
{
|
|
if (!mailchimp_is_configured()) return;
|
|
|
|
if ($post->post_status !== 'auto-draft') {
|
|
if ('product' == $post->post_type) {
|
|
wp_queue(new MailChimp_WooCommerce_Single_Product($post_id), 5);
|
|
} elseif ('shop_order' == $post->post_type) {
|
|
$this->handleOrderStatusChanged($post_id, is_admin());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $post_id
|
|
*/
|
|
public function handlePostTrashed($post_id)
|
|
{
|
|
if (!mailchimp_is_configured()) return;
|
|
|
|
switch (get_post_type($post_id)) {
|
|
case 'shop_coupon':
|
|
mailchimp_get_api()->deletePromoRule(mailchimp_get_store_id(), $post_id);
|
|
mailchimp_log('promo_code.deleted', "deleted promo code {$post_id}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $post_id
|
|
*/
|
|
public function handlePostRestored($post_id)
|
|
{
|
|
if (!mailchimp_is_configured()) return;
|
|
|
|
switch(get_post_type($post_id)) {
|
|
case 'shop_coupon':
|
|
return $this->handleCouponRestored($post_id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $user_id
|
|
*/
|
|
public function handleUserRegistration($user_id)
|
|
{
|
|
if (!mailchimp_is_configured()) return;
|
|
|
|
$subscribed = (bool) isset($_POST['mailchimp_woocommerce_newsletter']) ?
|
|
$_POST['mailchimp_woocommerce_newsletter'] : false;
|
|
|
|
// update the user meta with the 'is_subscribed' form element
|
|
update_user_meta($user_id, 'mailchimp_woocommerce_is_subscribed', $subscribed);
|
|
|
|
if ($subscribed) {
|
|
wp_queue(new MailChimp_WooCommerce_User_Submit($user_id, $subscribed));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $user_id
|
|
* @param $old_user_data
|
|
*/
|
|
function handleUserUpdated($user_id, $old_user_data)
|
|
{
|
|
if (!mailchimp_is_configured()) return;
|
|
|
|
// only update this person if they were marked as subscribed before
|
|
$is_subscribed = get_user_meta($user_id, 'mailchimp_woocommerce_is_subscribed', true);
|
|
|
|
// if they don't have a meta set for is_subscribed, we will get a blank string, so just ignore this.
|
|
if ($is_subscribed === '' || $is_subscribed === null) return;
|
|
|
|
// only send this update if the user actually has a boolean value.
|
|
wp_queue(new MailChimp_WooCommerce_User_Submit($user_id, (bool) $is_subscribed, $old_user_data));
|
|
}
|
|
|
|
/**
|
|
* Delete all the options pointing to the pages, and re-start the sync process.
|
|
* @param bool $only_products
|
|
* @return bool
|
|
*/
|
|
protected function syncProducts($only_products = false)
|
|
{
|
|
if (!$this->isAdmin()) return false;
|
|
$this->removePointers(true, ($only_products ? false : true));
|
|
update_option('mailchimp-woocommerce-sync.orders.prevent', $only_products);
|
|
MailChimp_WooCommerce_Process_Products::push();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Delete all the options pointing to the pages, and re-start the sync process.
|
|
* @return bool
|
|
*/
|
|
protected function syncOrders()
|
|
{
|
|
if (!$this->isAdmin()) return false;
|
|
$this->removePointers(false, true);
|
|
// since the products are all good, let's sync up the orders now.
|
|
wp_queue(new MailChimp_WooCommerce_Process_Orders());
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return bool|string
|
|
*/
|
|
public function getCurrentUserEmail()
|
|
{
|
|
if (isset($this->user_email) && !empty($this->user_email)) {
|
|
return $this->user_email = strtolower($this->user_email);
|
|
}
|
|
|
|
$user = wp_get_current_user();
|
|
$email = ($user->ID > 0 && isset($user->user_email)) ? $user->user_email : $this->getEmailFromSession();
|
|
|
|
return $this->user_email = strtolower($email);
|
|
}
|
|
|
|
/**
|
|
* @return bool|array
|
|
*/
|
|
public function getCartItems()
|
|
{
|
|
if (!($this->cart = $this->getWooSession('cart', false))) {
|
|
$this->cart = function_exists('WC') ? false : WC()->cart->get_cart();
|
|
} else {
|
|
$cart_session = array();
|
|
foreach ( $this->cart as $key => $values ) {
|
|
$cart_session[$key] = $values;
|
|
unset($cart_session[$key]['data']); // Unset product object
|
|
}
|
|
return $this->cart = $cart_session;
|
|
}
|
|
|
|
return is_array($this->cart) ? $this->cart : false;
|
|
}
|
|
|
|
/**
|
|
* Set the cookie of the mailchimp campaigns if we have one.
|
|
*/
|
|
public function handleCampaignTracking()
|
|
{
|
|
// set the landing site cookie if we don't have one.
|
|
$this->setLandingSiteCookie();
|
|
|
|
$cookie_duration = $this->getCookieDuration();
|
|
|
|
// if we have a query string of the mc_cart_id in the URL, that means we are sending a campaign from MC
|
|
if (isset($_GET['mc_cart_id']) && !isset($_GET['removed_item'])) {
|
|
|
|
// try to pull the cart from the database.
|
|
if (($cart = $this->getCart($_GET['mc_cart_id'])) && !empty($cart)) {
|
|
|
|
// set the current user email
|
|
$this->user_email = trim(str_replace(' ','+', $cart->email));
|
|
|
|
if (($current_email = $this->getEmailFromSession()) && $current_email !== $this->user_email) {
|
|
$this->previous_email = $current_email;
|
|
@setcookie('mailchimp_user_previous_email',$this->user_email, $cookie_duration, '/' );
|
|
}
|
|
|
|
// cookie the current email
|
|
@setcookie('mailchimp_user_email', $this->user_email, $cookie_duration, '/' );
|
|
|
|
// set the cart data.
|
|
$this->setWooSession('cart', unserialize($cart->cart));
|
|
}
|
|
}
|
|
|
|
if (isset($_REQUEST['mc_cid'])) {
|
|
$this->setCampaignTrackingID($_REQUEST['mc_cid'], $cookie_duration);
|
|
}
|
|
|
|
if (isset($_REQUEST['mc_eid'])) {
|
|
@setcookie('mailchimp_email_id', trim($_REQUEST['mc_eid']), $cookie_duration, '/' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return mixed|null
|
|
*/
|
|
public function getCampaignTrackingID()
|
|
{
|
|
$cookie = $this->cookie('mailchimp_campaign_id', false);
|
|
if (empty($cookie)) {
|
|
$cookie = $this->getWooSession('mailchimp_tracking_id', false);
|
|
}
|
|
|
|
return $cookie;
|
|
}
|
|
|
|
/**
|
|
* @param $id
|
|
* @param $cookie_duration
|
|
* @return $this
|
|
*/
|
|
public function setCampaignTrackingID($id, $cookie_duration)
|
|
{
|
|
$cid = trim($id);
|
|
|
|
@setcookie('mailchimp_campaign_id', $cid, $cookie_duration, '/' );
|
|
$this->setWooSession('mailchimp_campaign_id', $cid);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return mixed|null
|
|
*/
|
|
public function getLandingSiteCookie()
|
|
{
|
|
$cookie = $this->cookie('mailchimp_landing_site', false);
|
|
|
|
if (empty($cookie)) {
|
|
$cookie = $this->getWooSession('mailchimp_landing_site', false);
|
|
}
|
|
|
|
return $cookie;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setLandingSiteCookie()
|
|
{
|
|
if (isset($_GET['expire_landing_site'])) $this->expireLandingSiteCookie();
|
|
|
|
// if we already have a cookie here, we need to skip it.
|
|
if ($this->getLandingSiteCookie() != false) return $this;
|
|
|
|
$http_referer = $this->getReferer();
|
|
|
|
if (!empty($http_referer)) {
|
|
|
|
// grab the current landing url since it's a referral.
|
|
$landing_site = home_url() . wp_unslash($_SERVER['REQUEST_URI']);
|
|
|
|
$compare_refer = str_replace(array('http://', 'https://'), '', $http_referer);
|
|
$compare_local = str_replace(array('http://', 'https://'), '', $landing_site);
|
|
|
|
if (strpos($compare_local, $compare_refer) === 0) return $this;
|
|
|
|
// set the cookie
|
|
@setcookie('mailchimp_landing_site', $landing_site, $this->getCookieDuration(), '/' );
|
|
|
|
$this->setWooSession('mailchimp_landing_site', $landing_site);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return array|bool|string
|
|
*/
|
|
public function getReferer()
|
|
{
|
|
if (!empty($_REQUEST['_wp_http_referer'])) {
|
|
return wp_unslash($_REQUEST['_wp_http_referer']);
|
|
} elseif (!empty($_SERVER['HTTP_REFERER'])) {
|
|
return wp_unslash( $_SERVER['HTTP_REFERER']);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function expireLandingSiteCookie()
|
|
{
|
|
@setcookie('mailchimp_landing_site', false, $this->getCookieDuration(), '/' );
|
|
$this->setWooSession('mailchimp_landing_site', false);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
protected function getEmailFromSession()
|
|
{
|
|
return $this->cookie('mailchimp_user_email', false);
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
protected function getPreviousEmailFromSession()
|
|
{
|
|
if ($this->previous_email) {
|
|
return $this->previous_email = strtolower($this->previous_email);
|
|
}
|
|
$email = $this->cookie('mailchimp_user_previous_email', false);
|
|
return $email ? strtolower($email) : false;
|
|
}
|
|
|
|
/**
|
|
* @param $key
|
|
* @param null $default
|
|
* @return mixed|null
|
|
*/
|
|
public function getWooSession($key, $default = null)
|
|
{
|
|
if (!function_exists('WC')) return $default;
|
|
|
|
if (!($woo = WC()) || empty($woo->session)) {
|
|
return $default;
|
|
}
|
|
return $woo->session->get($key, $default);
|
|
}
|
|
|
|
/**
|
|
* @param $key
|
|
* @param $value
|
|
* @return $this
|
|
*/
|
|
public function setWooSession($key, $value)
|
|
{
|
|
if (!function_exists('WC')) return $this;
|
|
|
|
if (!($woo = WC()) || empty($woo->session)) {
|
|
return $this;
|
|
}
|
|
|
|
$woo->session->set($key, $value);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @param $key
|
|
* @return $this
|
|
*/
|
|
public function removeWooSession($key)
|
|
{
|
|
if (!function_exists('WC')) return $this;
|
|
|
|
if (!($woo = WC()) || empty($woo->session)) {
|
|
return $this;
|
|
}
|
|
|
|
$woo->session->__unset($key);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function get_user_by_hash()
|
|
{
|
|
if ($this->doingAjax() && isset($_GET['hash'])) {
|
|
if (($cart = $this->getCart($_GET['hash']))) {
|
|
$this->respondJSON(array('success' => true, 'email' => $cart->email));
|
|
}
|
|
}
|
|
$this->respondJSON(array('success' => false, 'email' => false));
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function set_user_by_email()
|
|
{
|
|
if ($this->is_admin) {
|
|
$this->respondJSON(array('success' => false));
|
|
}
|
|
|
|
if ($this->doingAjax() && isset($_GET['email'])) {
|
|
|
|
$cookie_duration = $this->getCookieDuration();
|
|
|
|
$this->user_email = trim(str_replace(' ','+', $_GET['email']));
|
|
|
|
if (($current_email = $this->getEmailFromSession()) && $current_email !== $this->user_email) {
|
|
$this->previous_email = $current_email;
|
|
$this->force_cart_post = true;
|
|
@setcookie('mailchimp_user_previous_email',$this->user_email, $cookie_duration, '/' );
|
|
}
|
|
|
|
@setcookie('mailchimp_user_email', $this->user_email, $cookie_duration, '/' );
|
|
|
|
$this->getCartItems();
|
|
|
|
$this->handleCartUpdated();
|
|
|
|
$this->respondJSON(array(
|
|
'success' => true,
|
|
'email' => $this->user_email,
|
|
'previous' => $this->previous_email,
|
|
'cart' => $this->cart,
|
|
));
|
|
}
|
|
|
|
$this->respondJSON(array('success' => false, 'email' => false));
|
|
}
|
|
|
|
/**
|
|
* @param string $time
|
|
* @return int
|
|
*/
|
|
protected function getCookieDuration($time = 'thirty_days')
|
|
{
|
|
$durations = array(
|
|
'one_day' => 86400, 'seven_days' => 604800, 'fourteen_days' => 1209600, 'thirty_days' => 2419200,
|
|
);
|
|
|
|
if (!array_key_exists($time, $durations)) {
|
|
$time = 'thirty_days';
|
|
}
|
|
|
|
return time() + $durations[$time];
|
|
}
|
|
|
|
/**
|
|
* @param $key
|
|
* @param bool $default
|
|
* @return bool
|
|
*/
|
|
protected function get($key, $default = false)
|
|
{
|
|
if (!isset($_REQUEST['mailchimp-woocommerce']) || !isset($_REQUEST['mailchimp-woocommerce'][$key])) {
|
|
return $default;
|
|
}
|
|
return $_REQUEST['mailchimp-woocommerce'][$key];
|
|
}
|
|
|
|
/**
|
|
* @param $uid
|
|
* @return array|bool|null|object|void
|
|
*/
|
|
protected function getCart($uid)
|
|
{
|
|
if (!$this->validated_cart_db) return false;
|
|
|
|
global $wpdb;
|
|
|
|
$table = "{$wpdb->prefix}mailchimp_carts";
|
|
$statement = "SELECT * FROM $table WHERE id = %s";
|
|
$sql = $wpdb->prepare($statement, $uid);
|
|
|
|
if (($saved_cart = $wpdb->get_row($sql)) && !empty($saved_cart)) {
|
|
return $saved_cart;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param $uid
|
|
* @return true
|
|
*/
|
|
protected function deleteCart($uid)
|
|
{
|
|
if (!$this->validated_cart_db) return false;
|
|
|
|
global $wpdb;
|
|
$table = "{$wpdb->prefix}mailchimp_carts";
|
|
$sql = $wpdb->prepare("DELETE FROM $table WHERE id = %s", $uid);
|
|
$wpdb->query($sql);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param $uid
|
|
* @param $email
|
|
* @return bool
|
|
*/
|
|
protected function trackCart($uid, $email)
|
|
{
|
|
if (!$this->validated_cart_db) return false;
|
|
|
|
global $wpdb;
|
|
|
|
$table = "{$wpdb->prefix}mailchimp_carts";
|
|
|
|
$statement = "SELECT * FROM $table WHERE id = %s";
|
|
$sql = $wpdb->prepare($statement, $uid);
|
|
|
|
$user_id = get_current_user_id();
|
|
|
|
if (($saved_cart = $wpdb->get_row($sql)) && is_object($saved_cart)) {
|
|
$statement = "UPDATE {$table} SET `cart` = '%s', `email` = '%s', `user_id` = %s WHERE `id` = '%s'";
|
|
$sql = $wpdb->prepare($statement, array(maybe_serialize($this->cart), $email, $user_id, $uid));
|
|
$wpdb->query($sql);
|
|
} else {
|
|
$wpdb->insert("{$wpdb->prefix}mailchimp_carts", array(
|
|
'id' => $uid,
|
|
'email' => $email,
|
|
'user_id' => (int) $user_id,
|
|
'cart' => maybe_serialize($this->cart),
|
|
'created_at' => gmdate('Y-m-d H:i:s', time()),
|
|
));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param $data
|
|
*/
|
|
protected function respondJSON($data)
|
|
{
|
|
header('Content-Type: application/json');
|
|
echo json_encode($data);
|
|
exit;
|
|
}
|
|
|
|
}
|