Пример #1
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);
 }
Пример #2
0
/**
 * Comprehensive product creation through Product Developer API.
 *
 * This function will do everything needed for creating a product except
 * attach product images and products. That is done in the Asset API. :)
 * You should be able to build an importer from another system using this function.
 *
 * It is also possible to update an existing product (by passing the
 * existing id as part of the $data array) or else you can alternatively
 * use shopp_update_product() for that.
 *
 * @todo possibly remove the capability of passing in an id to update a product
 *
 * @api
 * @since 1.2
 *
 * @param array $data (required) associative array structure containing a single product definition, see _validate_product_data for how this array is structured/validated.
 * @return Product the created product object, or boolean false on a failure.
 **/
function shopp_add_product($data = array())
{
    if (empty($data)) {
        shopp_debug(__FUNCTION__ . " failed: Empty data parameter.");
        return false;
    }
    $problems = _validate_product_data($data);
    if (!empty($problems)) {
        shopp_debug("Problems detected: " . Shopp::object_r($problems));
        return false;
    }
    $Product = new ShoppProduct();
    // Set Product publish status
    if (isset($data['publish'])) {
        $Product->publish = _shopp_product_publish_date($data['publish']);
        if ($Product->publish > 0) {
            $Product->status = 'future';
        }
    }
    // Set Product name
    if (empty($data['name'])) {
        shopp_debug(__FUNCTION__ . " failed: Missing product name.");
    }
    $Product->name = $data['name'];
    // Set Product slug
    if (!empty($data['slug'])) {
        $Product->slug = $data['slug'];
    }
    if (empty($Product->slug)) {
        $Product->slug = sanitize_title($Product->name);
    }
    $Product->slug = wp_unique_post_slug($Product->slug, $Product->id, $Product->status, ShoppProduct::posttype(), 0);
    $Product->updates($data, array('meta', 'categories', 'prices', 'tags', 'publish'));
    $Product->save();
    ShoppProduct::publishset(array($Product->id), $data['publish']['flag'] ? 'publish' : 'draft');
    if (empty($Product->id)) {
        shopp_debug(__FUNCTION__ . " failed: Failure to create new Product object.");
        return false;
    }
    // Product-wide settings
    $Product->featured = isset($data['featured']) && true === $data['featured'] ? "on" : "off";
    $Product->variants = isset($data['variants']) ? "on" : "off";
    $Product->addons = isset($data['addons']) ? "on" : "off";
    if (isset($data['packaging'])) {
        $packaging_set = shopp_product_set_packaging($Product->id, $data['packaging']);
        if (!$packaging_set) {
            shopp_debug(__FUNCTION__ . " failed: Failure to set packaging setting.");
            return false;
        }
    }
    // Save Taxonomies
    // Categories
    if (isset($data['categories']) && isset($data['categories']['terms'])) {
        $cats_set = shopp_product_add_categories($Product->id, $data['categories']['terms']);
        if (!$cats_set) {
            shopp_debug(__FUNCTION__ . " failed: Failure to add product categories to product.");
            return false;
        }
    }
    // Tags
    if (isset($data['tags']) && isset($data['tags']['terms'])) {
        $tags_set = shopp_product_add_tags($Product->id, $data['tags']['terms']);
        if (!$tags_set) {
            shopp_debug(__FUNCTION__ . " failed: Failure to add product tags to product.");
            return false;
        }
    }
    // Terms
    if (isset($data['terms']) && isset($data['terms']['terms']) && isset($data['terms']['taxonomy'])) {
        $terms_set = shopp_product_add_terms($Product->id, $data['terms']['terms'], $data['terms']['taxonomy']);
        if (!$terms_set) {
            shopp_debug(__FUNCTION__ . " failed: Failure to add product taxonomy terms to product.");
            return false;
        }
    }
    // Create Specs
    if (isset($data['specs'])) {
        $specs_set = shopp_product_set_specs($Product->id, $data['specs']);
        if (!$specs_set) {
            shopp_debug(__FUNCTION__ . " failed: Failure to add product specs to product.");
            return false;
        }
    }
    $subjects = array();
    $prices = array();
    // Create Prices
    if (isset($data['single'])) {
        if (!empty($data['single'])) {
            $subjects['product'] = array($data['single']);
        }
    } else {
        if (isset($data['variants'])) {
            // Construct and Populate variants
            if (!isset($data['variants']['menu']) || empty($data['variants']['menu'])) {
                shopp_debug(__FUNCTION__ . " failed: variants menu is empty.");
                return false;
            }
            $new_variants = shopp_product_set_variant_options($Product->id, $data['variants']['menu'], false);
            $pricekeys = $prices = array();
            foreach ($new_variants as $Price) {
                $prices[$Price->id] = $pricekeys[$Price->optionkey] = $Price;
            }
            if (!$prices) {
                shopp_debug(__FUNCTION__ . " failed: Unable to set variant options.");
                return false;
            }
            $subjects['variants'] = $data['variants'];
        }
    }
    // Create the "product" Price
    $Price = new ShoppPrice();
    $Price->label = __('Price & Delivery', 'Shopp');
    $Price->context = 'product';
    $Price->product = $Product->id;
    if (isset($subjects['variants'])) {
        $Price->type = 'N/A';
    }
    // disabled
    $Price->save();
    $prices[$Price->id] = $productprice = $Price;
    // Create Addons
    if (isset($data['addons'])) {
        if (!isset($data['addons']['menu']) || empty($data['addons']['menu'])) {
            shopp_debug(__FUNCTION__ . " failed: addons menu is empty");
            return false;
        }
        $new_addons = shopp_product_set_addon_options($Product->id, $data['addons']['menu'], false);
        $addon_prices = array();
        foreach ($new_addons as $Addon) {
            $addon_prices[$Addon->id] = $Addon;
        }
        if (!$addon_prices) {
            shopp_debug(__FUNCTION__ . " failed: Unable to set addon options.");
            return false;
        }
        $prices = $prices + $addon_prices;
        $subjects['addons'] = $data['addons'];
    }
    $contexts = array('addons' => 'addon', 'product' => 'product', 'variants' => 'variant');
    foreach ($subjects as $pricetype => $variants) {
        // apply settings for each priceline
        foreach ($variants as $key => $variant) {
            if (!is_numeric($key)) {
                continue;
            }
            $price = null;
            if ('product' == $pricetype) {
                $price = $productprice->id;
            } else {
                // 'option' => 'array',	// array option example: Color=>Blue, Size=>Small
                if (!isset($variant['option']) || empty($variant['option'])) {
                    shopp_debug(__FUNCTION__ . " failed: variant {$key} missing variant options.");
                    return false;
                }
                list($optionkey, $options, $label, $mapping) = $Product->optionmap($variant['option'], $variants['menu'], 'variants' == $pricetype ? 'variant' : 'addon');
                if ('variants' == $pricetype && isset($pricekeys[$optionkey])) {
                    $price = $pricekeys[$optionkey]->id;
                } else {
                    // Find the correct Price
                    foreach ($addon_prices as $index => $Price) {
                        if ($Price->options == $options && $Price->label == $label) {
                            $price = $index;
                            break;
                        }
                    }
                }
            }
            if (null === $price || !isset($prices[$price])) {
                shopp_debug(__FUNCTION__ . " failed: Variant {$key} not valid for this option set.");
                return false;
            }
            // modify each priceline
            $prices[$price] = shopp_product_set_variant($prices[$price], $variant, $contexts[$pricetype]);
            if (!$prices[$price]) {
                shopp_debug(__FUNCTION__ . " failed: Product variant setup failed.");
                return false;
            }
            // save priceline settings
            if (isset($prices[$price]->settings)) {
                shopp_set_meta($prices[$price]->id, 'price', 'settings', $prices[$price]->settings);
            }
            // We have everything we need to complete this price line
            $prices[$price]->save();
        }
        //end variants foreach
    }
    // end subjects foreach
    // Reset rollup figures for prices.
    $Product->resum();
    // Calculates aggregate product stats
    // foreach ( $prices as $Price ) {
    // 	$Product->sumprice($Price);
    // }
    // Skeleton summary
    $Summary = new ProductSummary();
    $sum_props = array_keys($Summary->_datatypes);
    // init default summary items
    foreach ($sum_props as $prop) {
        if (!isset($Product->{$prop})) {
            $Product->{$prop} = NULL;
        }
    }
    // Process pricing stats
    $records = null;
    foreach ($prices as $Price) {
        $Product->pricing($records, $Price);
    }
    // Saves generated stats to the product summary
    $Product->sumup();
    return shopp_product($Product->id);
}