Example #1
0
 /**
  * Convert WP queried Shopp product post types to a Shopp Product
  *
  * @author Jonathan Davis
  * @since 1.2
  *
  * @param object $wp The main WP object from the 'wp' action
  * @return void
  **/
 public function loaded(WP $wp)
 {
     if (!is_shopp_product()) {
         return;
     }
     // Get the loaded object (a Shopp product post type)
     global $wp_the_query;
     $object = $wp_the_query->get_queried_object();
     // Populate so we don't have to req-uery
     $Product = new ShoppProduct();
     $Product->populate($object);
     ShoppProduct($Product);
     $this->Requested = $Product;
 }
Example #2
0
 /**
  * Detects a Shopp post type editor.
  *
  * @since 1.4
  *
  * @return bool True if the admin page is a Shopp post-type editor, false otherwise.
  **/
 public static function posteditor()
 {
     return ShoppProduct::posttype() == ShoppFlow()->request('post_type') && 'edit' == ShoppFlow()->request('action');
 }
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
/**
 * shopp_add_product_download
 *
 * Add product download file to a product/variation.
 *
 * @api
 * @since 1.2
 *
 * @param int $product id of the product the download asset will be added to
 * @param string $file full or correct relative path to the download file asset.
 * @param int $variant id of the variant the download asset will be attached to.  For products with variants, this is a required parameter.
 * @return mixed false of failure, the new download asset id on success
 **/
