PageRenderTime 229ms CodeModel.GetById 44ms RepoModel.GetById 3ms app.codeStats 1ms

/wp-content/plugins/shopp/core/flow/Storefront.php

https://bitbucket.org/sanders_nick/legacy-media
PHP | 1868 lines | 1021 code | 295 blank | 552 comment | 238 complexity | 825b5533639e562bda320ee46a908042 MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-2.1, GPL-2.0, BSD-3-Clause, GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Storefront
  4. *
  5. * Flow controller for the front-end shopping interfaces
  6. *
  7. * @author Jonathan Davis
  8. * @version 1.0
  9. * @copyright Ingenesis Limited, January 12, 2010
  10. * @license GNU GPL version 3 (or later) {@see license.txt}
  11. * @package shopp
  12. * @subpackage storefront
  13. **/
  14. /**
  15. * Storefront
  16. *
  17. * @author Jonathan Davis
  18. * @since 1.1
  19. * @package shopp
  20. * @subpackage storefront
  21. **/
  22. class Storefront extends FlowController {
  23. var $behaviors = array(); // Runtime JavaScript behaviors
  24. var $browsing = array();
  25. var $checkout = false; // Flags when the checkout form is being processed
  26. var $Page = false;
  27. var $pages = array();
  28. var $referrer = false;
  29. var $request = false;
  30. var $Requested = false; // Property for tracking the originally requested content
  31. var $search = false; // The search query string
  32. var $searching = false; // Flags if a search request has been made
  33. var $shortcoded = array();
  34. var $viewed = array();
  35. var $account = false; // Account dashboard requests
  36. var $dashboard = array(); // Registry of account dashboard pages
  37. var $menus = array(); // Account dashboard menu registry
  38. function __construct () {
  39. parent::__construct();
  40. ShoppCatalog(new Catalog());
  41. ShoppingObject::store('search',$this->search);
  42. ShoppingObject::store('browsing',$this->browsing);
  43. ShoppingObject::store('referrer',$this->referrer);
  44. ShoppingObject::store('viewed',$this->viewed);
  45. // Setup WP_Query overrides
  46. add_action('parse_query', array($this, 'query'));
  47. add_filter('posts_request', array($this, 'noquery'),10,2);
  48. add_filter('posts_results', array($this, 'found'),10,2);
  49. add_filter('the_posts', array($this, 'posts'),10,2);
  50. add_action('wp', array($this, 'loaded'));
  51. add_action('wp', array($this, 'security'));
  52. add_action('wp', array($this, 'trackurl'));
  53. add_action('wp', array($this, 'viewed'));
  54. add_action('wp', array($this, 'cart'));
  55. add_action('wp', array($this, 'dashboard'));
  56. add_action('wp', array($this, 'shortcodes'));
  57. add_action('wp', array($this, 'behaviors'));
  58. add_filter('wp_get_nav_menu_items', array($this,'menulinks'), 10, 2);
  59. add_filter('shopp_order_lookup','shoppdiv');
  60. add_filter('shopp_order_confirmation','shoppdiv');
  61. add_filter('shopp_errors_page','shoppdiv');
  62. add_filter('shopp_catalog_template','shoppdiv');
  63. add_filter('shopp_cart_template','shoppdiv');
  64. add_filter('shopp_checkout_page','shoppdiv');
  65. add_filter('shopp_account_template','shoppdiv');
  66. add_filter('shopp_category_template','shoppdiv');
  67. add_filter('shopp_order_receipt','shoppdiv');
  68. add_filter('shopp_account_manager','shoppdiv');
  69. add_filter('shopp_account_vieworder','shoppdiv');
  70. add_filter('the_content',array($this,'autowrap'),99);
  71. add_action('wp_enqueue_scripts', 'shopp_dependencies');
  72. add_action('shopp_storefront_init',array($this,'collections'));
  73. add_action('shopp_storefront_init',array($this,'account'));
  74. add_filter('wp_nav_menu_objects',array($this,'menus'));
  75. add_filter('search_template',array($this,'maintenance'));
  76. add_filter('taxonomy_template',array($this,'maintenance'));
  77. add_filter('page_template',array($this,'maintenance'));
  78. add_filter('single_template',array($this,'maintenance'));
  79. add_action('do_feed_rss2',array($this,'feed'),1);
  80. add_filter('search_template',array($this,'pages'));
  81. add_filter('taxonomy_template',array($this,'pages'));
  82. add_filter('page_template',array($this,'pages'));
  83. add_filter('single_template',array($this,'single'));
  84. }
  85. /**
  86. * Determines if the wp_query is for Shopp content
  87. *
  88. * @author Jonathan Davis
  89. * @since 1.2
  90. *
  91. * @return void
  92. **/
  93. function request ( $wp_query = false ) {
  94. return is_shopp_query($wp_query) && ! is_shopp_product($wp_query);
  95. }
  96. /**
  97. * Override the WP posts_request so the storefront controller can take over
  98. *
  99. * @author Jonathan Davis
  100. * @since 1.2
  101. *
  102. * @return string|boolean The request, or false if a Shopp Storefront request
  103. **/
  104. function noquery ($request,$wp_query) {
  105. if ( $this->request($wp_query) ) return false;
  106. return $request;
  107. }
  108. /**
  109. * Sets the found count to avoid 404 pages when handling Shopp requests
  110. *
  111. * @author Jonathan Davis
  112. * @since 1.2
  113. *
  114. * @return int|boolean Number of posts found or, true if a Shopp Storefront request
  115. **/
  116. function found ($found_posts,$wp_query) {
  117. if ( $this->request($wp_query) ) return true;
  118. return $found_posts;
  119. }
  120. /**
  121. * Provide a stub to the wp_query posts property to mimic post functionality
  122. *
  123. * @author Jonathan Davis
  124. * @since 1.2
  125. *
  126. * @param array $posts The current list of posts
  127. * @param object $wp_query The working WP_Query object
  128. * @return array List of posts, or a list with the post stub for Shopp Storefront requests
  129. **/
  130. function posts ($posts, $wp_query) {
  131. if ( $this->request($wp_query) ) {
  132. $StubPage = new StorefrontPage();
  133. return array($StubPage->poststub());
  134. }
  135. if (count($posts) == 1) { // @deprecated Legacy support to redirect old shortcode pages
  136. $shortcodes = join('|', array_keys( self::pages_settings() ) );
  137. if (preg_match("/\[($shortcodes)\]/",$posts[0]->post_content,$matches)) {
  138. $shortcode = next($matches);
  139. if ('catalog' == $shortcode) $shortcode = '';
  140. shopp_redirect( shoppurl($shortcode) );
  141. exit();
  142. }
  143. }
  144. return $posts;
  145. }
  146. /**
  147. * Parse the query request and initialize Shopp content
  148. *
  149. * @author Jonathan Davis
  150. * @since 1.2
  151. *
  152. * @param object $wp_query The WP_Query object (passed via parse_query action)
  153. * @return void
  154. **/
  155. function query ($wp_query) {
  156. if ( ! $this->request($wp_query) ) return;
  157. $page = $wp_query->get('shopp_page');
  158. $posttype = $wp_query->get('post_type');
  159. $product = $wp_query->get(Product::$posttype);
  160. $collection = $wp_query->get('shopp_collection');
  161. $sortorder = $wp_query->get('s_so');
  162. $searching = $wp_query->get('s_cs');
  163. $search = $wp_query->get('s');
  164. // Shopp requests are never automatic on the home page
  165. $wp_query->is_home = false;
  166. if (!empty($sortorder)) $this->browsing['sortorder'] = $sortorder;
  167. $catalog = Storefront::slug('catalog');
  168. // Detect catalog page requests
  169. if (is_archive() && $posttype == Product::$posttype && '' == $product.$collection.$page.$search) {
  170. $page = $catalog;
  171. $wp_query->set('shopp_page',$page);
  172. }
  173. // Shopp request, remove noindex
  174. remove_action( 'wp_head', 'noindex', 1 );
  175. $wp_query->set('suppress_filters',false); // Override default WP_Query request
  176. // Restore paged query var for Shopp's alpha-pagination support
  177. if (isset($wp_query->query['paged']) && false != preg_match('/([A-Z]|0\-9)/i',$wp_query->query['paged']))
  178. $wp_query->query_vars['paged'] = strtoupper($wp_query->query['paged']);
  179. // Handle Taxonomies
  180. if (is_archive()) {
  181. $taxonomies = get_object_taxonomies(Product::$posttype, 'object');
  182. foreach ( $taxonomies as $t ) {
  183. if ($wp_query->get($t->query_var) == '') continue;
  184. $taxonomy = $wp_query->get($t->query_var);
  185. if ($t->hierarchical) ShoppCollection( new ProductCategory($taxonomy,'slug',$t->name) );
  186. else ShoppCollection( new ProductTag($taxonomy,'slug',$t->name) );
  187. $page = $catalog;
  188. }
  189. }
  190. $options = array();
  191. if ( $searching ) { // Catalog search
  192. $collection = 'search-results';
  193. $options = array('search'=>$search);
  194. }
  195. // Handle Shopp Smart Collections
  196. if ( ! empty($collection) ) {
  197. // Overrides to enforce archive behavior
  198. $page = $catalog;
  199. // Promo Collection routing
  200. $promos = shopp_setting('active_catalog_promos');
  201. if ( isset($promos[$collection]) ) {
  202. $options['id'] = $promos[$collection][0];
  203. $collection = 'promo';
  204. }
  205. ShoppCollection( Catalog::load_collection($collection,$options) );
  206. if (!is_feed()) ShoppCollection()->load(array('load'=>array('coverimages')));
  207. // Provide a stub to the queried object for smart collections since WP has no parallel
  208. $post_archive = new stdClass();
  209. $post_archive->labels = new stdClass();
  210. $post_archive->labels->name = ShoppCollection()->name;
  211. $post_archive->post_title = ShoppCollection()->name; // Added so single_post_title will return the title properly
  212. $wp_query->queried_object = $post_archive;
  213. $wp_query->queried_object_id = 0;
  214. }
  215. $Collection = ShoppCollection();
  216. if (!empty($Collection)) {
  217. $this->Requested = $Collection;
  218. add_action('wp_head', array($this, 'metadata'));
  219. remove_action('wp_head','feed_links',2);
  220. add_action('wp_head', array($this, 'feedlinks'),2);
  221. }
  222. if ( ! empty($page) ) {
  223. // Overrides to enforce page behavior
  224. $wp_query->set('shopp_page',$page);
  225. $wp_query->is_page = true;
  226. $wp_query->is_singular = true;
  227. $wp_query->post_count = true;
  228. $wp_query->shopp_page = true;
  229. $wp_query->is_archive = false;
  230. }
  231. }
  232. /**
  233. * Convert WP queried Shopp product post types to a Shopp Product
  234. *
  235. * @author Jonathan Davis
  236. * @since 1.2
  237. *
  238. * @param object $wp The main WP object from the 'wp' action
  239. * @return void
  240. **/
  241. function loaded ($wp) {
  242. if ( ! is_shopp_product() ) return;
  243. // Get the loaded object (a Shopp product post type)
  244. global $wp_the_query;
  245. $object = $wp_the_query->get_queried_object();
  246. // Populate so we don't have to req-uery
  247. $Product = new Product();
  248. $Product->populate($object);
  249. ShoppProduct($Product);
  250. $this->Requested = $Product;
  251. }
  252. /**
  253. * Tracks a product as recently viewed
  254. *
  255. * @author Jonathan Davis
  256. * @since 1.2
  257. *
  258. * @param object $wp The main WP object from the 'wp' action
  259. * @return void
  260. **/
  261. function viewed ($wp) {
  262. if ( ! is_shopp_product() ) return;
  263. if (in_array($this->Requested->id,$this->viewed)) return;
  264. array_unshift($this->viewed,$this->Requested->id);
  265. $this->viewed = array_slice($this->viewed,0,apply_filters('shopp_recently_viewed_limit',25));
  266. }
  267. /**
  268. * Track the URL as a referrer from catalog pages (collections, taxonomies, products)
  269. *
  270. * @author Jonathan Davis
  271. * @since 1.2
  272. *
  273. * @param object $wp The main WP object from the 'wp' action
  274. * @return void
  275. **/
  276. function trackurl ($wp) {
  277. if (!is_catalog_page()) return;
  278. // Track referrer for the cart referrer URL
  279. $referrer = get_bloginfo('url')."/".$wp->request;
  280. if (!empty($_GET)) $referrer = add_query_arg($_GET,$referrer);
  281. $this->referrer = user_trailingslashit($referrer);
  282. }
  283. /**
  284. * Render a maintenance message on the storefront when Shopp in maintenance mode
  285. *
  286. * Detects when maintenance is required and overrides all other storefront
  287. * template output to display an overridable maintenance screen.
  288. *
  289. * Create a shopp-maintenanace.php template in your WordPress theme for a custom
  290. * maintenance message.
  291. *
  292. * @author Jonathan Davis
  293. * @since 1.2
  294. *
  295. * @return void
  296. **/
  297. function maintenance ($template) {
  298. // Only run if in maintenance mode
  299. if (!is_shopp_page()) return $template;
  300. if (!Shopp::maintenance()) return $template;
  301. // Remove normal Shopp Storefront template processing
  302. // so maintenance content takes over
  303. remove_filter('page_template',array($this,'pages'));
  304. remove_filter('single_template',array($this,'single'));
  305. $Collection = ShoppCollection();
  306. $post_type = get_query_var('post_type');
  307. $page = self::slugpage( get_query_var('shopp_page') );
  308. // Build the page
  309. $this->Page = new MaintenanceStorefrontPage();
  310. $this->Page->poststub();
  311. // Send the template back to WordPress
  312. return locate_template($this->Page->templates());
  313. }
  314. /**
  315. * Filters WP template handlers to render Shopp Storefront page templates
  316. *
  317. * @author Jonathan Davis
  318. * @since 1.2
  319. * @version 1.2.1
  320. *
  321. * @param string $template The template
  322. * @return string The output of the templates
  323. **/
  324. function pages ($template) {
  325. // Get the requested storefront page identifier from the slug
  326. $page = self::slugpage( get_query_var('shopp_page') );
  327. if ( empty($page) ) return $template;
  328. // Load the request Storefront page settings
  329. $pages = self::pages_settings();
  330. if ( ! isset($pages[$page]) ) return $template;
  331. $settings = $pages[$page];
  332. // Build the page
  333. if ( is_shopp_collection() ) $StorefrontPage = 'CollectionStorefrontPage';
  334. else $StorefrontPage = ucfirst($page).'StorefrontPage';
  335. if (!class_exists($StorefrontPage)) $StorefrontPage = 'StorefrontPage';
  336. if (Shopp::maintenance()) $StorefrontPage = 'MaintenanceStorefrontPage';
  337. $this->Page = new $StorefrontPage($settings);
  338. // Send the template back to WordPress
  339. return locate_template($this->Page->templates());
  340. }
  341. /**
  342. * Filters WP template handlers to render a Shopp product page
  343. *
  344. * @author Jonathan Davis
  345. * @since 1.2
  346. * @version 1.2.1
  347. *
  348. * @param string $template The template
  349. * @return string The output of the templates
  350. **/
  351. function single ($template) {
  352. if ( ! is_shopp_product() ) return $template;
  353. $this->Page = new ProductStorefrontPage();
  354. return locate_template($this->Page->templates());
  355. }
  356. /**
  357. * Handles RSS-feed requests
  358. *
  359. * @author Jonathan Davis
  360. * @since 1.1
  361. *
  362. * @return void
  363. **/
  364. function feed () {
  365. if (! is_shopp_collection()) return;
  366. $Collection = ShoppCollection();
  367. $base = shopp_setting('base_operations');
  368. add_filter('shopp_rss_description','wptexturize');
  369. add_filter('shopp_rss_description','convert_chars');
  370. add_filter('shopp_rss_description','make_clickable',9);
  371. add_filter('shopp_rss_description','force_balance_tags', 25);
  372. add_filter('shopp_rss_description','convert_smilies',20);
  373. add_filter('shopp_rss_description','wpautop',30);
  374. add_filter('shopp_rss_description','ent2ncr');
  375. do_action_ref_array('shopp_collection_feed',array($Collection));
  376. $rss = array('title' => trim(get_bloginfo('name')." ".$Collection->name),
  377. 'link' => shopp($Collection,'get-feed-url'),
  378. 'description' => $Collection->description,
  379. 'sitename' => get_bloginfo('name').' ('.get_bloginfo('url').')',
  380. 'xmlns' => array('shopp'=>'http://shopplugin.net/xmlns',
  381. 'g'=>'http://base.google.com/ns/1.0',
  382. 'atom'=>'http://www.w3.org/2005/Atom',
  383. 'content'=>'http://purl.org/rss/1.0/modules/content/')
  384. );
  385. $rss = apply_filters('shopp_rss_meta',$rss);
  386. $tax_inclusive = shopp_setting_enabled('tax_inclusive');
  387. $template = locate_shopp_template(array('feed-'.$Collection->slug.'.php','feed.php'));
  388. if (!$template) $template = SHOPP_ADMIN_PATH.'/categories/feed.php';
  389. header("Content-type: application/rss+xml; charset=".get_option('blog_charset'));
  390. include($template);
  391. exit();
  392. }
  393. /**
  394. * Renders RSS feed link tags for category product feeds
  395. *
  396. * @author Jonathan Davis
  397. * @since 1.1
  398. *
  399. * @return void
  400. **/
  401. function feedlinks () {
  402. if (empty(ShoppCollection()->name)) return;
  403. $title = apply_filters('shopp_collection_feed_title', sprintf('%s %s %s', get_bloginfo('name'), ShoppCollection()->name, __('Feed','Shopp')) );
  404. echo '<link rel="alternate" type="'.feed_content_type('rss').'" title="'.esc_attr($title).'" href="'.esc_attr(shopp('collection','get-feed-url')).'" />'."\n";
  405. }
  406. /**
  407. * Forces SSL on pages when required by gateways that handle sensitive payment details onsite
  408. *
  409. * @author Jonathan Davis
  410. * @since 1.1
  411. * @version 1.2
  412. *
  413. * @return void
  414. **/
  415. function security () {
  416. global $Shopp;
  417. if (SHOPP_NOSSL || !$Shopp->Gateways->secure || is_ssl() ) return;
  418. $redirect = false;
  419. if (is_checkout_page()) $redirect = 'checkout';
  420. if (is_confirm_page()) $redirect = 'confirm';
  421. if (is_account_page()) $redirect = 'account';
  422. if ($redirect)
  423. shopp_redirect( shoppurl($_GET,$redirect,true) );
  424. }
  425. /**
  426. * Adds nocache headers on sensitive account pages
  427. *
  428. * @author Jonathan Davis
  429. * @since 1.1
  430. *
  431. * @return void
  432. **/
  433. function account () {
  434. $request = get_query_var('acct');
  435. if (!empty($request)) add_filter('wp_headers',array($this,'nocache'));
  436. }
  437. /**
  438. * Adds nocache headers to WP page headers
  439. *
  440. * @author Jonathan Davis
  441. * @since 1.1
  442. *
  443. * @param array $headers The current WP HTTP headers
  444. * @return array Modified headers
  445. **/
  446. function nocache ($headers) {
  447. $headers = array_merge($headers, wp_get_nocache_headers());
  448. return $headers;
  449. }
  450. /**
  451. * Queues Shopp storefront javascript and styles as needed
  452. *
  453. * @author Jonathan Davis
  454. * @since 1.1
  455. *
  456. * @return void
  457. **/
  458. function behaviors () {
  459. global $Shopp;
  460. if(is_ssl()) {
  461. add_filter('option_siteurl', 'force_ssl');
  462. add_filter('option_home', 'force_ssl');
  463. add_filter('option_url', 'force_ssl');
  464. add_filter('option_wpurl', 'force_ssl');
  465. add_filter('option_stylesheet_url', 'force_ssl');
  466. add_filter('option_template_url', 'force_ssl');
  467. add_filter('script_loader_src', 'force_ssl');
  468. }
  469. // Include stylesheets and javascript based on whether shopp shortcodes are used
  470. add_action('wp_print_styles',array($this, 'catalogcss'));
  471. // Replace the WordPress canonical link
  472. remove_action('wp_head','rel_canonical');
  473. add_action('wp_head', array($this, 'header'));
  474. add_action('wp_footer', array($this, 'footer'));
  475. wp_enqueue_style('shopp.catalog',SHOPP_ADMIN_URI.'/styles/catalog.css',array(),20110511,'screen');
  476. wp_enqueue_style('shopp',shopp_template_url('shopp.css'),array(),20110511,'screen');
  477. wp_enqueue_style('shopp.colorbox',SHOPP_ADMIN_URI.'/styles/colorbox.css',array(),20110511,'screen');
  478. $page = $this->slugpage(get_query_var('shopp_page'));
  479. $thankspage = ('thanks' == $page);
  480. $orderhistory = ('account' == $page && !empty($_GET['id']));
  481. if ($thankspage || $orderhistory)
  482. wp_enqueue_style('shopp.printable',SHOPP_ADMIN_URI.'/styles/printable.css',array(),20110511,'print');
  483. $loading = shopp_setting('script_loading');
  484. if (!$loading || 'global' == $loading || !empty($page)) {
  485. shopp_enqueue_script('colorbox');
  486. shopp_enqueue_script('shopp');
  487. shopp_enqueue_script('catalog');
  488. shopp_enqueue_script('cart');
  489. if (is_shopp_page('catalog'))
  490. shopp_custom_script('catalog',"var pricetags = {};\n");
  491. add_action('wp_head', array(&$Shopp, 'settingsjs'));
  492. }
  493. if ('checkout' == $page) {
  494. shopp_enqueue_script('address');
  495. shopp_enqueue_script('checkout');
  496. }
  497. if ('account' == $page) {
  498. shopp_enqueue_script('address');
  499. $regions = Lookup::country_zones();
  500. $js = "var regions=".json_encode($regions);
  501. add_storefrontjs($js,true);
  502. }
  503. }
  504. /**
  505. * Modifies the WP page title to include product/category names (when available)
  506. *
  507. * @author Jonathan Davis
  508. * @since 1.1
  509. *
  510. * @param string $title The current WP page title
  511. * @param string $sep (optional) The page title separator to include between page titles
  512. * @param string $placement (optional) The placement of the separator (defaults 'left')
  513. * @return string The modified page title
  514. **/
  515. function titles ($title,$sep='&mdash;',$placement='left') {
  516. $request = array();
  517. $vars = array('s_cat','s_tag','s_pd','s_pid');
  518. foreach ($vars as $v) $request[] = get_query_var($v);
  519. if (empty($request)) return $title;
  520. if (empty(ShoppProduct()->name) && empty(ShoppCollection()->name)) return $title;
  521. $_ = array();
  522. if (!empty($title)) $_[] = $title;
  523. if (!empty(ShoppCollection()->name)) $_[] = ShoppCollection()->name;
  524. if (!empty(ShoppProduct()->name)) $_[] = ShoppProduct()->name;
  525. if ('right' == $placement) $_ = array_reverse($_);
  526. $_ = apply_filters('shopp_document_titles',$_);
  527. $sep = trim($sep);
  528. if (empty($sep)) $sep = '&mdash;';
  529. return join(" $sep ",$_);
  530. }
  531. /**
  532. * Adds 'keyword' and 'description' <meta> tags into the page markup
  533. *
  534. * The 'keyword' tag is a list of tags applied to a product. No default 'keyword' meta
  535. * is generated for categories, however, the 'shopp_meta_keywords' filter hook can be
  536. * used to generate a custom list.
  537. *
  538. * The 'description' tag is generated from the product summary or category description.
  539. * It can also be customized with the 'shopp_meta_description' filter hook.
  540. *
  541. * @author Jonathan Davis
  542. * @since 1.1
  543. *
  544. * @return void
  545. **/
  546. function metadata () {
  547. $keywords = false;
  548. $description = false;
  549. if (!empty(ShoppProduct()->id)) {
  550. if (empty(ShoppProduct()->tags)) ShoppProduct()->load_data(array('tags'));
  551. foreach(ShoppProduct()->tags as $tag)
  552. $keywords .= (!empty($keywords))?", {$tag->name}":$tag->name;
  553. $description = ShoppProduct()->summary;
  554. } elseif (isset(ShoppCollection()->description)) {
  555. $description = ShoppCollection()->description;
  556. }
  557. $keywords = esc_attr(apply_filters('shopp_meta_keywords',$keywords));
  558. $description = esc_attr(apply_filters('shopp_meta_description',$description));
  559. ?>
  560. <?php if ($keywords): ?><meta name="keywords" content="<?php echo $keywords; ?>" />
  561. <?php endif; ?>
  562. <?php if ($description): ?><meta name="description" content="<?php echo $description; ?>" />
  563. <?php endif;
  564. }
  565. /**
  566. * Returns canonical product and category URLs
  567. *
  568. * @author Jonathan Davis
  569. * @since 1.1
  570. *
  571. * @param string $url The current url
  572. * @return string The canonical url
  573. **/
  574. function canonurls ($url) {
  575. // Product catalog archive (landing) page URL
  576. if (is_post_type_archive() && is_shopp_page('catalog'))
  577. return shopp('catalog','get-url');
  578. // Specific product/category URLs
  579. if (!empty($Shopp->Product->slug)) return shopp('product','get-url');
  580. if (!empty($Shopp->Category->slug)) {
  581. $paged = (int)get_query_var('paged');
  582. $url = shopp('category','get-url');
  583. if ($paged > 1) $url = shopp('category','get-url',"page=$paged");
  584. }
  585. return $url;
  586. }
  587. /**
  588. * Registers available collections
  589. *
  590. * New collections can be added by creating a new Collection class
  591. * in a custom plugin or the theme functions.php file.
  592. *
  593. * @author Jonathan Davis
  594. * @since 1.1
  595. *
  596. * @return void
  597. **/
  598. function collections () {
  599. do_action('shopp_register_smartcategories'); // @deprecated
  600. do_action('shopp_register_collections');
  601. }
  602. /**
  603. * Includes a canonical reference <link> tag for the catalog page
  604. *
  605. * @author Jonathan Davis
  606. * @since 1.1
  607. *
  608. * @return void Description...
  609. **/
  610. function header () {
  611. $canonurl = $this->canonurls(false);
  612. // Add canonical URLs
  613. if (is_shopp_page('catalog') && !empty($canonurl))
  614. echo '<link rel="canonical" href="'.apply_filters('shopp_canonical_link',$canonurl).'" />';
  615. // Add noindex for cart, checkout, account pages
  616. if (is_shopp_page('cart') || is_shopp_page('checkout') || is_shopp_page('account'))
  617. noindex();
  618. }
  619. /**
  620. * Adds a dynamic style declaration for the category grid view
  621. *
  622. * Ties the presentation setting to the grid view category rendering
  623. * in the storefront.
  624. *
  625. * @author Jonathan Davis
  626. * @since 1.1
  627. *
  628. * @return void Description...
  629. **/
  630. function catalogcss () {
  631. if (!isset($row_products)) $row_products = 3;
  632. $row_products = shopp_setting('row_products');
  633. $products_per_row = floor((100/$row_products));
  634. $css = '
  635. <!-- Shopp catalog styles for dynamic grid view -->
  636. <style type="text/css">
  637. #shopp ul.products li.product { width: '.$products_per_row.'%; }
  638. </style>
  639. ';
  640. echo $css;
  641. }
  642. /**
  643. * Renders footer content and extra scripting as needed
  644. *
  645. * @author Jonathan Davis
  646. * @since 1.1
  647. *
  648. * @return void Description...
  649. **/
  650. function footer () {
  651. $globals = false;
  652. if (isset($this->behaviors['global'])) {
  653. $globals = $this->behaviors['global'];
  654. unset($this->behaviors['global']);
  655. }
  656. $script = '';
  657. if (!empty($globals)) $script .= "\t".join("\n\t",$globals)."\n";
  658. if (!empty($this->behaviors)) {
  659. $script .= 'jQuery(window).ready(function($){'."\n";
  660. $script .= "\t".join("\t\n",$this->behaviors)."\n";
  661. $script .= '});'."\n";
  662. }
  663. shopp_custom_script('catalog',$script);
  664. }
  665. /**
  666. * Manages CSS relationship classes applied to Shopp elements appearing in a WordPress navigation menu
  667. *
  668. * @author Jonathan Davis
  669. * @since 1.2
  670. *
  671. * @param array $menuitems The provided WordPress menu items
  672. * @return array Updated menu items with proper relationship classes
  673. **/
  674. function menus ($menuitems) {
  675. $is_shopp_page = is_shopp_page();
  676. $keymap = array();
  677. $parents = array();
  678. foreach ($menuitems as $key => $item) {
  679. $page = false;
  680. // Remove the faulty wp_page_menu (deprecated) class for Shopp pages
  681. if ($is_shopp_page && in_array('current_page_parent',$item->classes))
  682. unset($item->classes[ array_search('current_page_parent',$item->classes) ]);
  683. // Otherwise, skip dealing with any non-Shopp page
  684. if ('shopp_page' == $item->type) {
  685. // Determine the queried Shopp page object name
  686. $page = Storefront::slugpage( get_query_var('shopp_page') );
  687. // Set the catalog as current page parent
  688. if ('catalog' == $item->object && $is_shopp_page) $item->classes[] = 'current-page-parent';
  689. $keymap[$item->db_id] = $key;
  690. }
  691. if ('shopp_collection' == $item->type) {
  692. $page = get_query_var($item->type);
  693. $keymap[$item->db_id] = $key;
  694. }
  695. if ($page == $item->object) {
  696. $item->classes[] = 'current-page-item';
  697. $item->classes[] = 'current-menu-item';
  698. $parents[] = $item->menu_item_parent;
  699. }
  700. }
  701. foreach ((array)$parents as $parentid) {
  702. if (!isset($keymap[$parentid])) continue;
  703. $parent = $menuitems[ $keymap[$parentid] ];
  704. $parent->classes[] = 'current-menu-parent';
  705. $parent->classes[] = 'current-page-parent';
  706. $parent->classes[] = 'current-menu-ancestor';
  707. $parent->classes[] = 'current-page-ancestor';
  708. $ancestor = $parent;
  709. while($ancestor->menu_item_parent != 0) {
  710. $ancestor = $menuitems[ $keymap[ $ancestor->menu_item_parent ] ];
  711. $ancestor->classes[] = 'current-menu-ancestor';
  712. $ancestor->classes[] = 'current-page-ancestor';
  713. }
  714. }
  715. return $menuitems;
  716. }
  717. /**
  718. * Overrides the URL properties for Shopp storefront pages and collections added to the WordPress Menu system
  719. *
  720. * @author Jonathan Davis
  721. * @since 1.2
  722. *
  723. * @param array $items Menu items from WordPress
  724. * @return array Shopp-enabled menu items
  725. **/
  726. function menulinks ($items) {
  727. foreach ($items as &$item) {
  728. switch (strtolower($item->type)) {
  729. case 'shopp_page': $item->url = shoppurl(false,$item->object); break;
  730. case 'shopp_collection':
  731. $namespace = get_class_property( 'SmartCollection' ,'namespace');
  732. $taxonomy = get_class_property( 'SmartCollection' ,'taxon');
  733. $prettyurls = ( '' != get_option('permalink_structure') );
  734. $item->url = shoppurl( $prettyurls ? "$namespace/$item->object" : array($taxonomy=>$item->object),false );
  735. break;
  736. }
  737. }
  738. return $items;
  739. }
  740. /**
  741. * Handles shopping cart requests
  742. *
  743. * @author Jonathan Davis
  744. * @since 1.1
  745. *
  746. * @return void Description...
  747. **/
  748. function cart () {
  749. global $Shopp;
  750. $Cart = $Shopp->Order->Cart;
  751. if (isset($_REQUEST['shopping']) && strtolower($_REQUEST['shopping']) == "reset") {
  752. $Shopping = ShoppShopping();
  753. $Shopping->reset();
  754. shopp_redirect(shoppurl());
  755. }
  756. if (empty($_REQUEST['cart'])) return true;
  757. do_action('shopp_cart_request');
  758. if (isset($_REQUEST['ajax'])) {
  759. $Cart->totals();
  760. $Cart->ajax();
  761. }
  762. $redirect = false;
  763. if (isset($_REQUEST['redirect'])) $redirect = $_REQUEST['redirect'];
  764. switch ($redirect) {
  765. case "checkout": shopp_redirect(shoppurl(false,$redirect,$Shopp->Order->security())); break;
  766. default:
  767. if (!empty($_REQUEST['redirect']))
  768. shopp_safe_redirect($_REQUEST['redirect']);
  769. else shopp_redirect(shoppurl(false,'cart'));
  770. }
  771. }
  772. /**
  773. * Setup and process account dashboard page requests
  774. *
  775. * @author Jonathan Davis
  776. * @since 1.2
  777. *
  778. * @return void
  779. **/
  780. function dashboard () {
  781. $Order = ShoppOrder();
  782. $this->add_dashboard('logout',__('Logout','Shopp'));
  783. $this->add_dashboard('orders',__('Your Orders','Shopp'),true,array(ShoppCustomer(),'load_orders'));
  784. $this->add_dashboard('downloads',__('Downloads','Shopp'),true,array(ShoppCustomer(),'load_downloads'));
  785. $this->add_dashboard('profile',__('My Account','Shopp'),true);
  786. // Pages not in menu navigation
  787. $this->add_dashboard('login',__('Login to your Account'),false);
  788. $this->add_dashboard('recover','Password Recovery',false);
  789. $this->add_dashboard('rp','Password Recovery',false);
  790. $this->add_dashboard('menu',__('Dashboard','Shopp'),false);
  791. do_action('shopp_account_menu');
  792. // Always handle customer profile updates
  793. add_action('shopp_account_management',array(ShoppCustomer(),'profile'));
  794. // Add dashboard page specific handlers
  795. add_action('shopp_account_management',array($this,'dashboard_handler'));
  796. $query = $_SERVER['QUERY_STRING'];
  797. $query = html_entity_decode($query);
  798. $query = explode('&', $query);
  799. $request = 'menu';
  800. $id = false;
  801. foreach ($query as $queryvar) {
  802. $value = false;
  803. if (false !== strpos($queryvar,'=')) list($key,$value) = explode('=',$queryvar);
  804. else $key = $queryvar;
  805. if ( in_array($key,array_keys($this->dashboard))) {
  806. $request = $key;
  807. $id = $value;
  808. }
  809. }
  810. $this->account = compact('request','id');
  811. $download_request = get_query_var('s_dl');
  812. if (!ShoppCustomer()->logged_in()) {
  813. $screens = array('login','recover','rp');
  814. if (!in_array($this->account['request'],$screens))
  815. $this->account = array('request' => 'login','id' => false);
  816. }
  817. do_action('shopp_account_management');
  818. if ('rp' == $request) AccountStorefrontPage::resetpassword($_GET['rp']);
  819. if (isset($_POST['recover-login'])) AccountStorefrontPage::recovery();
  820. }
  821. /**
  822. * Account dashboard callback trigger
  823. *
  824. * @author Jonathan Davis
  825. * @since 1.2
  826. *
  827. * @return void
  828. **/
  829. function dashboard_handler () {
  830. $request = $this->account['request'];
  831. if (isset($this->dashboard[$request])
  832. && is_callable($this->dashboard[$request]->handler))
  833. call_user_func($this->dashboard[$request]->handler);
  834. }
  835. /**
  836. * Registers a new account dashboard page
  837. *
  838. * @author Jonathan Davis
  839. * @since 1.2
  840. *
  841. * @param string $request The query request name associated with the page
  842. * @param string $label The label (title) of the page
  843. * @param boolean $visible Flag to show or hide the page in the menus
  844. * @param string|array $callback The function callback for pre-page processing
  845. * @param int $position The position of the page in the account menu list
  846. * @return void
  847. **/
  848. function add_dashboard ($request,$label,$visible=true,$callback=false,$position=0) {
  849. $this->dashboard[$request] = new StorefrontDashboardPage($request,$label,$callback);
  850. if ($visible) array_splice($this->menus,$position,0,array($this->dashboard[$request]));
  851. }
  852. /**
  853. * Sets handlers for Shopp shortcodes
  854. *
  855. * @author Jonathan Davis
  856. * @since 1.1
  857. *
  858. * @return void
  859. **/
  860. function shortcodes () {
  861. $this->shortcodes = array();
  862. // Additional shortcode functionality
  863. $this->shortcodes['catalog-product'] = array('StorefrontShortcodes','product');
  864. $this->shortcodes['catalog-buynow'] = array('StorefrontShortcodes','buynow');
  865. $this->shortcodes['catalog-collection'] = array('StorefrontShortcodes','collection');
  866. // @deprecated shortcodes
  867. $this->shortcodes['product'] = array('StorefrontShortcodes','product');
  868. $this->shortcodes['buynow'] = array('StorefrontShortcodes','buynow');
  869. $this->shortcodes['category'] = array('StorefrontShortcodes','collection');
  870. foreach ($this->shortcodes as $name => &$callback)
  871. if (shopp_setting('maintenance') == 'on' || !ShoppSettings()->available() || Shopp::maintenance())
  872. add_shortcode($name,array('','maintenance_shortcode'));
  873. else add_shortcode($name,$callback);
  874. }
  875. function autowrap ($content) {
  876. if ( ! in_array(get_the_ID(),$this->shortcoded) ) return $content;
  877. return shoppdiv($content);
  878. }
  879. /**
  880. * Renders the errors template
  881. *
  882. * @author Jonathan Davis
  883. * @since 1.1
  884. *
  885. * @return string The processed errors.php template file
  886. **/
  887. static function errors ($template='errors.php') {
  888. ob_start();
  889. locate_shopp_template((array)$template,true);
  890. $content = ob_get_contents();
  891. ob_end_clean();
  892. return apply_filters('shopp_storefront_errors',$content);
  893. }
  894. static function default_pages () {
  895. return array(
  896. 'catalog' => array('title' => __('Shop','Shopp'), 'slug' => 'shop', 'description'=>__('The page title and base slug for products, categories &amp; collections.','Shopp') ),
  897. 'account' => array('title' => __('Account','Shopp'), 'slug' => 'account', 'description'=>__('Used to display customer account dashboard &amp; profile pages.','Shopp'),'edit' => array('page' => 'shopp-settings-pages') ),
  898. 'cart' => array('title' => __('Cart','Shopp'), 'slug' => 'cart', 'description'=>__('Displays the shopping cart.','Shopp') ),
  899. 'checkout' => array('title' => __('Checkout','Shopp'), 'slug' => 'checkout', 'description'=>__('Displays the checkout form.','Shopp') ),
  900. 'confirm' => array('title' => __('Confirm Order','Shopp'), 'slug' => 'confirm-order', 'description'=>__('Used to display an order summary to confirm changes in order price.','Shopp') ),
  901. 'thanks' => array('title' => __('Thank You!','Shopp'), 'slug' => 'thanks', 'description'=>__('The final page of the ordering process.','Shopp') ),
  902. );
  903. }
  904. static function pages_settings ($updates=false) {
  905. $pages = self::default_pages();
  906. $ShoppSettings = ShoppSettings();
  907. if (!$ShoppSettings) $ShoppSettings = new Settings();
  908. $settings = $ShoppSettings->get('storefront_pages');
  909. // @todo Check if slug is unique amongst shopp_product post type records to prevent namespace conflicts
  910. foreach ($pages as $name => &$page) {
  911. $page['name'] = $name;
  912. if (is_array($settings) && isset($settings[$name]))
  913. $page = array_merge($page,$settings[$name]);
  914. if (is_array($updates) && isset($updates[$name]))
  915. $page = array_merge($page,$updates[$name]);
  916. }
  917. // Remove pages if the shopping cart is disabled
  918. if (!shopp_setting_enabled('shopping_cart'))
  919. unset($pages['cart'],$pages['checkout'],$pages['confirm'],$pages['thanks']);
  920. return $pages;
  921. }
  922. /**
  923. * Provides the Storefront page slug by its named system ID
  924. *
  925. * @author Jonathan Davis
  926. * @since 1.2
  927. * @see Storefront::default_pages()
  928. *
  929. * @return string Named ID of the page
  930. **/
  931. static function slug ($page='catalog') {
  932. $pages = self::pages_settings();
  933. if (!isset($pages[$page])) $page = 'catalog';
  934. return $pages[$page]['slug'];
  935. }
  936. /**
  937. * Provides the system named ID from a Storefront page slug
  938. *
  939. * @author Jonathan Davis
  940. * @since 1.2
  941. *
  942. * @return string The page slug
  943. **/
  944. static function slugpage ($slug) {
  945. $pages = self::pages_settings();
  946. foreach ($pages as $name => $page)
  947. if ($slug == $page['slug']) return $name;
  948. return false;
  949. }
  950. } // END class Storefront
  951. /**
  952. * StorefrontPage
  953. *
  954. * A base utility class to provide basic WordPress content override behaviors
  955. * for rendering Shopp storefront content. StorefrontPage classes use filters
  956. * to override the page title and content with information from Shopp provided
  957. * by template instructions in Shopp content templates.
  958. *
  959. * @author Jonathan Davis
  960. * @since 1.2.1
  961. * @package shopp
  962. **/
  963. class StorefrontPage {
  964. var $name = '';
  965. var $title = '';
  966. var $slug = '';
  967. var $description = '';
  968. var $template = '';
  969. var $edit = '';
  970. var $stubpost = false;
  971. function __construct ( $options = array() ) {
  972. $defaults = array(
  973. 'name' => '',
  974. 'title' => '',
  975. 'slug' => '',
  976. 'description' => '',
  977. 'template' => false,
  978. 'edit' => array()
  979. );
  980. $options = array_merge($defaults,$options);
  981. foreach ($options as $name => $value)
  982. if (isset($this->$name)) $this->$name = $value;
  983. add_filter('get_edit_post_link',array($this,'editlink'));
  984. // Page title has to be reprocessed
  985. add_filter('wp_title',array($this,'title'),9);
  986. add_filter('single_post_title',array($this,'title'));
  987. add_filter('the_title',array($this,'title'));
  988. add_filter('the_content',array($this,'content'),20);
  989. add_filter('the_excerpt',array($this,'content'),20);
  990. add_filter('shopp_content_container_classes',array($this,'styleclass'));
  991. }
  992. function editlink ($link) {
  993. $url = admin_url('admin.php');
  994. if (!empty($this->edit)) $url = add_query_arg($this->edit,$url);
  995. return $url;
  996. }
  997. function styleclass ($classes) {
  998. $classes[] = $this->name;
  999. return $classes;
  1000. }
  1001. function content ($content) {
  1002. return $content;
  1003. }
  1004. /**
  1005. * Provides the title for the page from settings
  1006. *
  1007. * @author Jonathan Davis
  1008. * @since 1.2
  1009. *
  1010. * @return string The page title
  1011. **/
  1012. function title ($title) {
  1013. global $wp_query,$wp_the_query;
  1014. if ( $wp_the_query !== $wp_query) return $title;
  1015. if ( empty($title) ) return apply_filters('shopp_'.$this->name.'_pagetitle',$this->title);
  1016. return $title;
  1017. }
  1018. function templates () {
  1019. $templates = array('shopp.php','page.php');
  1020. if (!empty($this->name)) array_unshift($templates,"$this->name.php");
  1021. if (!empty($this->template)) array_unshift($templates,$this->template);
  1022. return $templates;
  1023. }
  1024. function poststub () {
  1025. global $wp_query,$wp_the_query;
  1026. if ($wp_the_query !== $wp_query) return;
  1027. $stub = new WPDatabaseObject();
  1028. $stub->init('posts');
  1029. $stub->ID = -42; // 42, the answer to everything. Force the stub to an unusable post ID
  1030. $stub->comment_status = 'closed'; // Force comments closed
  1031. $stub->post_title = $this->title;
  1032. $stub->post_content = '';
  1033. $wp_query->posts = array($stub);
  1034. }
  1035. }
  1036. /**
  1037. * CatalogStorefrontPage
  1038. *
  1039. * Renders the Shopp catalog storefront page
  1040. *
  1041. * @author Jonathan Davis
  1042. * @since 1.2.1
  1043. * @package shopp
  1044. **/
  1045. class CatalogStorefrontPage extends StorefrontPage {
  1046. function content ($content) {
  1047. global $wp_query,$wp_the_query;
  1048. // Test that this is the main query and it is a catalog page
  1049. if ( $wp_the_query !== $wp_query || ! is_catalog_frontpage() ) return $content;
  1050. global $Shopp,$wp,$wp_query;
  1051. if (SHOPP_DEBUG) new ShoppError('Displaying catalog page request: '.$_SERVER['REQUEST_URI'],'shopp_catalog',SHOPP_DEBUG_ERR);
  1052. ob_start();
  1053. locate_shopp_template(array('catalog.php'),true);
  1054. $content = ob_get_contents();
  1055. ob_end_clean();
  1056. return apply_filters('shopp_catalog_template',$content);
  1057. }
  1058. }
  1059. /**
  1060. * ProductStorefrontPage
  1061. *
  1062. * Handles rendering storefront the product page
  1063. *
  1064. * @author Jonathan Davis
  1065. * @since 1.2.1
  1066. * @package shopp
  1067. **/
  1068. class ProductStorefrontPage extends StorefrontPage {
  1069. function __construct ( $settings = array() ) {
  1070. $settings['template'] = 'single-'.Product::$posttype. '.php';
  1071. parent::__construct($settings);
  1072. }
  1073. function editlink ($link) {
  1074. return $link;
  1075. }
  1076. function content ($content) {
  1077. global $wp_query,$wp_the_query;
  1078. // Test that this is the main query and it is a product
  1079. if ( $wp_the_query !== $wp_query || ! is_shopp_product() ) return $content;
  1080. $Product = ShoppProduct();
  1081. $templates = array('product.php');
  1082. if (isset($Product->id) && !empty($Product->id))
  1083. array_unshift($templates,'product-'.$Product->id.'.php');
  1084. if (isset($Product->slug) && !empty($Product->slug))
  1085. array_unshift($templates,'product-'.$Product->slug.'.php');
  1086. // Load product summary data, before checking inventory
  1087. if (!isset($Product->summed)) $Product->load_data(array('summary'));
  1088. if ( str_true($Product->inventory) && $Product->stock < 1 )
  1089. array_unshift($templates,'product-outofstock.php');
  1090. ob_start();
  1091. locate_shopp_template($templates,true);
  1092. $content = ob_get_contents();
  1093. ob_end_clean();
  1094. return shoppdiv($content);
  1095. }
  1096. }
  1097. /**
  1098. * CollectionStorefrontPage
  1099. *
  1100. * Responsible for Shopp product collections, custom categories, tags and other taxonomy pages for Shopp
  1101. *
  1102. * @author Jonathan Davis
  1103. * @since 1.2.1
  1104. * @package shopp
  1105. **/
  1106. class CollectionStorefrontPage extends StorefrontPage {
  1107. function __construct ( $settings = array() ) {
  1108. $Collection = ShoppCollection();
  1109. // Define the edit link for collections and taxonomies
  1110. $editlink = add_query_arg('page','shopp-settings-pages',admin_url('admin.php'));
  1111. if (isset($Collection->taxonomy) && isset($Collection->id)) {
  1112. $page = 'edit-tags.php';
  1113. $query = array(
  1114. 'action' => 'edit',
  1115. 'taxonomy' => $Collection->taxonomy,
  1116. 'tag_ID' => $Collection->id
  1117. );
  1118. if ('shopp_category' == $Collection->taxonomy) {
  1119. $page = 'admin.php';
  1120. $query = array(
  1121. 'page' => 'shopp-categories',
  1122. 'id' => $Collection->id
  1123. );
  1124. }
  1125. $editlink = add_query_arg($query,admin_url($page));
  1126. }
  1127. $settings = array(
  1128. 'title' => shopp($Collection,'get-name'),
  1129. 'edit' => $editlink,
  1130. 'template' => 'shopp-collection.php',
  1131. );
  1132. parent::__construct($settings);
  1133. }
  1134. function editlink ($link) {
  1135. return $this->edit;
  1136. }
  1137. function content ($content) {
  1138. global $wp_query,$wp_the_query;
  1139. // Only modify content for Shopp collections (Shopp smart collections and taxonomies)
  1140. if ( $wp_the_query !== $wp_query || ! is_shopp_collection() ) return $content;
  1141. $Collection = ShoppCollection();
  1142. ob_start();
  1143. if (empty($Collection)) locate_shopp_template(array('catalog.php'),true);
  1144. else {
  1145. $templates = array('category.php','collection.php');
  1146. $ids = array('slug','id');
  1147. foreach ($ids as $property) {
  1148. if (isset($Collection->$property)) $id = $Collection->$property;
  1149. array_unshift($templates,'category-'.$id.'.php','collection-'.$id.'.php');
  1150. }
  1151. locate_shopp_template($templates,true);
  1152. }
  1153. $content = ob_get_contents();
  1154. ob_end_clean();
  1155. return apply_filters('shopp_category_template',$content);
  1156. }
  1157. }
  1158. /**
  1159. * AccountStorefrontPage
  1160. *
  1161. * The account dashboard page
  1162. *
  1163. * @author Jonathan Davis
  1164. * @since 1.2.1
  1165. * @package shopp
  1166. **/
  1167. class AccountStorefrontPage extends StorefrontPage {
  1168. function __construct ( $settings = array() ) {
  1169. if ('none' == shopp_setting('account_system')) {
  1170. $settings['title'] = __('Order Lookup','Shopp');
  1171. }
  1172. parent::__construct($settings);
  1173. }
  1174. function content ($content,$request=false) {
  1175. if (!$request) {
  1176. global $wp_query,$wp_the_query;
  1177. // Test that this is the main query and it is the account page
  1178. if ( $wp_the_query !== $wp_query || ! is_shopp_page('account') ) return $content;
  1179. }
  1180. if ('none' == shopp_setting('account_system'))
  1181. return apply_filters('shopp_account_template',shopp('customer','get-order-lookup'));
  1182. $download_request = get_query_var('s_dl');
  1183. if (!$request) $request = ShoppStorefront()->account['request'];
  1184. $templates = array('account-'.$request.'.php','account.php');
  1185. if ('login' == $request || !ShoppCustomer()->logged_in()) $templates = array('login-'.$request.'.php','login.php');
  1186. ob_start();
  1187. if (apply_filters('shopp_show_account_errors',true) && ShoppErrors()->exist(SHOPP_AUTH_ERR))
  1188. echo Storefront::errors(array('account-errors.php','errors.php'));
  1189. locate_shopp_template($templates,true);
  1190. $content = ob_get_contents();
  1191. ob_end_clean();
  1192. return apply_filters('shopp_account_template',$content);
  1193. }
  1194. /**
  1195. * Password recovery processing
  1196. *
  1197. * @author Jonathan Davis
  1198. * @since 1.0
  1199. * @version 1.1
  1200. *
  1201. * @return void
  1202. **/
  1203. static function recovery () {
  1204. $errors = array();
  1205. // Check email or login supplied
  1206. if (empty($_POST['account-login'])) {
  1207. if ( 'wordpress' == shopp_setting('account_system') ) $errors[] = new ShoppError(__('Enter an email address or login name','Shopp'));
  1208. else $errors[] = new ShoppError(__('Enter an email address','Shopp'));
  1209. } else {
  1210. // Check that the account exists
  1211. if (strpos($_POST['account-login'],'@') !== false) {
  1212. $RecoveryCustomer = new Customer($_POST['account-login'],'email');
  1213. if (!$RecoveryCustomer->id)
  1214. $errors[] = new ShoppError(__('There is no user registered with that email address.','Shopp'),'password_recover_noaccount',SHOPP_AUTH_ERR);
  1215. } else {
  1216. $user_data = get_userdatabylogin($_POST['account-login']);
  1217. $RecoveryCustomer = new Customer($user_data->ID,'wpuser');
  1218. if (empty($RecoveryCustomer->id))
  1219. $errors[] = new ShoppError(__('There is no user registered with that login name.','Shopp'),'password_recover_noaccount',SHOPP_AUTH_ERR);
  1220. }
  1221. }
  1222. // return errors
  1223. if (!empty($errors)) return;
  1224. // Generate new key
  1225. $RecoveryCustomer->activation = wp_generate_password(20, false);
  1226. do_action_ref_array('shopp_generate_password_key', array(&$RecoveryCustomer));
  1227. $RecoveryCustomer->save();
  1228. $subject = apply_filters('shopp_recover_password_subject', sprintf(__('[%s] Password Recovery Request','Shopp'),get_option('blogname')));
  1229. $_ = array();
  1230. $_[] = 'From: "'.get_option('blogname').'" <'.shopp_setting('merchant_email').'>';
  1231. $_[] = 'To: '.$RecoveryCustomer->email;
  1232. $_[] = 'Subject: '.$subject;
  1233. $_[] = '';
  1234. $_[] = '<p>'.__('A request has been made to reset the password for the following site and account:','Shopp').'<br />';
  1235. $_[] = get_option('siteurl').'</p>';
  1236. $_[] = '';
  1237. $_[] = '<ul>';
  1238. if (isset($_POST['email-login']))
  1239. $_[] = '<li>'.sprintf(__('Email: %s','Shopp'), $RecoveryCustomer->email).'</li>';
  1240. if (isset($_POST['loginname-login']))
  1241. $_[] = '<li>'.sprintf(__('Login name: %s','Shopp'), $user_data->user_login).'</li>';
  1242. if (isset($_POST['account-login']))
  1243. $_[] = '<li>'.sprintf(__('Login: %s','Shopp'), $user_data->user_login).'</li>';
  1244. $_[] = '</ul>';
  1245. $_[] = '';
  1246. $_[] = '<p>'.__('To reset your password visit the following address, otherwise just ignore this email and nothing will happen.');
  1247. $_[] = '';
  1248. $_[] = '<p>'.add_query_arg(array('rp'=>$RecoveryCustomer->activation),shoppurl(false,'account')).'</p>';
  1249. $message = apply_filters('shopp_recover_password_message',$_);
  1250. if (!shopp_email(join("\n",$message))) {
  1251. new ShoppError(__('The e-mail could not be sent.'),'password_recovery_email',SHOPP_ERR);
  1252. shopp_redirect(add_query_arg('acct','recover',shoppurl(false,'account')));
  1253. } else {
  1254. new ShoppError(__('Check your email address for instructions on resetting the password for your account.','Shopp'),'password_recovery_email',SHOPP_ERR);
  1255. }
  1256. }
  1257. static function resetpassword ($activation) {
  1258. if ( 'none' == shopp_setting('account_system') ) return;
  1259. $user_data = false;
  1260. $activation = preg_replace('/[^a-z0-9]/i', '', $activation);
  1261. $errors = array();
  1262. if (empty($activation) || !is_string($activation))
  1263. $errors[] = new ShoppError(__('Invalid key','Shopp'));
  1264. $RecoveryCustomer = new Customer($activation,'activation');
  1265. if (empty($RecoveryCustomer->id))
  1266. $errors[] = new ShoppError(__('Invalid key','Shopp'));
  1267. if (!empty($errors)) return false;
  1268. // Generate a new random password
  1269. $password = wp_generate_password();
  1270. do_action_ref_array('password_reset', array(&$RecoveryCustomer,$password));
  1271. $RecoveryCustomer->password = wp_hash_password($password);
  1272. if ( 'wordpress' == shopp_setting('account_system') ) {
  1273. $user_data = get_userdata($RecoveryCustomer->wpuser);
  1274. wp_set_password($password, $user_data->ID);
  1275. }
  1276. $RecoveryCustomer->activation = '';
  1277. $RecoveryCustomer->save();
  1278. $subject = apply_filters('shopp_reset_password_subject', sprintf(__('[%s] New Password','Shopp'),get_option('blogname')));
  1279. $_ = array();
  1280. $_[] = 'From: "'.get_option('blogname').'" <'.shopp_setting('merchant_email').'>';
  1281. $_[] = 'To: '.$RecoveryCustomer->email;
  1282. $_[] = 'Subject: '.$subject;
  1283. $_[] = '';
  1284. $_[] = '<p>'.sprintf(__('Your new password for %s:','Shopp'),get_option('siteurl')).'</p>';
  1285. $_[] = '';
  1286. $_[] = '<ul>';
  1287. if ($user_data)
  1288. $_[] = '<li>'.sprintf(__('Login name: %s','Shopp'), $user_data->user_login).'</li>';
  1289. $_[] = '<li>'.sprintf(__('Password: %s'), $password).'</li>';
  1290. $_[] = '</ul>';
  1291. $_[] = '';
  1292. $_[] = '<p>'.__('Click here to login:').' '.shoppurl(false,'account').'</p>';
  1293. $message = apply_filters('shopp_reset_password_message',$_);
  1294. if (!shopp_email(join("\n",$message))) {
  1295. new ShoppError(__('The e-mail could not be sent.'),'password_reset_email',SHOPP_ERR);
  1296. shopp_redirect(add_query_arg('acct','recover',shoppurl(false,'account')));
  1297. } else new ShoppError(__('Check your email address for your new password.','Shopp'),'password_reset_email',SHOPP_ERR);
  1298. unset($_GET['acct']);
  1299. }
  1300. }
  1301. /**
  1302. * CartStorefrontPage
  1303. *
  1304. * The shopping cart page
  1305. *
  1306. * @author Jonathan Davis
  1307. * @since 1.2.1
  1308. * @package shopp
  1309. **/
  1310. class CartStorefrontPage extends StorefrontPage {
  1311. function content ($content) {
  1312. global $wp_query,$wp_the_query;
  1313. // Test that this is the main query and it is the cart page
  1314. if ( $wp_the_query !== $wp_query || ! is_shopp_page('cart') ) return $content;
  1315. $Order = ShoppOrder();
  1316. $Cart = $Order->Cart;
  1317. ob_start();
  1318. if (ShoppErrors()->exist(SHOPP_COMM_ERR)) echo Storefront::errors(array('errors.php'));
  1319. locate_shopp_template(array('cart.php'),true);
  1320. $content = ob_get_contents();
  1321. ob_end_clean();
  1322. return apply_filters('shopp_cart_template',$content);
  1323. }
  1324. }
  1325. /**
  1326. * CheckoutStorefrontPage
  1327. *
  1328. * The checkout page.
  1329. *
  1330. * @author Jonathan Davis
  1331. * @since 1.2.1
  1332. * @package shopp
  1333. **/
  1334. class CheckoutStorefrontPage extends StorefrontPage {
  1335. function content ($content) {
  1336. global $wp_query,$wp_the_query;
  1337. // Test that this is the main query and it is the checkout page
  1338. if ( $wp_the_query !== $wp_query || ! is_shopp_page('checkout') ) return $content;
  1339. $Errors = ShoppErrors();
  1340. $Order = ShoppOrder();
  1341. $Cart = $Order->Cart;
  1342. $process = get_query_var('s_pr');
  1343. do_action('shopp_init_checkout');
  1344. ob_start();
  1345. if ($Errors->exist(SHOPP_COMM_ERR))
  1346. echo Storefront::errors(array('errors.php'));
  1347. ShoppStorefront()->checkout = true;
  1348. locate_shopp_template(array('checkout.php'),true);
  1349. ShoppStorefront()->checkout = false;
  1350. $content = ob_get_contents();
  1351. ob_end_clean();
  1352. return apply_filters('shopp_checkout_page',$content);
  1353. }
  1354. }
  1355. /**
  1356. * ConfirmStorefrontPage
  1357. *
  1358. * The confirmation page shown after submitting the checkout form. This page
  1359. * is designed to give customers a chance to confirm order details. This is necessary
  1360. * for situations where the address details change from the shopping cart shipping and
  1361. * tax estimates to a final address that cause shipping and taxes to recalculate. The
  1362. * customer must be given the opportunity to see the cost changes before proceeding
  1363. * with the order.
  1364. *
  1365. * @author Jonathan Davis
  1366. * @since 1.2.1
  1367. * @package shopp
  1368. **/
  1369. class ConfirmStorefrontPage extends StorefrontPage {
  1370. function content ($content) {
  1371. global $wp_query,$wp_the_query;
  1372. // Test that this is the main query and it is the confirm order page
  1373. if ( $wp_the_query !== $wp_query || ! is_shopp_page

Large files files are truncated, but you can click here to view the full file