Example #1
0
 /**
  * Sets attendee data on order posts
  *
  * @since 4.1
  *
  * @param OrderEventMessage $order_event Shopp order event
  */
 public function save_attendee_meta_to_order(OrderEventMessage $order_event)
 {
     $order = shopp_order($order_event->order);
     $order_items = $order->purchased;
     // Bail if the order is empty
     if (empty($order_items)) {
         return;
     }
     $product_ids = array();
     // gather product ids
     foreach ((array) $order_items as $item) {
         if (empty($item->product)) {
             continue;
         }
         $product_ids[] = $item->product;
     }
     $meta_object = Tribe__Tickets_Plus__Main::instance()->meta();
     // build the custom meta data that will be stored in the order meta
     if (!($order_meta = $meta_object->build_order_meta($product_ids))) {
         return;
     }
     // store the custom meta on the order
     shopp_set_meta($order->id, 'purchase', Tribe__Tickets_Plus__Meta::META_KEY, $order_meta);
     // clear out product custom meta data cookies
     foreach ($product_ids as $product_id) {
         $meta_object->clear_meta_cookie_data($product_id);
     }
 }
Example #2
0
 /**
  * Creates or retrieves temporary account registration information for the order
  *
  * @author Jonathan Davis
  * @since 1.3
  *
  * @return array A list of the registration objects
  **/
 public function registration($args = false)
 {
     if (empty($this->id)) {
         return false;
     }
     $args = func_get_args();
     if (count($args) == 1 && 'cleanup' == reset($args)) {
         return shopp_rmv_meta($this->id, 'purchase', 'registration');
     }
     $registration = array();
     $objectmap = array('ShoppCustomer' => 'Customer', 'BillingAddress' => 'Billing', 'ShippingAddress' => 'Shipping');
     foreach ($args as $Object) {
         $class = is_object($Object) ? get_class($Object) : '';
         $Record = new StdClass();
         $properties = array_keys($Object->_datatypes);
         foreach ($properties as $property) {
             if (isset($Object->{$property})) {
                 $Record->{$property} = $Object->{$property};
             }
         }
         if ('ShoppCustomer' == $class) {
             // hash the password before storage
             $Object->hashpass();
             if (isset($Object->passhash)) {
                 $Record->passhash = $Object->passhash;
             }
             $Record->loginname = $Object->loginname;
         }
         if (isset($objectmap[$class])) {
             $registration[$objectmap[$class]] = $Record;
         }
     }
     if (!empty($registration)) {
         shopp_set_meta($this->id, 'purchase', 'registration', $registration);
     }
     $meta = shopp_meta($this->id, 'purchase', 'registration');
     return $meta;
 }