function shopp_add_product_download($product, $file, $variant = false)
{
    if (empty($product) || empty($file)) {
        shopp_debug(__FUNCTION__ . ' failed: One or more missing parameters.');
        return false;
    }
    $File = new ProductDownload();
    $instore = $File->found($file);
    if (!$instore && (!is_file($file) || !is_readable($file))) {
        shopp_debug(__FUNCTION__ . " failed for file {$file}: File missing or unreadable.");
        return false;
    }
    $Product = new ShoppProduct($product);
    if (empty($Product->id)) {
        shopp_debug(__FUNCTION__ . " failed for file {$file}: No such product with id {$product}.");
        return false;
    }
    $Product->load_data(array('summary', 'prices'));
    if ("on" == $Product->variants && false === $variant) {
        shopp_debug(__FUNCTION__ . " failed for file {$file}: You must specify the variant id parameter for product {$product}.");
        return false;
    }
    $Price = reset($Product->prices);
    if (empty($Price->id)) {
        shopp_debug(__FUNCTION__ . " failed for file {$file}: Failed to load product variants.");
        return false;
    }
    if ($variant) {
        $Price = false;
        foreach ($Product->prices as $Price) {
            if ($variant == $Price->id) {
                break;
            }
        }
        if (false === $Price) {
            shopp_debug(__FUNCTION__ . " failed for file {$file}: You must specify a valid variant id parameter for product {$product}.");
            return false;
        }
    }
    // Save the uploaded file
    $File->load(array('type' => 'download', 'parent' => $Price->id));
    $File->parent = $Price->id;
    $File->context = "price";
    $File->type = "download";
    $File->name = basename($file);
    $File->filename = $File->name;
    if (!$instore) {
        $File->mime = file_mimetype($file, $File->name);
        $File->size = filesize($file);
        $File->store($file, 'file');
    } else {
        $File->uri = $file;
        $File->readmeta();
    }
    $File->save();
    if ($File->id) {
        return $File->id;
    }
    shopp_debug(__FUNCTION__ . " failed for file {$file}");
    return false;
}
Example #5
0
 public function load(array $options = array())
 {
     $thisclass = get_class($this);
     $slug = isset($this->slug) ? $this->slug : sanitize_key($thisclass);
     $Storefront = ShoppStorefront();
     $Shopping = ShoppShopping();
     $Processing = new ShoppProduct();
     $summary_table = ShoppDatabaseObject::tablename(ProductSummary::$table);
     $defaults = array('columns' => false, 'useindex' => false, 'joins' => array(), 'where' => array(), 'groupby' => false, 'orderby' => false, 'having' => array(), 'limit' => false, 'order' => false, 'page' => false, 'paged' => false, 'nostock' => null, 'pagination' => true, 'published' => true, 'ids' => false, 'adjacent' => false, 'product' => false, 'load' => array('coverimages'), 'inventory' => false, 'taxquery' => false, 'debug' => false);
     $loading = array_merge($defaults, $options);
     $loading = apply_filters("shopp_collection_load_options", $loading);
     $loading = apply_filters("shopp_{$slug}_collection_load_options", $loading);
     extract($loading);
     // Setup pagination
     $this->paged = false;
     $this->pagination = false === $paged ? shopp_setting('catalog_pagination') : $paged;
     $page = false === $page ? get_query_var('paged') : $page;
     $this->page = (int) $page > 0 || preg_match('/(0\\-9|[A-Z])/', $page) ? $page : 1;
     // Hard product limit per category to keep resources "reasonable"
     $hardlimit = apply_filters('shopp_category_products_hardlimit', 1000);
     // Enforce the where parameter as an array
     if (!is_array($where)) {
         return shopp_debug('The "where" parameter for ' . __METHOD__ . ' must be formatted as an array.');
     }
     // Inventory filtering
     if (shopp_setting_enabled('inventory') && (is_null($nostock) && !shopp_setting_enabled('outofstock_catalog') || !is_null($nostock) && !Shopp::str_true($nostock))) {
         $where[] = "( s.inventory='off' OR (s.inventory='on' AND s.stock > 0) )";
     }
     if (Shopp::str_true($published)) {
         $where[] = "p.post_status='publish'";
     }
     // Multiple taxonomy queries
     if (is_array($taxquery)) {
         $tqdefaults = array('relation' => 'AND', 'include_children' => true);
         $taxquery = array_merge($tqdefaults, $taxquery);
         $TQ = new WP_Tax_Query($taxquery);
         $sql = $TQ->get_sql($Processing->_table, 'ID');
         unset($TQ);
         $joins['taxquery'] = self::taxquery($sql['join']);
         $where[] = self::taxquery($sql['where']);
     }
     // Sort Order
     if (!$orderby) {
         $titlesort = "p.post_title ASC";
         $defaultsort = empty($order) ? $titlesort : $order;
         // Define filterable built-in sort methods (you're welcome)
         $sortmethods = apply_filters('shopp_collection_sort_methods', array('bestselling' => "s.sold DESC,{$titlesort}", 'highprice' => "maxprice DESC,{$titlesort}", 'lowprice' => "minprice ASC,{$titlesort}", 'newest' => "p.post_date DESC,{$titlesort}", 'oldest' => "p.post_date ASC,{$titlesort}", 'random' => "RAND(" . crc32($Shopping->session) . ")", 'chaos' => "RAND(" . time() . ")", 'reverse' => "p.post_title DESC", 'title' => $titlesort, 'custom' => is_subclass_of($this, 'ProductTaxonomy') ? "tr.term_order ASC,{$titlesort}" : $defaultsort, 'recommended' => is_subclass_of($this, 'ProductTaxonomy') ? "tr.term_order ASC,{$titlesort}" : $defaultsort, 'default' => $defaultsort));
         // Handle valid user browsing sort change requests
         if (isset($_REQUEST['sort']) && !empty($_REQUEST['sort']) && array_key_exists(strtolower($_REQUEST['sort']), $sortmethods)) {
             $Storefront->browsing['sortorder'] = strtolower($_REQUEST['sort']);
         }
         // Collect sort setting sources (Shopp admin setting, User browsing setting, programmer specified setting)
         $sortsettings = array(shopp_setting('default_product_order'), isset($Storefront->browsing['sortorder']) ? $Storefront->browsing['sortorder'] : false, !empty($order) ? $order : false);
         // Go through setting sources to determine most applicable setting
         $sorting = 'title';
         foreach ($sortsettings as $setting) {
             if (!empty($setting) && isset($sortmethods[strtolower($setting)])) {
                 $sorting = strtolower($setting);
             }
         }
         $orderby = $sortmethods[$sorting];
     }
     if (empty($orderby)) {
         $orderby = 'p.post_title ASC';
     }
     // Pagination
     if (empty($limit)) {
         if ($this->pagination > 0 && is_numeric($this->page) && Shopp::str_true($pagination)) {
             if (!$this->pagination || $this->pagination < 0) {
                 $this->pagination = $hardlimit;
             }
             $start = $this->pagination * ($this->page - 1);
             $limit = "{$start},{$this->pagination}";
         } else {
             $limit = $hardlimit;
         }
         $limited = false;
         // Flag that the result set does not have forced limits
     } else {
         $limited = true;
     }
     // The result set has forced limits
     // Core query components
     // Load core product data and product summary columns
     $cols = array('p.ID', 'p.post_title', 'p.post_name', 'p.post_excerpt', 'p.post_status', 'p.post_date', 'p.post_modified', 's.modified AS summed', 's.sold', 's.grossed', 's.maxprice', 's.minprice', 's.ranges', 's.taxed', 's.stock', 's.lowstock', 's.inventory', 's.featured', 's.variants', 's.addons', 's.sale');
     if ($ids) {
         $cols = array('p.ID');
     }
     $columns = "SQL_CALC_FOUND_ROWS " . join(',', $cols) . ($columns !== false ? ',' . $columns : '');
     $table = "{$Processing->_table} AS p";
     $where[] = "p.post_type='" . ShoppProduct::posttype() . "'";
     $joins[$summary_table] = "LEFT OUTER JOIN {$summary_table} AS s ON s.product=p.ID";
     $options = compact('columns', 'useindex', 'table', 'joins', 'where', 'groupby', 'having', 'limit', 'orderby');
     // Alphabetic pagination
     if ('alpha' === $pagination || preg_match('/(0\\-9|[A-Z])/', $page)) {
         // Setup Roman alphabet navigation
         $alphanav = array_merge(array('0-9'), range('A', 'Z'));
         $this->alpha = array_combine($alphanav, array_fill(0, count($alphanav), 0));
         // Setup alphabetized index query
         $a = $options;
         $a['columns'] = "count(DISTINCT p.ID) AS total,IF(LEFT(p.post_title,1) REGEXP '[0-9]',LEFT(p.post_title,1),LEFT(SOUNDEX(p.post_title),1)) AS letter";
         $a['groupby'] = "letter";
         $alphaquery = sDB::select($a);
         $cachehash = 'collection_alphanav_' . md5($alphaquery);
         $cached = Shopp::cache_get($cachehash, 'shopp_collection_alphanav');
         if ($cached) {
             // Load from object cache,  if available
             $this->alpha = $cached;
             $cached = false;
         } else {
             // Run query and cache results
             $expire = apply_filters('shopp_collection_cache_expire', 43200);
             $alpha = sDB::query($alphaquery, 'array', array($this, 'alphatable'));
             Shopp::cache_set($cachehash, $alpha, 'shopp_collection_alphanav', $expire);
         }
         $this->paged = true;
         if ($this->page == 1) {
             $this->page = '0-9';
         }
         $alphafilter = $this->page == "0-9" ? "(LEFT(p.post_title,1) REGEXP '[0-9]') = 1" : "IF(LEFT(p.post_title,1) REGEXP '[0-9]',LEFT(p.post_title,1),LEFT(SOUNDEX(p.post_title),1))='{$this->page}'";
         $options['where'][] = $alphafilter;
     }
     $query = sDB::select(apply_filters('shopp_collection_query', $options));
     if ($debug) {
         echo $query . BR . BR;
     }
     // Load from cached results if available, or run the query and cache the results
     $cachehash = 'collection_' . md5($query);
     $cached = Shopp::cache_get($cachehash, 'shopp_collection');
     if ($cached) {
         $this->products = $cached->products;
         $this->total = $cached->total;
     } else {
         $expire = apply_filters('shopp_collection_cache_expire', 43200);
         $cache = new stdClass();
         if ($ids) {
             $cache->products = $this->products = sDB::query($query, 'array', 'col', 'ID');
         } else {
             $cache->products = $this->products = sDB::query($query, 'array', array($Processing, 'loader'));
         }
         $cache->total = $this->total = sDB::found();
         // If running a limited set, the reported total found should not exceed the limit (but can because of SQL_CALC_FOUND_ROWS)
         // Don't use the limit if it is offset
         if ($limited && false === strpos($limit, ',')) {
             $cache->total = $this->total = min($limit, $this->total);
         }
         Shopp::cache_set($cachehash, $cache, 'shopp_collection', $expire);
     }
     if (false === $this->products) {
         $this->products = array();
     }
     if ($ids) {
         return $this->size() > 0;
     }
     // Finish up pagination construction
     if ($this->pagination > 0 && $this->total > $this->pagination) {
         $this->pages = ceil($this->total / $this->pagination);
         if ($this->pages > 1) {
             $this->paged = true;
         }
     }
     // Load all requested product meta from other data sources
     $Processing->load_data($load, $this->products);
     // If products are missing summary data, resum them
     if (isset($Processing->resum) && !empty($Processing->resum)) {
         $Processing->load_data(array('prices'), $Processing->resum);
     }
     unset($Processing);
     // Free memory
     $this->loaded = true;
     return $this->size() > 0;
 }
