Example #1
0
 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'));
 }
Example #2
0
 /**
  * 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);
         }
     }
 }
Example #3
0
/**
 * 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;
}
Example #4
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 #5
0
 /**
  * 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;
 }
Example #6
0
 /**
  * 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();
     }
 }
Example #7
0
 /**
  * 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();
 }
Example #8
0
 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');
 }
Example #9
0
 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'");
     }
 }
Example #10
0
 /**
  * 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;
 }