Example #3
0
 /**
  * Handles saving updates from the product editor
  *
  * Saves all product related information which includes core product data
  * and supporting elements such as images, digital downloads, tags,
  * assigned categories, specs and pricing variations.
  *
  * @author Jonathan Davis
  * @since 1.0
  *
  * @param Product $Product
  * @return void
  **/
 public function save(ShoppProduct $Product)
 {
     check_admin_referer('shopp-save-product');
     if (!current_user_can('shopp_products')) {
         wp_die(__('You do not have sufficient permissions to access this page.'));
     }
     ShoppSettings()->saveform();
     // Save workflow setting
     $status = $Product->status;
     // Set publish date
     if ('publish' == $_POST['status']) {
         $publishing = isset($_POST['publish']) ? $_POST['publish'] : array();
         $fields = array('month' => '', 'date' => '', 'year' => '', 'hour' => '', 'minute' => '', 'meridiem' => '');
         $publishdate = join('', array_merge($fields, $publishing));
         if (!empty($publishdate)) {
             $publish =& $_POST['publish'];
             if ($publish['meridiem'] == "PM" && $publish['hour'] < 12) {
                 $publish['hour'] += 12;
             }
             $publish = mktime($publish['hour'], $publish['minute'], 0, $publish['month'], $publish['date'], $publish['year']);
             $Product->status = 'future';
             unset($_POST['status']);
         } else {
             unset($_POST['publish']);
             // Auto set the publish date if not set (or more accurately, if set to an irrelevant timestamp)
             if ($Product->publish <= 86400) {
                 $Product->publish = null;
             }
         }
     } else {
         unset($_POST['publish']);
         $Product->publish = 0;
     }
     // Set a unique product slug
     if (empty($Product->slug)) {
         $Product->slug = sanitize_title($_POST['name']);
     }
     $Product->slug = wp_unique_post_slug($Product->slug, $Product->id, $Product->status, ShoppProduct::posttype(), 0);
     $Product->featured = 'off';
     if (isset($_POST['content'])) {
         $_POST['description'] = $_POST['content'];
     }
     $Product->updates($_POST, array('meta', 'categories', 'prices', 'tags'));
     do_action('shopp_pre_product_save');
     $Product->save();
     // Remove deleted images
     if (!empty($_POST['deleteImages'])) {
         $deletes = array();
         if (strpos($_POST['deleteImages'], ",") !== false) {
             $deletes = explode(',', $_POST['deleteImages']);
         } else {
             $deletes = array($_POST['deleteImages']);
         }
         $Product->delete_images($deletes);
     }
     // Update image data
     if (!empty($_POST['images']) && is_array($_POST['images'])) {
         $Product->link_images($_POST['images']);
         $Product->save_imageorder($_POST['images']);
         if (!empty($_POST['imagedetails'])) {
             $Product->update_images($_POST['imagedetails']);
         }
     }
     // Update Prices
     if (!empty($_POST['price']) && is_array($_POST['price'])) {
         // Delete prices that were marked for removal
         if (!empty($_POST['deletePrices'])) {
             $deletes = array();
             if (strpos($_POST['deletePrices'], ",")) {
                 $deletes = explode(',', $_POST['deletePrices']);
             } else {
                 $deletes = array($_POST['deletePrices']);
             }
             foreach ($deletes as $option) {
                 $Price = new ShoppPrice($option);
                 $Price->delete();
             }
         }
         $Product->resum();
         // Save prices that there are updates for
         foreach ($_POST['price'] as $i => $priceline) {
             if (empty($priceline['id'])) {
                 $Price = new ShoppPrice();
                 $priceline['product'] = $Product->id;
             } else {
                 $Price = new ShoppPrice($priceline['id']);
             }
             $priceline['sortorder'] = array_search($i, $_POST['sortorder']) + 1;
             $priceline['shipfee'] = Shopp::floatval($priceline['shipfee']);
             if (isset($priceline['recurring']['trialprice'])) {
                 $priceline['recurring']['trialprice'] = Shopp::floatval($priceline['recurring']['trialprice']);
             }
             if ($Price->stock != $priceline['stocked']) {
                 $priceline['stock'] = (int) $priceline['stocked'];
                 do_action('shopp_stock_product', $priceline['stock'], $Price, $Price->stock, $Price->stocklevel);
             } else {
                 unset($priceline['stocked']);
             }
             $Price->updates($priceline);
             $Price->save();
             // Save 'price' meta records after saving the price record
             if (isset($priceline['dimensions']) && is_array($priceline['dimensions'])) {
                 $priceline['dimensions'] = array_map(array('Shopp', 'floatval'), $priceline['dimensions']);
             }
             $settings = array('donation', 'recurring', 'membership', 'dimensions');
             $priceline['settings'] = array();
             foreach ($settings as $setting) {
                 if (!isset($priceline[$setting])) {
                     continue;
                 }
                 $priceline['settings'][$setting] = $priceline[$setting];
             }
             if (!empty($priceline['settings'])) {
                 shopp_set_meta($Price->id, 'price', 'settings', $priceline['settings']);
             }
             if (!empty($priceline['options'])) {
                 shopp_set_meta($Price->id, 'price', 'options', $priceline['options']);
             }
             $Product->sumprice($Price);
             if (!empty($priceline['download'])) {
                 $Price->attach_download($priceline['download']);
             }
             if (!empty($priceline['downloadpath'])) {
                 // Attach file specified by URI/path
                 if (!empty($Price->download->id) || empty($Price->download) && $Price->load_download()) {
                     $File = $Price->download;
                 } else {
                     $File = new ProductDownload();
                 }
                 $stored = false;
                 $tmpfile = sanitize_path($priceline['downloadpath']);
                 $File->storage = false;
                 $Engine = $File->engine();
                 // Set engine from storage settings
                 $File->parent = $Price->id;
                 $File->context = "price";
                 $File->type = "download";
                 $File->name = !empty($priceline['downloadfile']) ? $priceline['downloadfile'] : basename($tmpfile);
                 $File->filename = $File->name;
                 if ($File->found($tmpfile)) {
                     $File->uri = $tmpfile;
                     $stored = true;
                 } else {
                     $stored = $File->store($tmpfile, 'file');
                 }
                 if ($stored) {
                     $File->readmeta();
                     $File->save();
                 }
             }
             // END attach file by path/uri
         }
         // END foreach()
         unset($Price);
     }
     // END if (!empty($_POST['price']))
     $Product->load_sold($Product->id);
     // Refresh accurate product sales stats
     $Product->sumup();
     // Update taxonomies after pricing summary is generated
     // Summary table entry is needed for ProductTaxonomy::recount() to
     // count properly based on aggregate product inventory, see #2968
     foreach (get_object_taxonomies(Product::$posttype) as $taxonomy) {
         $tags = '';
         $taxonomy_obj = get_taxonomy($taxonomy);
         if (isset($_POST['tax_input']) && isset($_POST['tax_input'][$taxonomy])) {
             $tags = $_POST['tax_input'][$taxonomy];
             if (is_array($tags)) {
                 // array = hierarchical, string = non-hierarchical.
                 $tags = array_filter($tags);
             }
         }
         if (current_user_can($taxonomy_obj->cap->assign_terms)) {
             wp_set_post_terms($Product->id, $tags, $taxonomy);
         }
     }
     // Ensure taxonomy counts are updated on status changes, see #2968
     if ($status != $_POST['status']) {
         $Post = new StdClass();
         $Post->ID = $Product->id;
         $Post->post_type = ShoppProduct::$posttype;
         wp_transition_post_status($_POST['status'], $Product->status, $Post);
     }
     if (!empty($_POST['meta']['options'])) {
         $_POST['meta']['options'] = stripslashes_deep($_POST['meta']['options']);
     } else {
         $_POST['meta']['options'] = false;
     }
     // No variation options at all, delete all variation-pricelines
     if (!empty($Product->prices) && is_array($Product->prices) && (empty($_POST['meta']['options']['v']) || empty($_POST['meta']['options']['a']))) {
         foreach ($Product->prices as $priceline) {
             // Skip if not tied to variation options
             if ($priceline->optionkey == 0) {
                 continue;
             }
             if (empty($_POST['meta']['options']['v']) && $priceline->context == "variation" || empty($_POST['meta']['options']['a']) && $priceline->context == "addon") {
                 $Price = new ShoppPrice($priceline->id);
                 $Price->delete();
             }
         }
     }
     // Handle product spec/detail data
     if (!empty($_POST['details']) || !empty($_POST['deletedSpecs'])) {
         // Delete specs queued for removal
         $ids = array();
         $deletes = array();
         if (!empty($_POST['deletedSpecs'])) {
             if (strpos($_POST['deleteImages'], ",") !== false) {
                 $deletes = explode(',', $_POST['deleteImages']);
             } else {
                 $deletes = array($_POST['deletedSpecs']);
             }
             $ids = db::escape($_POST['deletedSpecs']);
             $Spec = new Spec();
             db::query("DELETE FROM {$Spec->_table} WHERE id IN ({$ids})");
         }
         if (is_array($_POST['details'])) {
             foreach ($_POST['details'] as $i => $spec) {
                 if (in_array($spec['id'], $deletes)) {
                     continue;
                 }
                 if (isset($spec['new'])) {
                     $Spec = new Spec();
                     $spec['id'] = '';
                     $spec['parent'] = $Product->id;
                 } else {
                     $Spec = new Spec($spec['id']);
                 }
                 $spec['sortorder'] = array_search($i, $_POST['details-sortorder']) + 1;
                 $Spec->updates($spec);
                 $Spec->save();
             }
         }
     }
     // Save any meta data
     if (isset($_POST['meta']) && is_array($_POST['meta'])) {
         foreach ($_POST['meta'] as $name => $value) {
             if (isset($Product->meta[$name])) {
                 $Meta = $Product->meta[$name];
                 if (is_array($Meta)) {
                     $Meta = reset($Product->meta[$name]);
                 }
             } else {
                 $Meta = new ShoppMetaObject(array('parent' => $Product->id, 'context' => 'product', 'type' => 'meta', 'name' => $name));
             }
             $Meta->parent = $Product->id;
             $Meta->name = $name;
             $Meta->value = $value;
             $Meta->save();
         }
     }
     $Product->load_data();
     // Reload data so everything is fresh for shopp_product_saved
     do_action_ref_array('shopp_product_saved', array(&$Product));
     unset($Product);
 }
