private function __construct() { $this->paths(); // Determine Shopp paths $this->constants(); // Setup Shopp constants $this->textdomain(); // Load the translation file // Load the Developer API ShoppDeveloperAPI::load(SHOPP_PATH); // Initialize error system ShoppErrors(); // Initialize application control processing $this->Flow = new ShoppFlow(); // Initialize Settings $this->Settings = ShoppSettings(); // Hooks add_action('init', array($this, 'init')); // Core WP integration add_action('shopp_init', array($this, 'pages')); add_action('shopp_init', array($this, 'collections')); add_action('shopp_init', array($this, 'taxonomies')); add_action('shopp_init', array($this, 'products'), 99); // Theme integration add_action('widgets_init', array($this, 'widgets')); // Request handling add_filter('rewrite_rules_array', array($this, 'rewrites')); add_filter('query_vars', array($this, 'queryvars')); }
/** * Sets handlers for Shopp shortcodes * * @author Jonathan Davis * @since 1.1 * * @return void **/ public function shortcodes() { $this->shortcodes = array(); // Additional shortcode functionality $this->shortcodes['catalog-product'] = array('ShoppShortcodes', 'product'); $this->shortcodes['catalog-buynow'] = array('ShoppShortcodes', 'buynow'); $this->shortcodes['catalog-collection'] = array('ShoppShortcodes', 'collection'); foreach ($this->shortcodes as $name => &$callback) { if (shopp_setting_enabled('maintenance') || !ShoppSettings()->available() || Shopp::maintenance()) { add_shortcode($name, array('', 'maintenance_shortcode')); } else { add_shortcode($name, $callback); } } }
/** * Saves all form POST submitted data to Shopp settings entries * * @api * @since 1.2 * * @param string $name The name of the setting that is to be stored. * @param mixed $value The value saved to the named setting. * @return bool true on success, false on failure. **/ function shopp_set_formsettings() { if (empty($_POST['settings']) || !is_array($_POST['settings'])) { shopp_debug(__FUNCTION__ . " failed: Setting name parameter required."); return false; } ShoppSettings()->saveform(); return true; }
/** * 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); }
/** * Initializes the ShoppDatabaseObject with functional necessities * * A ShoppDatabaseObject tracks meta data relevant to translating PHP object * data into SQL-ready data. This is done by reading and caching the * table schema so the properties and their data types can be known * in order to automate query building. * * The table schema is stored in an array structure that contains * the columns and their datatypes. This structure is cached as the * current data_model setting. If a table is missing from the data_model * a new table schema structure is generated on the fly. * * @author Jonathan Davis * @since 1.0 * * @param string $table The base table name (without prefixes) * @param string $key (optional) The column name of the primary key * @return boolean True if init was successful, otherwise false **/ public function init($table, $key = null) { if (is_null($key)) { $key = 'id'; } $Settings = ShoppSettings(); // So we know what the table name is if (!empty($table) && (!isset($this->_table) || empty($this->_table))) { $this->_table = $this->tablename($table); } if (empty($this->_table)) { return false; } $this->_key = $key; // So we know what the primary key is $this->_datatypes = array(); // So we know the format of the table $this->_lists = array(); // So we know the options for each list $defaults = array(); // So we know the default values for each field $map = !empty($this->_map) ? array_flip($this->_map) : array(); $Tables = $Settings->available() ? $Settings->get('data_model') : array(); if (isset($Tables[$this->_table])) { $this->_datatypes = $Tables[$this->_table]->_datatypes; $this->_lists = $Tables[$this->_table]->_lists; $defaults = $Tables[$this->_table]->_defaults; foreach ($this->_datatypes as $var => $type) { $property = isset($map[$var]) ? $map[$var] : $var; if (!isset($this->{$property})) { $this->{$property} = isset($defaults[$var]) ? $defaults[$var] : ''; } if ('date' == $type && ('0000-00-00 00:00:00' == $this->{$property} || empty($this->{$property}))) { $this->{$property} = null; } } return true; } if (!($r = sDB::query("SHOW COLUMNS FROM {$this->_table}", 'array'))) { return false; } // Map out the table definition into our data structure foreach ($r as $object) { $var = $object->Field; $this->_datatypes[$var] = sDB::datatype($object->Type); $this->_defaults[$var] = $object->Default; // Grab out options from list fields if ('list' == sDB::datatype($object->Type)) { $values = str_replace("','", ",", substr($object->Type, strpos($object->Type, "'") + 1, -2)); $this->_lists[$var] = explode(",", $values); } if (!empty($map) && !isset($map[$var])) { continue; } // Remap properties if a property map is available $property = isset($map[$var]) ? $map[$var] : $var; if (!isset($this->{$property})) { $this->{$property} = $this->_defaults[$var]; } } if ($Settings->available()) { $Tables[$this->_table] = new StdClass(); $Tables[$this->_table]->_datatypes =& $this->_datatypes; $Tables[$this->_table]->_lists =& $this->_lists; $Tables[$this->_table]->_defaults =& $this->_defaults; $Settings->save('data_model', $Tables); } return true; }
/** * Sets up the Shopp stub environment and ShoppSettings * * @author Jonathan Davis * @since 1.2 * @version 1.3 * * @return void **/ public function setup() { global $Shopp; if (!defined('SHOPP_PATH')) { define('SHOPP_PATH', self::path()); } if (!defined('SHOPP_STORAGE')) { define('SHOPP_STORAGE', SHOPP_PATH . '/storage'); } if (!defined('SHOPP_ADDONS')) { define('SHOPP_ADDONS', WP_CONTENT_DIR . '/shopp-addons'); } $Shopp->Storage = new StorageEngines(); ShoppSettings(); $modules = shopp_setting('imaging_modules'); // Only load image modules if necessary if (!empty($modules)) { new ShoppImagingModules(); } }
/** * Detect if a database schema upgrade is required * * @author Jonathan Davis * @since 1.3 * * @return boolean **/ public static function upgradedb() { return !ShoppSettings()->available() || ShoppSettings()->dbversion() != ShoppVersion::db(); }
public function verify_file() { check_admin_referer('wp_ajax_shopp_verify_file'); $Settings =& ShoppSettings(); chdir(WP_CONTENT_DIR); // relative file system path context for realpath $url = $_POST['url']; $request = parse_url($url); if ($request['scheme'] == "http") { $results = get_headers(linkencode($url)); if (substr($url, -1) == "/") { die("ISDIR"); } if (strpos($results[0], '200') === false) { die("NULL"); } } else { $url = str_replace('file://', '', $url); if ($url[0] != "/" || substr($url, 0, 2) == "./" || substr($url, 0, 3) == "../") { $result = apply_filters('shopp_verify_stored_file', $url); } $url = sanitize_path(realpath($url)); if (!file_exists($url)) { die('NULL'); } if (is_dir($url)) { die('ISDIR'); } if (!is_readable($url)) { die('READ'); } } die('OK'); }
public function upgrade_120() { // 1.2 schema changes $db_version = ShoppSettings::dbversion(); if ($db_version < 1120) { $this->upschema('schema-120.sql'); } global $wpdb; // Clear the shopping session table $shopping_table = ShoppDatabaseObject::tablename('shopping'); sDB::query("DELETE FROM {$shopping_table}"); if ($db_version <= 1140) { $summary_table = ShoppDatabaseObject::tablename('summary'); // Force summaries to rebuild sDB::query("UPDATE {$summary_table} SET modified='0000-00-00 00:00:01'"); } $purchase_table = ShoppDatabaseObject::tablename('purchase'); sDB::query("UPDATE {$purchase_table} SET txnstatus='captured' WHERE txnstatus='CHARGED'"); sDB::query("UPDATE {$purchase_table} SET txnstatus='voided' WHERE txnstatus='VOID'"); if ($db_version <= 1130) { // Move settings to meta table $meta_table = ShoppDatabaseObject::tablename('meta'); $setting_table = ShoppDatabaseObject::tablename('setting'); sDB::query("INSERT INTO {$meta_table} (context, type, name, value, created, modified) SELECT 'shopp', 'setting', name, value, created, modified FROM {$setting_table}"); // Clean up unnecessary duplicate settings shopp_rmv_setting('data_model'); shopp_rmv_setting('updates'); shopp_rmv_setting('shopp_setup'); shopp_rmv_setting('maintenance'); // Re-load the Shopp settings registry ShoppSettings()->load(); shopp_set_setting('maintenance', 'on'); $db_version = intval(shopp_setting('db_version')); // Force inventory in 1.2 on to mimic 1.1 behavior (inventory tracking always on) shopp_set_setting('inventory', 'on'); // Convert Shopp 1.1.x shipping settings to Shopp 1.2-compatible settings $active_shipping = array(); $regions = Lookup::regions(); $countries = Lookup::countries(); $areas = Lookup::country_areas(); $calcnaming = array('FlatRates::order' => 'OrderRates', 'FlatRates::item' => 'ItemRates', 'FreeOption' => 'FreeOption', 'ItemQuantity::range' => 'ItemQuantity', 'OrderAmount::range' => 'OrderAmount', 'OrderWeight::range' => 'OrderWeight'); $shipping_rates = shopp_setting('shipping_rates'); foreach ((array) $shipping_rates as $id => $old) { if (isset($calcnaming[$old['method']])) { // Add to active setting registry for that calculator class $calcname = $calcnaming[$old['method']]; if (!isset(${$calcname}) && !is_array(${$calcname})) { ${$calcname} = array(); } ${$calcname}[] = true; $active_shipping[$calcname] = ${$calcname}; // Define the setting name $settingid = end(array_keys(${$calcname})); $setting_name = $calcname . '-' . $settingid; } else { // Not a calculator, must be a shipping rate provider module, add it to the active roster $active_shipping[$old['name']] = true; continue; } $new = array(); $new['label'] = $old['name']; list($new['mindelivery'], $new['maxdelivery']) = explode('-', $old['delivery']); $new['fallback'] = 'off'; // Not used in legacy settings $oldkeys = array_keys($old); $old_destinations = array_diff($oldkeys, array('name', 'delivery', 'method', 'max')); $table = array(); foreach ($old_destinations as $old_dest) { $_ = array(); if ('Worldwide' == $old_dest) { $d = '*'; } $region = array_search($old_dest, $regions); if (false !== $region) { $d = "{$region}"; } if (isset($countries[$old_dest])) { $country = $countries[$old_dest]; $region = $country['region']; $d = "{$region}, {$old_dest}"; } foreach ($areas as $countrykey => $countryarea) { $areakeys = array_keys($countryarea); $area = array_search($old_dest, $areakeys); if (false !== $area) { $country = $countrykey; $region = $countries[$countrykey]['region']; $area = $areakeys[$area]; $d = "{$region}, {$country}, {$area}"; break; } } $_['destination'] = $d; $_['postcode'] = '*'; // Postcodes are new in 1.2, hardcode to wildcard if (isset($old['max']) && !empty($old['max'])) { // Capture tiered rates $_['tiers'] = array(); $prior = 1; foreach ($old['max'] as $index => $oldthreshold) { $tier = array('threshold' => 0, 'rate' => 0); if (in_array($oldthreshold, array('+', '>'))) { $tier['threshold'] = $prior + 1; } elseif (1 == $oldthreshold) { $tier['threshold'] = 1; } else { $tier['threshold'] = $prior + 1; } $prior = $oldthreshold; $tier['rate'] = $old[$old_dest][$index]; $_['tiers'][] = $tier; } } else { $_['rate'] = $old[$old_dest][0]; } // Capture flat rates $table[] = $_; } $new['table'] = $table; shopp_set_setting($setting_name, $new); // Save the converted settings } // End foreach($shipping_rates) to convert old shipping calculator setting format shopp_set_setting('active_shipping', $active_shipping); // Save the active shipping options } if ($db_version <= 1121) { $address_table = ShoppDatabaseObject::tablename('address'); $billing_table = ShoppDatabaseObject::tablename('billing'); $shipping_table = ShoppDatabaseObject::tablename('shipping'); // Move billing address data to the address table sDB::query("INSERT INTO {$address_table} (customer, type, address, xaddress, city, state, country, postcode, created, modified)\n\t\t\t\t\t\tSELECT customer, 'billing', address, xaddress, city, state, country, postcode, created, modified FROM {$billing_table}"); sDB::query("INSERT INTO {$address_table} (customer, type, address, xaddress, city, state, country, postcode, created, modified)\n\t\t\t\t\t\tSELECT customer, 'shipping', address, xaddress, city, state, country, postcode, created, modified FROM {$shipping_table}"); } // Migrate to WP custom posts & taxonomies if ($db_version <= 1131) { // Copy products to posts $catalog_table = ShoppDatabaseObject::tablename('catalog'); $product_table = ShoppDatabaseObject::tablename('product'); $price_table = ShoppDatabaseObject::tablename('price'); $summary_table = ShoppDatabaseObject::tablename('summary'); $meta_table = ShoppDatabaseObject::tablename('meta'); $category_table = ShoppDatabaseObject::tablename('category'); $tag_table = ShoppDatabaseObject::tablename('tag'); $purchased_table = ShoppDatabaseObject::tablename('purchased'); $index_table = ShoppDatabaseObject::tablename('index'); $post_type = 'shopp_product'; // Create custom post types from products, temporarily use post_parent for link to original product entry sDB::query("INSERT INTO {$wpdb->posts} (post_type, post_name, post_title, post_excerpt, post_content, post_status, post_date, post_date_gmt, post_modified, post_modified_gmt, post_parent)\n\t\t\t\t\t\t\tSELECT '{$post_type}', slug, name, summary, description, status, created, created, modified, modified, id FROM {$product_table}"); // Update purchased table product column with new Post ID so sold counts can be updated sDB::query("UPDATE {$purchased_table} AS pd JOIN {$wpdb->posts} AS wp ON wp.post_parent=pd.product AND wp.post_type='{$post_type}' SET pd.product=wp.ID"); // Update product links for prices and meta sDB::query("UPDATE {$price_table} AS price JOIN {$wpdb->posts} AS wp ON price.product=wp.post_parent SET price.product=wp.ID WHERE wp.post_type='{$post_type}'"); sDB::query("UPDATE {$meta_table} AS meta JOIN {$wpdb->posts} AS wp ON meta.parent=wp.post_parent AND wp.post_type='{$post_type}' AND meta.context='product' SET meta.parent=wp.ID"); sDB::query("UPDATE {$index_table} AS i JOIN {$wpdb->posts} AS wp ON i.product=wp.post_parent AND wp.post_type='{$post_type}' SET i.product=wp.ID"); // Preliminary summary data sDB::query("INSERT INTO {$summary_table} (product, featured, variants, addons, modified)\n\t\t\t\t\t\t SELECT wp.ID, p.featured, p.variations, p.addons, '0000-00-00 00:00:01'\n\t\t\t\t\t\t FROM {$product_table} AS p\n\t\t\t\t\t\t JOIN {$wpdb->posts} as wp ON p.id=wp.post_parent AND wp.post_type='{$post_type}'"); // Move product options column to meta setting sDB::query("INSERT INTO {$meta_table} (parent, context, type, name, value)\n\t\t\t\t\t\tSELECT wp.ID, 'product', 'meta', 'options', options\n\t\t\t\t\t\tFROM {$product_table} AS p\n\t\t\t\t\t\tJOIN {$wpdb->posts} AS wp ON p.id=wp.post_parent AND wp.post_type='{$post_type}'"); // Migrate Shopp categories and tags to WP taxonomies // Are there tag entries in the meta table? Old dev data present use meta table tags. No? use tags table. $dev_migration = $db_version >= 1120; // Copy categories and tags to WP taxonomies $tag_current_table = $dev_migration ? "{$meta_table} WHERE context='catalog' AND type='tag'" : $tag_table; $terms = sDB::query("(SELECT id, 'shopp_category' AS taxonomy, name, parent, description, slug FROM {$category_table})\n\t\t\t\t\t\t\t\t\t\t\tUNION\n\t\t\t\t\t\t\t\t\t\t(SELECT id, 'shopp_tag' AS taxonomy, name, 0 AS parent, '' AS description, name AS slug FROM {$tag_current_table}) ORDER BY id", 'array'); // Prep category images for the move $category_image_offset = 65535; sDB::query("UPDATE {$meta_table} set parent=parent+{$category_image_offset} WHERE context='category' AND type='image'"); $mapping = array(); $children = array(); $tt_ids = array(); foreach ($terms as $term) { $term_id = (int) $term->id; $taxonomy = $term->taxonomy; if (!isset($mapping[$taxonomy])) { $mapping[$taxonomy] = array(); } if (!isset($children[$taxonomy])) { $children[$taxonomy] = array(); } $name = $term->name; $parent = $term->parent; $description = $term->description; $slug = strpos($term->slug, ' ') === false ? $term->slug : sanitize_title_with_dashes($term->slug); $term_group = 0; if ($exists = sDB::query("SELECT term_id, term_group FROM {$wpdb->terms} WHERE slug = '{$slug}'", 'array')) { $term_group = $exists[0]->term_group; $id = $exists[0]->term_id; $num = 2; do { $alternate = sDB::escape($slug . "-" . $num++); $alternate_used = sDB::query("SELECT slug FROM {$wpdb->terms} WHERE slug='{$alternate}'"); } while ($alternate_used); $slug = $alternate; if (empty($term_group)) { $term_group = sDB::query("SELECT MAX(term_group) AS term_group FROM {$wpdb->terms} GROUP BY term_group", 'auto', 'col', 'term_group'); sDB::query("UPDATE {$wpdb->terms} SET term_group='{$term_group}' WHERE term_id='{$id}'"); } } // Move the term into the terms table $wpdb->query($wpdb->prepare("INSERT INTO {$wpdb->terms} (name, slug, term_group) VALUES (%s, %s, %d)", $name, $slug, $term_group)); $mapping[$taxonomy][$term_id] = (int) $wpdb->insert_id; // Map the old id to the new id $term_id = $mapping[$taxonomy][$term_id]; // Update the working id to the new id if (!isset($tt_ids[$taxonomy])) { $tt_ids[$taxonomy] = array(); } if ('shopp_category' == $taxonomy) { // If the parent term has already been added to the terms table, set the new parent id if (isset($mapping[$taxonomy][$parent])) { $parent = $mapping[$taxonomy][$parent]; } else { // Parent hasn't been created, keep track of children for the parent to do a mass update when the parent term record is created if (!isset($children[$taxonomy][$parent])) { $children[$taxonomy][$parent] = array(); } $children[$taxonomy][$parent][] = $term_id; } if (!empty($children[$taxonomy][$term->id])) { // If there are children already created for this term, update their parent to our new id $wpdb->query("UPDATE {$wpdb->term_taxonomy} SET parent={$term_id} WHERE term_id IN (" . join(', ', $children[$taxonomy][$term->id]) . ")"); } // Associate the term to the proper taxonomy and parent terms $wpdb->query($wpdb->prepare("INSERT INTO {$wpdb->term_taxonomy} (term_id, taxonomy, description, parent, count) VALUES ( %d, %s, %s, %d, %d)", $term_id, $taxonomy, $description, $parent, 0)); $tt_ids[$taxonomy][$term_id] = (int) $wpdb->insert_id; if (!empty($term_id)) { // Move category settings to meta $metafields = array('spectemplate', 'facetedmenus', 'variations', 'pricerange', 'priceranges', 'specs', 'options', 'prices'); foreach ($metafields as $field) { sDB::query("INSERT INTO {$meta_table} (parent, context, type, name, value)\n\t\t\t\t\t\t\t\t\t\t\tSELECT {$term_id}, 'category', 'meta', '{$field}', {$field}\n\t\t\t\t\t\t\t\t\t\t\tFROM {$category_table}\n\t\t\t\t\t\t\t\t\t\t\tWHERE id={$term->id}"); } // Update category images to new term ids sDB::query("UPDATE {$meta_table} set parent='{$term_id}' WHERE parent='" . ((int) $term->id + $category_image_offset) . "' AND context='category' AND type='image'"); } } if ('shopp_tag' == $taxonomy) { $wpdb->query($wpdb->prepare("INSERT INTO {$wpdb->term_taxonomy} (term_id, taxonomy, description, parent, count) VALUES ( %d, %s, %s, %d, %d)", $term_id, $taxonomy, $description, $parent, 0)); $tt_ids[$taxonomy][$term_id] = (int) $wpdb->insert_id; } } update_option('shopp_category_children', ''); // Re-catalog custom post type_products term relationships (new taxonomical catalog) from old Shopp catalog table $wp_taxonomies = array(0 => 'shopp_category', 1 => 'shopp_tag', 'category' => 'shopp_category', 'tag' => 'shopp_tag'); $cols = 'wp.ID AS product, c.parent, c.type'; $where = "type='category' OR type='tag'"; if ($db_version >= 1125) { $cols = 'wp.ID AS product, c.parent, c.taxonomy, c.type'; $where = "taxonomy=0 OR taxonomy=1"; } $rels = sDB::query("SELECT {$cols} FROM {$catalog_table} AS c LEFT JOIN {$wpdb->posts} AS wp ON c.product=wp.post_parent AND wp.post_type='{$post_type}' WHERE {$where}", 'array'); foreach ((array) $rels as $r) { $object_id = $r->product; $taxonomy = $wp_taxonomies[$db_version >= 1125 ? $r->taxonomy : $r->type]; $term_id = $mapping[$taxonomy][$r->parent]; if (!isset($tt_ids[$taxonomy])) { continue; } if (!isset($tt_ids[$taxonomy][$term_id])) { continue; } $tt_id = $tt_ids[$taxonomy][$term_id]; if (empty($tt_id)) { continue; } sDB::query("INSERT {$wpdb->term_relationships} (object_id, term_taxonomy_id) VALUES ({$object_id}, {$tt_id})"); } if (isset($tt_ids['shopp_category'])) { wp_update_term_count_now($tt_ids['shopp_category'], 'shopp_category'); } if (isset($tt_ids['shopp_tag'])) { wp_update_term_count_now($tt_ids['shopp_tag'], 'shopp_tag'); } // Clear custom post type parents sDB::query("UPDATE {$wpdb->posts} SET post_parent=0 WHERE post_type='{$post_type}'"); } // END if ($db_version <= 1131) if ($db_version <= 1133) { // Ditch old WP pages for pseudorific new ones $search = array(); $shortcodes = array('[catalog]', '[cart]', '[checkout]', '[account]'); foreach ($shortcodes as $string) { $search[] = "post_content LIKE '%{$string}%'"; } $results = sDB::query("SELECT ID, post_title AS title, post_name AS slug, post_content FROM {$wpdb->posts} WHERE post_type='page' AND (" . join(" OR ", $search) . ")", 'array'); $pages = $trash = array(); foreach ($results as $post) { $trash[] = $post->ID; foreach ($shortcodes as $code) { if (strpos($post->post_content, $code) === false) { continue; } $pagename = trim($code, '[]'); $pages[$pagename] = array('title' => $post->title, 'slug' => $post->slug); } // end foreach $shortcodes } // end foreach $results shopp_set_setting('storefront_pages', $pages); sDB::query("UPDATE {$wpdb->posts} SET post_name=CONCAT(post_name, '-deprecated'), post_status='trash' where ID IN (" . join(', ', $trash) . ")"); } // Move needed price table columns to price meta records if ($db_version <= 1135) { $meta_table = ShoppDatabaseObject::tablename('meta'); $price_table = ShoppDatabaseObject::tablename('price'); // Move 'options' to meta 'options' record sDB::query("INSERT INTO {$meta_table} (parent, context, type, name, value, created, modified)\n\t\t\t\t\t\tSELECT id, 'price', 'meta', 'options', options, created, modified FROM {$price_table}"); // Merge 'weight', 'dimensions' and 'donation' columns to a price 'settings' record sDB::query("INSERT INTO {$meta_table} (parent, context, type, name, value, created, modified)\n\t\t\t\t\t\t\tSELECT id, 'price', 'meta', 'settings',\n\t\t\t\t\t\t\tCONCAT('a:2:{s:10:\"dimensions\";',\n\t\t\t\t\t\t\t\tIF(weight = 0 AND dimensions = '0', 'a:0:{}',\n\t\t\t\t\t\t\t\t\tIF(dimensions = '0',\n\t\t\t\t\t\t\t\t\t\tCONCAT(\n\t\t\t\t\t\t\t\t\t\t\t'a:1:{s:6:\"weight\";s:', CHAR_LENGTH(weight), ':\"', weight, '\";}'\n\t\t\t\t\t\t\t\t\t\t), dimensions\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t), 's:8:\"donation\";', IF(donation='', 'N;', donation), '}'\n\t\t\t\t\t\t\t), created, modified FROM {$price_table}"); } // END if ($db_version <= 1135) if ($db_version <= 1145) { // Update purchase gateway property to use gateway class names // for proper order event handling on 1.1-generated orders $gateways = array('PayPal Standard' => 'PayPalStandard', 'PayPal Expresss' => 'PayPalExpress', 'PayPal Pro' => 'PayPalPro', '2Checkout.com' => '_2Checkout', 'Authorize.Net' => 'AuthorizeNet', 'Google Checkout' => 'GoogleCheckout', 'HSBC ePayments' => 'HSBCepayments', 'iDeal Mollie' => 'iDealMollie', 'Manual Processing' => 'ManualProcessing', 'Merchant Warrior' => 'MerchantWarrior', 'Offline Payment' => 'OfflinePayment', 'PayPal Payflow Pro' => 'PayflowPro', 'Test Mode' => 'TestMode'); foreach ($gateways as $name => $classname) { sDB::query("UPDATE {$purchase_table} SET gateway='{$classname}' WHERE gateway='{$name}'"); } } // END if ($db_version <= 1145) if ($db_version <= 1148) { $price_table = ShoppDatabaseObject::tablename('price'); sDB::query("UPDATE {$price_table} SET optionkey=(options*7001) WHERE context='addon'"); } if ($db_verison <= 1150) { $meta_table = ShoppDatabaseObject::tablename('meta'); sDB::query("DELETE {$meta_table} FROM {$meta_table} LEFT OUTER JOIN (SELECT MAX(id) AS keepid FROM {$meta_table} WHERE context='category' AND type='meta' GROUP BY parent, name) AS keepRowTable ON {$meta_table}.id = keepRowTable.keepid WHERE keepRowTable.keepid IS NULL AND context='category' AND type='meta'"); } }
/** * Provides the installed database schema version from the database (if available) * * Queries the database to get the installed database version number. If not available, * also checks the legacy * * @since 1.3 * * @param string $legacy Set to anything but boolean false to attempt to lookup the version from the pre-1.2 settings table * @return integer The installed database schema version number (0 means not installed) **/ public static function dbversion($legacy = false) { if (!empty(ShoppSettings()->registry['db_version'])) { return ShoppSettings()->registry['db_version']; } $source = $legacy ? 'setting' : self::$table; $table = ShoppDatabaseObject::tablename($source); $version = sDB::query("SELECT value FROM {$table} WHERE name='db_version' ORDER BY id DESC LIMIT 1", 'object', 'col'); // Try again using the legacy table if (false === $version && false === $legacy) { $version = self::dbversion('legacy'); } elseif (false !== $legacy) { // No version in the legacy settings table, possible 1.0 install? // Look in the old settings table for the old Shopp version setting $shopp_version = sDB::query("SELECT value FROM {$table} WHERE name='version' ORDER BY id DESC LIMIT 1", 'object', 'col'); // Use (int) 1 to indicate Shopp 1.0 installed and avoid the install process if (version_compare($shopp_version, '1.1', '<')) { $version = 1; } } if (false === $version) { ShoppSettings()->registry['db_version'] = null; } else { ShoppSettings()->registry['db_version'] = (int) $version; } return (int) $version; }