Example #6
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 #7
0
 /**
  * Provides overall layout for the product editor interface
  *
  * Makes use of WordPress postboxes to generate panels (box) content
  * containers that are customizable with drag & drop, collapsable, and
  * can be toggled to be hidden or visible in the interface.
  *
  * @author Jonathan Davis
  * @return
  **/
 public function layout()
 {
     $Product = $this->Model;
     $Product->load_data();
     new ShoppAdminProductSaveBox($this, 'side', 'core', array('Product' => $Product, 'posttype' => ShoppProduct::posttype()));
     // Load all Shopp product taxonomies
     foreach (get_object_taxonomies(ShoppProduct::posttype()) as $taxonomy_name) {
         $taxonomy = get_taxonomy($taxonomy_name);
         $label = $taxonomy->labels->name;
         if (is_taxonomy_hierarchical($taxonomy_name)) {
             new ShoppAdminProductCategoriesBox(ShoppProduct::posttype(), 'side', 'core', array('Product' => $Product, 'taxonomy' => $taxonomy_name, 'label' => $label));
         } else {
             new ShoppAdminProductTaggingBox(ShoppProduct::posttype(), 'side', 'core', array('Product' => $Product, 'taxonomy' => $taxonomy_name, 'label' => $label));
         }
     }
     new ShoppAdminProductSettingsBox($this, 'side', 'core', array('Product' => $Product, 'posttype' => ShoppProduct::posttype()));
     new ShoppAdminProductSummaryBox($this, 'normal', 'core', array('Product' => $Product, 'posttype' => ShoppProduct::posttype()));
     new ShoppAdminProductDetailsBox($this, 'normal', 'core', array('Product' => $Product, 'posttype' => ShoppProduct::posttype()));
     new ShoppAdminProductImagesBox($this, 'normal', 'core', array('Product' => $Product, 'posttype' => ShoppProduct::posttype()));
     new ShoppAdminProductPricingBox($this, 'normal', 'core', array('Product' => $Product, 'posttype' => ShoppProduct::posttype()));
 }