Example #4
0
 /**
  * Creates a duplicate product of this product's data
  *
  * @author Jonathan Davis
  * @since 1.0
  * @version 1.2
  *
  * @return void
  **/
 public function duplicate()
 {
     $original = $this->id;
     $this->load_data();
     // Load everything
     $this->id = '';
     $this->name = $this->name . ' ' . __('copy', 'Shopp');
     $slug = sanitize_title_with_dashes($this->name);
     $this->slug = wp_unique_post_slug($slug, $this->id, $this->status, ShoppProduct::posttype(), 0);
     $this->created = '';
     $this->modified = '';
     $this->status = 'draft';
     // Set duplicated product to draft status
     $this->save();
     // Copy prices
     foreach ($this->prices as $price) {
         $Price = new ShoppPrice();
         $Price->copydata($price);
         $Price->product = $this->id;
         $Price->save();
         // Copy Price record meta entries
         $meta = array('donation', 'recurring', 'membership', 'dimensions');
         $priceline['settings'] = array();
         $settings = array();
         foreach ($meta as $name) {
             if (isset($price->{$name})) {
                 $settings[$name] = $price->{$name};
             }
         }
         shopp_set_meta($Price->id, 'price', 'settings', $settings);
         shopp_set_meta($Price->id, 'price', 'options', $price->options);
     }
     // Copy taxonomy assignments
     $terms = array();
     $taxonomies = get_object_taxonomies(self::$posttype);
     $assignments = wp_get_object_terms($original, $taxonomies, array('fields' => 'all_with_object_id'));
     foreach ($assignments as $term) {
         // Map WP taxonomy data to object meta
         if (!isset($term->term_id) || empty($term->term_id)) {
             continue;
         }
         // Skip invalid entries
         if (!isset($term->taxonomy) || empty($term->taxonomy)) {
             continue;
         }
         // Skip invalid entries
         if (!isset($terms[$term->taxonomy])) {
             $terms[$term->taxonomy] = array();
         }
         $terms[$term->taxonomy][] = (int) $term->term_id;
     }
     foreach ($terms as $taxonomy => $termlist) {
         wp_set_object_terms($this->id, $termlist, $taxonomy);
     }
     $metadata = array('specs', 'images', 'settings', 'meta');
     foreach ($metadata as $metaset) {
         if (!is_array($this->{$metaset})) {
             continue;
         }
         foreach ($this->{$metaset} as $metaobjects) {
             if (!is_array($metaobjects)) {
                 $metaobjects = array($metaobjects);
             }
             foreach ($metaobjects as $meta) {
                 $ObjectClass = get_class($meta);
                 $Meta = new $ObjectClass();
                 $Meta->copydata($meta);
                 $Meta->parent = $this->id;
                 $Meta->save();
             }
         }
     }
     // Duplicate summary (primarily for summary settings data)
     $Summary = new ProductSummary($original);
     $Summary->product = $this->id;
     $Summary->sold = $Summary->grossed = $Summary->stock = 0;
     $Summary->save();
     // Re-summarize product pricing
     $this->load_data(array('prices', 'summary'));
     // Duplicate (WP) post meta data
     foreach (get_post_custom($original) as $key => $values) {
         foreach ((array) $values as $value) {
             add_post_meta($this->id, $key, $value);
         }
     }
 }
