/** * 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; }
/** * 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'); }
/** * 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); }
/** * 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; }
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; }
/** * 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); } } }
/** * 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())); }
/** * 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; }
/** * 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; }
/** * 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; }
// @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; ?> &<?php echo $Image->resizing(38, 0, 1); ?> " width="38" height="38" class="alignleft" /> <?php } echo apply_filters('shopp_purchased_item_name', $itemname);
/** * 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))); } }
} 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