Example #8
0
 /**
  * Provides the product weight or the variant weight range
  *
  * @api `shopp('product.weight')`
  * @since 1.2
  *
  * @param string       $result  The output
  * @param array        $options The options
  * - **convert**: `off` (on,off) Enable or disable converting from the product units to the display **unit** setting
  * - **max**: Only show the maximum weight
  * - **min**: Only show the minium weight
  * - **unit**: Set the unit
  * - **units**: `on` (on,off) Include the weight units label
  * @param ShoppProduct $O       The working object
  * @return string The weight or weight range
  **/
 public static function weight($result, $options, $O)
 {
     $defaults = array('convert' => false, 'max' => $O->max['weight'], 'min' => $O->min['weight'], 'units' => true);
     $options = array_merge($defaults, $options);
     extract($options);
     $unit = shopp_setting('weight_unit');
     if (empty($O->prices)) {
         $O->load_data(array('prices'));
     }
     if (!isset($O->min['weight'])) {
         return false;
     }
     if ($convert !== false) {
         $min = convert_unit($min, $convert);
         $max = convert_unit($max, $convert);
         if (is_null($units)) {
             $units = true;
         }
         $unit = $convert;
     }
     $range = false;
     if ($min != $max) {
         $range = array($min, $max);
         sort($range);
     }
     $string = $min == $max ? round($min, 3) : round($range[0], 3) . " - " . round($range[1], 3);
     $string .= Shopp::str_true($units) ? " {$unit}" : "";
     return $string;
 }