Example #5
0
/**
 * Create or update a new product meta record
 *
 * @api
 * @since 1.2
 *
 * @param int $product (required on creation/update) product object to create/update the meta record on
 * @param string $name (required on update) the name of the meta entry, more specific than type
 * @param mixed $value (optional default: false) the value stored to the meta entry
 * @param string $type (optional default: meta) the type or classification of the meta data
 * @param string $valuetype (optional default: 'value') 'numeral' or 'value', if the value is numeric, 'numeric' will store in numeric field.
 * @return bool true on successful save or update, fail on failure
 **/
function shopp_set_product_meta($product = false, $name = false, $value = false, $type = 'meta', $valuetype = 'value')
{
    return shopp_set_meta($product, 'product', $name, $value, $type, $valuetype);
}
Example #6
0
/**
 * shopp_product_set_addon_options - Creates a complete set of addon product options on a specified product, by letting you
 * specify the set of options types, and corresponding options.  This function will create new addon options in the database and
 * will attach them to the specified product.
 *
 * @api
 * @since 1.2
 *
 * @param int $product (required) The product id of the product that you wish to add the addon options to.
 * @param array $options (Description...) A two dimensional array describing the addon options.
 * The outer array is keyed on the name of the option type (Framing, Matting, Glass, etc.)
 * The inner contains the corresponding option values.
 * Ex. $options = array( 'Framing' => array('Wood', 'Gold'), 'Glass' => array('Anti-glare', 'UV Protectant') );
 * @return array addon Price objects that have been created on the product.
 *
 **/
function shopp_product_set_addon_options($product = false, $options = array(), $summary = 'save')
{
    if (!$product || empty($options)) {
        shopp_debug(__FUNCTION__ . " failed: Missing required parameters.");
        return false;
    }
    $Product = new ShoppProduct($product);
    if (empty($Product->id)) {
        shopp_debug(__FUNCTION__ . " failed: Product not found for product id {$product}.");
        return false;
    }
    $Product->load_data(array('summary'));
    // clean up old variations
    $table = ShoppDatabaseObject::tablename(ShoppPrice::$table);
    db::query("DELETE FROM {$table} WHERE product={$product} AND context='addon'");
    $prices = array();
    $mapping = array();
    foreach ($options as $type => $opts) {
        foreach ($opts as $index => $option) {
            $addon = array($type => $option);
            $Price = new ShoppPrice();
            $Price->type = 'Shipped';
            $Price->product = $product;
            $Price->context = 'addon';
            $Price->sortorder = $index + 2;
            // default price sort order is 1, start at minimum 2 #2847
            list($Price->optionkey, $Price->options, $Price->label, $mapping) = $Product->optionmap($addon, $options, 'addon');
            $Price->save();
            shopp_set_meta($Price->id, 'price', 'options', $Price->options);
            $prices[] = $Price;
        }
    }
    $metaopts = shopp_product_meta($product, 'options');
    $metaopts['a'] = array();
    $i = 1;
    foreach ($options as $optname => $option) {
        if (!isset($metaopts['a'][$i])) {
            $metaopts['a'][$i] = array('id' => $i, 'name' => $optname, 'options' => array());
        }
        foreach ($option as $value) {
            $metaopts['a'][$i]['options'][$mapping[$optname][$value]] = array('id' => $mapping[$optname][$value], 'name' => $value, 'linked' => "off");
        }
        $i++;
    }
    shopp_set_product_meta($product, 'options', $metaopts);
    $Product->addons = "on";
    if ('save' == $summary) {
        $Product->sumup();
    }
    return $prices;
}
Example #7
0
 /**
  * Generate and store all the attendees information for a new order.
  *
  * @param OrderEventMessage $order_event
  */
 public function generate_tickets(OrderEventMessage $order_event)
 {
     $order = shopp_order($order_event->order);
     $has_tickets = false;
     $optout = (bool) shopp_meta($order->id, 'purchase', self::ATTENDEE_OPTOUT_KEY);
     // Iterate over each product
     foreach ($order->purchased as $item) {
         $order_attendee_id = 0;
         $event_id = $this->get_related_event($item->product);
         if (empty($event_id)) {
             continue;
         }
         // Iterate over all the amount of tickets purchased (for this product)
         $quantity = intval($item->quantity);
         for ($i = 0; $i < $quantity; $i++) {
             $attendee = array('post_status' => 'publish', 'post_title' => $order->id . ' | ' . $item->name . ' | ' . ($i + 1), 'post_type' => self::ATTENDEE_OBJECT, 'ping_status' => 'closed');
             // Insert individual ticket purchased
             $attendee_id = wp_insert_post($attendee);
             update_post_meta($attendee_id, self::ATTENDEE_PRODUCT_KEY, $item->product);
             update_post_meta($attendee_id, self::ATTENDEE_ORDER_KEY, $order->id);
             update_post_meta($attendee_id, self::ATTENDEE_EVENT_KEY, $event_id);
             update_post_meta($attendee_id, $this->security_code, $this->generate_security_code($order->id, $attendee_id));
             update_post_meta($attendee_id, self::ATTENDEE_OPTOUT_KEY, $optout);
             /**
              * Shopp specific action fired when a Shopp-driven attendee ticket for an event is generated
              *
              * @param $attendee_id ID of attendee ticket
              * @param $event_id ID of event
              * @param $order_id Shopp order ID
              * @param $product_id Shopp product ID
              */
             do_action('event_tickets_shopp_attendee_created', $attendee_id, $event_id, $order, $item->product);
             /**
              * Action fired when an attendee ticket is generated
              *
              * @param $attendee_id ID of attendee ticket
              * @param $order_id ID of order
              * @param $product_id Product ID attendee is "purchasing"
              * @param $order_attendee_id Attendee # for order
              */
             do_action('event_tickets_shopp_ticket_created', $attendee_id, $order->id, $item->product, $order_attendee_id);
             $this->record_attendee_user_id($attendee_id);
             $order_attendee_id++;
         }
         $has_tickets = true;
     }
     if ($has_tickets) {
         shopp_set_meta($order->id, 'purchase', $this->order_has_tickets, true);
         $this->send_tickets($order);
     }
 }