Example #9
0
 /**
  * Handles Product/Category slug editor
  *
  * @author Jonathan Davis
  * @since 1.1
  *
  * @return void
  **/
 public function edit_slug()
 {
     check_admin_referer('wp_ajax_shopp_edit_slug');
     $defaults = array('slug' => false, 'type' => false, 'id' => false, 'title' => false);
     $p = array_merge($defaults, $_POST);
     extract($p);
     if (false === $slug || false === $id) {
         die('-1');
     }
     switch ($type) {
         case 'category':
             $Category = new ProductCategory($_POST['id']);
             if ($slug == $Category->slug) {
                 die('-1');
             }
             $term = get_term($Category->id, $Category->taxonomy);
             $Category->slug = wp_unique_term_slug(sanitize_title_with_dashes($slug), $term);
             $Category->save();
             echo apply_filters('editable_slug', $Category->slug);
             break;
         case 'product':
             $Product = new ShoppProduct($_POST['id']);
             if ($slug == $Product->slug) {
                 die('-1');
             }
             // Same as before? Nothing to do so bail
             // Regardless of the true post status, we'll pass 'publish' here to ensure we get a unique slug back
             $Product->slug = wp_unique_post_slug(sanitize_title_with_dashes($slug), $Product->id, 'publish', ShoppProduct::posttype(), 0);
             $Product->save();
             echo apply_filters('editable_slug', $Product->slug);
             break;
     }
     exit;
 }
Example #10
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 #11
0
                        // @todo Find a way to make this more efficient by loading product slugs with load_purchased()?
                        $viewurl = shopp('product.get-url');
                        $editurl = $this->url(array('id' => $Purchase->id, 'editline' => $id));
                        $rmvurl = $this->url(array('id' => $Purchase->id, 'rmvline' => $id));
                        $producturl = $this->url(array('id' => $Item->product));
                        ?>
									<td class="<?php 
                        echo esc_attr(join(' ', $classes));
                        ?>
">
										<a href="<?php 
                        echo $producturl;
                        ?>
">
                                            <?php 
                        $Product = new ShoppProduct($Item->product);
                        $Product->load_data(array('images'));
                        $Image = reset($Product->images);
                        if (!empty($Image)) {
                            $image_id = apply_filters('shopp_order_item_image_id', $Image->id, $Item, $Product);
                            ?>
                                                <img src="?siid=<?php 
                            echo $image_id;
                            ?>
&amp;<?php 
                            echo $Image->resizing(38, 0, 1);
                            ?>
" width="38" height="38" class="alignleft" />
                                            <?php 
                        }
                        echo apply_filters('shopp_purchased_item_name', $itemname);
Example #12
0
 /**
  * Adds Shopp shortcuts to the WordPress Admin Bar
  *
  * @since 1.2
  *
  * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance
  * @return void
  **/
 public function adminbar($wp_admin_bar)
 {
     $posttype = get_post_type_object(ShoppProduct::posttype());
     if (empty($posttype) || !current_user_can($posttype->cap->edit_post)) {
         return;
     }
     $wp_admin_bar->add_menu(array('parent' => 'new-content', 'id' => 'new-' . ShoppProduct::posttype(), 'title' => $posttype->labels->singular_name, 'href' => admin_url(str_replace('%d', 'new', $posttype->_edit_link))));
     $object = get_queried_object();
     if (!empty($object) && isset($object->post_type) && $object->post_type == $posttype->name) {
         $wp_admin_bar->add_menu(array('id' => 'edit', 'title' => $posttype->labels->edit_item, 'href' => get_edit_post_link($object->ID)));
     }
 }
Example #13
0
                    } else {
                        _e('Last Modified');
                    }
                    ?>
						</td>
					<?php 
                    break;
                default:
                    ?>
						<td class="<?php 
                    echo esc_attr(join(' ', $classes));
                    ?>
">
					<?php 
                    do_action('shopp_manage_product_custom_column', $column, $Product);
                    do_action('manage_' . ShoppProduct::posttype() . '_posts_custom_column', $column, $Product);
                    ?>
						</td>
					<?php 
                    break;
            }
        }
        /* $columns */
        ?>
		</tr>
		<?php 
    }
    /* $Products */
    ?>
		</tbody>
	<?php