Example #8
0
 /**
  * Generate and store all the attendees information for a new order.
  *
  * @param OrderEventMessage $order_event
  */
 public function generate_tickets(OrderEventMessage $order_event)
 {
     $order = shopp_order($order_event->order);
     $has_tickets = false;
     // Iterate over each product
     foreach ($order->purchased as $item) {
         $event_id = $this->get_related_event($item->product);
         if (empty($event_id)) {
             continue;
         }
         // Iterate over all the amount of tickets purchased (for this product)
         $quantity = intval($item->quantity);
         for ($i = 0; $i < $quantity; $i++) {
             $attendee = array('post_status' => 'publish', 'post_title' => $order->id . ' | ' . $item->name . ' | ' . ($i + 1), 'post_type' => self::ATTENDEE_OBJECT, 'ping_status' => 'closed');
             // Insert individual ticket purchased
             $attendee_id = wp_insert_post($attendee);
             update_post_meta($attendee_id, self::ATTENDEE_PRODUCT_KEY, $item->product);
             update_post_meta($attendee_id, self::ATTENDEE_ORDER_KEY, $order->id);
             update_post_meta($attendee_id, self::ATTENDEE_EVENT_KEY, $event_id);
             update_post_meta($attendee_id, $this->security_code, $this->generate_security_code($order->id, $attendee_id));
         }
         $has_tickets = true;
     }
     if ($has_tickets) {
         shopp_set_meta($order->id, 'purchase', $this->order_has_tickets, true);
         $this->send_tickets($order);
     }
 }