-
Notifications
You must be signed in to change notification settings - Fork 4
/
usu.php
1638 lines (1485 loc) · 64.4 KB
/
usu.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
/**
* Part of Ultimate URLs for Zen Cart. Originally derived from Ultimate SEO URLs
* v2.1 for osCommerce by Chemo.
*
* @copyright Copyright 2019-2021 Cindy Merkin (vinosdefrutastropicales.com), @prosela
* @copyright Copyright 2012 - 2015 Andrew Ballanger
* @copyright Portions Copyright 2003 - 2015 Zen Cart Development Team
* @copyright Portions Copyright 2005 Joshua Dechant
* @copyright Portions Copyright 2005 Bobby Easland
* @license http://www.gnu.org/licenses/gpl.txt GNU GPL V3.0
*/
/**
* Provides methods for generating and handling alternative URLs in Zen Cart.
*
* This handles: canonical URLs, 301 redirects, multilingual URLs, filtering
* product / category / ez-page names, non-cookie sessions (zenid), and caching
* of generated URLs.
*/
class usu
{
public $canonical = null;
protected $cache,
$languages_id,
$parameters_valid,
$reg_anchors,
$cache_file,
$uri,
$real_uri,
$redirect_uri;
protected static $unicodeEnabled;
private $filter_pcre,
$filter_char,
$filter_page;
function __construct($languages_id = '')
{
if ($languages_id == '') {
$languages_id = $_SESSION['languages_id'];
}
$this->languages_id = (int)$languages_id;
$this->cache = false;
$this->reg_anchors = array(
'products_id' => '-p-',
'cPath' => '-c-',
'manufacturers_id' => '-m-',
'pID' => '-pi-',
'products_id_review' => '-pr-',
'products_id_review_info' => '-pri-',
'id' => '-ezp-',
);
if (null === self::$unicodeEnabled) {
self::$unicodeEnabled = (@preg_match('/\pL/u', 'a')) ? true : false;
}
$this->filter_pcre = defined('USU_FILTER_PCRE') ? $this->expand(USU_FILTER_PCRE) : 'false';
$this->filter_page = defined('USU_FILTER_PAGES') && zen_not_null(USU_FILTER_PAGES) ? explode(',', str_replace(' ', '', USU_FILTER_PAGES)) : array();
if (defined('USU_CACHE_GLOBAL') && USU_CACHE_GLOBAL == 'true') {
// Prepare in memory cache
$this->cache = array(
'PRODUCTS' => array(),
'CATEGORIES' => array(),
'MANUFACTURERS' => array(),
'EZPAGES' => array()
);
// Handle the SQL cache options if the table exists
if ($GLOBALS['sniffer']->table_exists(TABLE_USU_CACHE)) {
// Cleanup the SQL caches
$this->cache_file = 'usu_v3_';
$this->cache_gc(); // Cleanup Cache
// Load or generate enabled SQL caches
if (USU_CACHE_PRODUCTS == 'true') {
$this->generate_products_cache();
}
if (USU_CACHE_CATEGORIES == 'true') {
$this->generate_categories_cache();
}
if (USU_CACHE_MANUFACTURERS == 'true') {
$this->generate_manufacturers_cache();
}
if (USU_CACHE_EZ_PAGES == 'true') {
$this->generate_ezpages_cache();
}
} elseif (IS_ADMIN_FLAG) {
// Message Stack will be available when loaded from the admin
$GLOBALS['messageStack']->add(sprintf(USU_PLUGIN_WARNING_TABLE, TABLE_USU_CACHE), 'warning');
}
}
// Start logging
$this->debug = false;
if (defined('USU_DEBUG') && USU_DEBUG == 'true') {
$this->debug = true;
if (IS_ADMIN_FLAG) {
$this->logfile = DIR_FS_LOGS . '/usu-adm-' . date('Ymd-His') . '.log';
$this->logpage = $_SERVER['SCRIPT_NAME'];
} else {
$this->logfile = DIR_FS_LOGS . '/usu-' . date('Ymd-His') . '.log';
$this->logpage = (isset($_GET['main_page'])) ? $_GET['main_page'] : 'index';
}
}
}
/**
* Generates the link to the requested page suitable for use in an href
* paramater.
*
* @param string $page the name of the page
* @param string $parameters any paramaters for the page
* @param string $connection 'NONSSL' or 'SSL' the type of connection to use
* @param bool $add_session_id true if a session id be added to the url, false otherwise
* @param bool $search_engine_safe true if we should use search engine safe urls, false otherwise
* @param bool $static true if this is a static page (no paramaters)
* @param bool $use_dir_ws_catalog true if we should use the DIR_WS_CATALOG / DIR_WS_HTTPS_CATALOG from the configuration
* @return NULL|string
*/
public function href_link($page = '', $parameters = '', $connection = 'NONSSL', $add_session_id = true, $search_engine_safe = true, $static = false, $use_dir_ws_catalog = true)
{
// Do not create an alternate URI when disabled
if (!defined('USU_ENABLED') || USU_ENABLED == 'false') {
return null;
}
// -----
// If this is the first href-link generated for the current page's rendering, include some information
// regarding which page (storefront vs. admin) that the request is being generated for.
//
if (!isset($this->first_access)) {
$this->first_access = true;
$this->log("=====> URL Generation Log Started, for page: {$this->uri}.");
}
$this->log(PHP_EOL .
'Request sent to href_link(' . var_export($page, true) . ', ' .
var_export($parameters, true) . ', ' . var_export($connection, true) . ', ' .
var_export($add_session_id, true) . ', ' . var_export($search_engine_safe, true) . ', ' .
var_export($static, true) . ', ' . var_export($use_dir_ws_catalog, true) . ')'
);
// If the request was for a real file (which called application_top)
// We should not create an alternate URI (such as ipn_main_handler.php).
if (zen_not_null($page) && $page != FILENAME_DEFAULT && $this->is_physical_file($page)) {
$this->log('Request was to a physical file, URI not generated!');
return null;
}
// Much of the code in Zen Cart creates the dynamic link by itself before
// passing the code to zen_href_link and then just claims the link is
// static and passes no params. Much of the code also has the bad habit
// of claiming a link is "static" when it is not. So we ignore the value
// of "static" if the page starts with "index.php?"
if (strstr($page, 'index.php?') !== false) {
// If we find the main_page parse the URL
$result = array();
if (preg_match('/[?&]main_page=([^&]*)/', $page, $result) === 1) {
$temp = parse_url($page);
// Adjust the page and parameters to be correct. This mainly
// fixes the handling of EZ-Pages (but may fix additional pages).
$page = $result[1];
$temp['query'] = preg_replace('/main_page=' . $result[1] . '/', '', $temp['query']);
$parameters = $temp['query'] . ($parameters != '' ? '&' . $parameters : '');
}
}
// Remove the end from the page if it is present
$pos = strrpos($page, USU_END);
if ($pos !== false) {
$page = substr($page, 0, $pos);
}
unset($pos);
// Do not rewrite if page is not in the list of pages to rewrite
if (!$this->filter_page($page)) {
$this->log("Page ($page) was not in the list of pages to rewrite, URI not generated!");
return null;
}
// The base URL for the request
if (IS_ADMIN_FLAG === true) {
if ($connection == 'SSL' && ENABLE_SSL_CATALOG == 'true') {
$link = HTTPS_CATALOG_SERVER;
if ($use_dir_ws_catalog) {
$link .= DIR_WS_HTTPS_CATALOG;
}
} else {
$link = HTTP_CATALOG_SERVER;
if ($use_dir_ws_catalog) {
$link .= DIR_WS_CATALOG;
}
}
} elseif ($connection == 'SSL' && ENABLE_SSL == 'true') {
$link = HTTPS_SERVER;
if ($use_dir_ws_catalog) {
$link .= DIR_WS_HTTPS_CATALOG;
}
} else {
$link = HTTP_SERVER;
if ($use_dir_ws_catalog) {
$link .= DIR_WS_CATALOG;
}
}
// -----
// Indicate that, so far, no issues have been found with the link's
// parameters. That might be changed during the parse_parameters method's
// processing, if an invalid products_id, cPath or EZ-Pages' id parameter is
// found.
//
$this->parameters_valid = true;
// We start with no separator, so define one.
$separator = '?';
if (zen_not_null($parameters)) {
$link .= $this->parse_parameters($page, $parameters, $separator);
} else {
$link .= (($page != FILENAME_DEFAULT && $page != '') ? $page . USU_END : '');
}
// -----
// If the parameters supplied for the link aren't valid, don't regenerate a USU-formatted
// link.
//
if (!$this->parameters_valid) {
return null;
}
$link = $this->add_sid($link, $add_session_id, $connection, $separator);
// -----
// As of v3.0.9, the USU class is instantiated _prior to_ the init_sanitize.php load to prevent
// a redirect-loop if an invalid/deleted product is associated with a 'products_id' variable in
// the requested link.
//
// Unfortunately, that initialization file is loaded _prior to_ the language-file loads,
// which is where the CHARSET definition occurs. We'll need to default to a 'utf-8' character
// set if that initialization processing is the point at which this href_link is being
// requested.
//
$charset = (defined('CHARSET')) ? CHARSET : 'utf-8';
$generated_url = htmlspecialchars($link, ENT_QUOTES, $charset, false);
$this->log("Generated URL: $generated_url");
return $generated_url;
}
/**
* Adds the sid to the end of the URL if needed. If a page cache has been
* enabled and no customer is logged in the sid is replaced with '<zinsid>'.
*
* @param string $link current URL.
* @param bool $add_session_id true if a session id be added to the url, false otherwise
* @param string $connection 'NONSSL' or 'SSL' the type of connection to use
* @param string $separator the separator to use between the link and this paramater (if added)
* @return string
*/
protected function add_sid($link, $add_session_id, $connection, $separator)
{
global $request_type, $http_domain, $https_domain, $session_started;
$_sid = '';
if ($add_session_id == true && $session_started && SESSION_FORCE_COOKIE_USE == 'False') {
if (defined('SID') && !empty(constant('SID'))) {
$_sid = constant('SID');
} else {
$ssl_enabled = (IS_ADMIN_FLAG === true) ? ENABLE_SSL_CATALOG : ENABLE_SSL;
if (($request_type == 'NONSSL' && $connection == 'SSL' && $ssl_enabled == 'true') || ($request_type == 'SSL' && $connection == 'NONSSL')) {
if ($http_domain != $https_domain) {
$_sid = zen_session_name() . '=' . zen_session_id();
}
}
}
}
switch (true) {
case (!isset($_SESSION['customer_id']) && defined('ENABLE_PAGE_CACHE') && ENABLE_PAGE_CACHE == 'true' && class_exists('page_cache')):
$return = $link . $separator . '<zensid>';
break;
case (!empty($_sid)):
$return = $link . $separator . $_sid;
break;
default:
$return = $link;
break;
}
return $return;
}
/**
* Parses the parameters for a page to generate a valid url for the page.
*
* @param string $page the name of the page
* @param string $params any parameters for the page
* @param string $separator the separator to use between the link and this parameter (if needed)
* @return Ambiguous <string, unknown>
*/
protected function parse_parameters($page, $params, &$separator)
{
// -----
// Strip any leading 'amp;' and change any '&' to '&'.
//
if (strpos($params, 'amp;') === 0) {
$params = substr($params, 4);
}
$params = str_replace('&', '&', $params);
// We always need cPath to be first, so find and extract
$cPath = false;
if (1 === preg_match('/(?:^|&)c[Pp]ath=([^&]+)/', $params, $path)) {
$params = str_replace($path[0], '', $params);
$cPath = $path[1];
}
// Cleanup parameters and convert to initial array
$params = trim($params, "?& \t\n\r\0\x0B");
$p = array();
if (!empty($params) && is_string($params)) {
$p = explode('&', $params);
}
// Add the cPath to the start of the parameters if present
if ($cPath !== false) {
array_unshift($p, 'cPath=' . $cPath);
}
$this->log('Parsing Parameters for ' . $page);
$this->log(var_export($p, true));
$link_params = array();
foreach ($p as $valuepair) {
// -----
// No '=' separating the key from its value? Set it, so that the array has at least
// two elements.
//
if (strpos($valuepair, '=') === false) {
$p2 = array($valuepair, '');
} else {
$p2 = explode('=', $valuepair);
}
// -----
// Determine if the to-be-generated URL is for one of the 'encoded' pages.
//
switch ($p2[0]) {
// -----
// If the 'products_id' variable is present, it could be for a product's details page or
// a product's reviews listing or detailed review.
//
case 'products_id':
// -----
// Make sure if uprid is passed it is converted to the correct pid
//
$prid = (int)zen_get_prid($p2[1]);
// -----
// If a cPath was supplied for the link, grab the immediate parent 'category'.
//
$cID = null;
if ($cPath !== false) {
$cID = strrpos($cPath, '_');
if ($cID !== false) {
$cID = substr($cPath, $cID + 1);
} else {
$cID = $cPath;
}
}
// -----
// Now, check for various pages whose URLs are encoded by USU.
//
$url_created = true;
switch (true) {
// -----
// A product's details' page, e.g. product_info.
//
case ($page == $this->getInfoPage($prid)):
$url = $this->make_url($page, $this->get_product_name($prid, $cID), $p2[0], $prid, USU_END);
// -----
// Note: The (string) cast is needed, otherwise the (now integer) $prid will be a match
// to its uprid (if supplied)!
//
if (((string)$prid) != $p2[1]) {
$link_params[] = $valuepair;
}
break;
// -----
// A listing of a product's reviews.
//
case ($page == FILENAME_PRODUCT_REVIEWS):
$url = $this->make_url($page, $this->get_product_name($prid, $cID), 'products_id_review', $prid, USU_END);
break;
// -----
// The display of a product's review details.
//
case ($page == FILENAME_PRODUCT_REVIEWS_INFO):
$url = $this->make_url($page, $this->get_product_name($prid, $cID), 'products_id_review_info', $prid, USU_END);
break;
// -----
// Anything else, just add the parameter to the link's parameters.
//
default:
$url_created = false;
$link_params[] = $valuepair;
break;
}
// -----
// If a product-specific URL was created and the store's configuration indicates that no cPath parameter should
// be included, remove it from the current link parameters.
//
if ($url_created && $cPath !== false && (USU_CATEGORY_DIR != 'off' || USU_CPATH != 'auto')) {
unset($link_params[0]);
}
break;
// -----
// A 'cPath' parameter is normally included on listing pages (for the categories' listings).
//
case 'cPath':
switch (true) {
case ($p2[1] == ''):
// Do nothing if cPath is empty
break;
case ($page == FILENAME_DEFAULT):
// Change $p2[1] to the actual category id
$tmp = strrpos($p2[1], '_');
if ($tmp !== false) {
$p2[1] = substr($p2[1], $tmp+1);
}
$category = $this->get_category_name($p2[1]);
if (USU_CATEGORY_DIR == 'off') {
$url = $this->make_url($page, $category, $p2[0], $p2[1], USU_END);
} else {
$url = $this->make_url($page, $category, $p2[0], $p2[1], '/');
}
unset($category);
break;
default:
$link_params[] = $valuepair;
break;
}
break;
case 'manufacturers_id':
switch (true) {
case ($page == FILENAME_DEFAULT && !$this->is_cPath_string($params) && !$this->is_product_string($params)):
$url = $this->make_url($page, $this->get_manufacturer_name($p2[1]), $p2[0], $p2[1], USU_END);
break;
// -----
// If the current 'page' requested is a 'product_[something_]info', don't add the parameter.
//
case (preg_match('/product_(\S+_)?info/', $page)):
break;
default:
$link_params[] = $valuepair;
break;
}
break;
case 'pID':
switch (true) {
case ($page == FILENAME_POPUP_IMAGE):
$url = $this->make_url($page, $this->get_product_name($p2[1]), $p2[0], $p2[1], USU_END);
break;
default:
$link_params[] = $valuepair;
break;
}
break;
case 'id': // EZ-Pages
switch (true) {
case ($page == FILENAME_EZPAGES):
$url = $this->make_url($page, $this->get_ezpages_name($p2[1]), $p2[0], $p2[1], USU_END);
break;
default:
$link_params[] = $valuepair;
break;
}
break;
default:
$link_params[] = $valuepair;
break;
}
}
$url = isset($url) ? $url : $page . USU_END;
if (!empty($link_params)) {
$url .= $separator . zen_output_string(implode('&', $link_params));
$separator = '&';
}
return $url;
}
protected function getInfoPage($products_id)
{
// -----
// Quick return if the zen_get_info_page function exists, noting that when
// run in the zc156 and earlier admin that it isn't!
//
if (function_exists('zen_get_info_page')) {
return zen_get_info_page($products_id);
}
// -----
// If the function doesn't exist, emulate its output.
//
$check = $GLOBALS['db']->Execute(
"SELECT pt.type_handler
FROM " . TABLE_PRODUCTS . " p
INNER JOIN " . TABLE_PRODUCT_TYPES . " pt
ON pt.type_id = p.products_type
WHERE p.products_id = " . (int)$products_id . "
LIMIT 1"
);
return ($check->EOF) ? 'product_info' : ($check->fields['type_handler'] . '_info');
}
/**
* Convert an array of query parameters to a URI query string. This is safe
* for use under 5.2+ with optimizations for PHP 5.4+.
*
* @param array the array of query parameters
*/
protected function build_query($parameters)
{
if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
$parameters = http_build_query($parameters, '', '&', PHP_QUERY_RFC3986);
} else {
$compile = array();
foreach ($parameters as $key => $value) {
if ($key !== null && $key != '') {
// Prior to PHP 5.3, tildes might be encoded per RFC 1738
// This should not impact functionality for 99% of users.
$compile[] = rawurlencode($key) . '=' . rawurlencode($value);
}
}
$parameters = implode('&', $compile);
unset($compile);
}
return $parameters;
}
/**
* Generates the url for the given page and paramaters.
*
* @param string $page the page for the link
* @param string $link the name to use for the url
* @param string $anchor_type the last paramater parsed type (products_id, cPath, etc.)
* @param string $id the last paramater parsed id (or cPath)
* @param string $extension Default =
* @return string the final generated url
*/
protected function make_url($page, $link, $anchor_type, $id, $extension = USU_END)
{
// In the future there may be additional methods here in the switch
switch (USU_ENGINE){
case 'rewrite':
return $link . $this->reg_anchors[$anchor_type] . $id . $extension;
break;
default:
break;
}
}
/**
* Function to get the product name. Use evaluated cache, per page cache,
* or database query in that order of precedent.
*
* @param integer $pID
* @return string product name
*/
protected function get_product_name($pID, $cID = null)
{
global $db;
$pID = (int)$pID;
// Handle generating the product name
switch (true) {
case (defined('PRODUCT_NAME_' . $pID)):
$return = constant('PRODUCT_NAME_' . $pID);
break;
case (is_array($this->cache) && isset($this->cache['PRODUCTS'][$pID])):
$return = $this->cache['PRODUCTS'][$pID];
break;
default:
$pName = $this->filter(zen_get_products_name($pID));
// -----
// If the products's name wasn't found, indicate that there's a link
// parameter issue so that the requested URL won't be generated.
//
if ($pName === '') {
$this->parameters_valid = false;
} else {
if (USU_FORMAT == 'parent' && USU_CATEGORY_DIR == 'off') {
$masterCatID = (int)zen_get_products_category_id($pID);
$category_id = ($cID !== null ? $cID : $masterCatID);
$pName = $this->get_category_name($category_id, 'original') . '-' . $pName;
}
if (is_array($this->cache)) {
$this->cache['PRODUCTS'][$pID] = $pName;
}
}
$return = $pName;
break;
}
// Add the category
$category = '';
if (USU_CATEGORY_DIR != 'off') {
if (empty($cID)) {
$masterCatID = (int)zen_get_products_category_id($pID);
$category = $this->get_category_name($masterCatID) . $this->reg_anchors['cPath'] . $masterCatID . '/';
} else {
if (zen_product_in_category($pID, $cID)) {
$category = $this->get_category_name($cID) . $this->reg_anchors['cPath'] . $cID . '/';
}
}
$return = $category . $return;
}
return $return;
}
/**
* Function to get the product canonical. Use evaluated cache, per page cache,
* or database query in that order of precedent.
*
* @param integer $pID
* @return string product canonical
*/
protected function get_product_canonical($pID)
{
global $db;
$retval = null;
$pID = (int)$pID;
// Only need to add the canonicals if different paths exist
if (USU_CATEGORY_DIR != 'off') {
// -----
// Selecting the specified product's master-category-id (which defines its canonical
// path for the link) as well as the product's name. Using 'INNER JOIN's since the
// product's name must be present and the product 'should' map to its master-category
// in the products_to_categories table.
//
// If either don't map, we'll return 'null' which will result in the product's canonical
// link being generated without any specific category information.
//
$pName = zen_get_products_name($pID);
if (!empty($pName)) {
$masterID = zen_get_products_category_id($pID);
$retval = $this->get_category_name($masterID) . $this->reg_anchors['cPath'] . $masterID . '/';
// Get the product name
switch (true) {
case (USU_CACHE_GLOBAL == 'true' && defined('PRODUCT_NAME_' . $pID)):
$retval .= constant('PRODUCT_NAME_' . $pID);
break;
case (is_array($this->cache) && isset($this->cache['PRODUCTS'][$pID])):
$retval .= $this->cache['PRODUCTS'][$pID];
break;
default:
if (USU_FORMAT == 'parent' && USU_CATEGORY_DIR == 'off') {
$pName = $this->get_category_name($masterID, 'original') . '-' . $pName;
}
$retval .= $pName;
unset($pName);
break;
}
}
}
return $retval;
}
/**
* Function to get the category name. Use evaluated cache, per page cache,
* or database query in that order of precedent
* @param integer $cID NOTE: passed by reference
* @return string category name
*/
protected function get_category_name(&$cID, $format = USU_FORMAT)
{
global $db;
$single_cID = (int)$cID;
$full_cPath = $this->get_full_cPath($cID, $single_cID); // full cPath needed for uniformity
switch (true) {
case (defined('CATEGORY_NAME_' . $full_cPath) && $format == USU_FORMAT):
$return = constant('CATEGORY_NAME_' . $full_cPath);
break;
case (is_array($this->cache) && isset($this->cache['CATEGORIES'][$full_cPath]) && $format == USU_FORMAT):
$return = $this->cache['CATEGORIES'][$full_cPath];
break;
default:
$cName = '';
if (USU_CATEGORY_DIR == 'full') {
$path = array();
$this->get_parent_categories_path($path, $single_cID);
if (count($path) > 0) {
$cName = implode('/', $path);
$cut = strrpos($cName, $this->reg_anchors['cPath']);
if ($cut !== false) {
$cName = substr($cName, 0, $cut);
}
unset($cut);
}
unset($path);
} elseif ($format == 'parent') {
$sql =
"SELECT c.categories_id, c.parent_id, cd.categories_name AS cName, cd2.categories_name AS cNameParent
FROM " . TABLE_CATEGORIES_DESCRIPTION . " AS cd, " . TABLE_CATEGORIES . " AS c
LEFT JOIN " . TABLE_CATEGORIES_DESCRIPTION . " AS cd2
ON c.parent_id = cd2.categories_id
AND cd2.language_id = {$this->languages_id}
WHERE c.categories_id = $single_cID
AND cd.categories_id = c.categories_id
AND cd.language_id = {$this->languages_id}
LIMIT 1";
$result = $db->Execute($sql, false, true, 43200);
if (!$result->EOF) {
$cName = !empty($result->fields['cNameParent']) ? $this->filter($result->fields['cNameParent'] . ' ' . $result->fields['cName']) : $this->filter($result->fields['cName']);
}
} else {
$sql =
"SELECT categories_name AS cName
FROM " . TABLE_CATEGORIES_DESCRIPTION . "
WHERE categories_id = $single_cID
AND language_id = {$this->languages_id}
LIMIT 1";
$result = $db->Execute($sql, false, true, 43200);
if (!$result->EOF) {
$cName = $this->filter($result->fields['cName']);
}
}
// -----
// If the category's name wasn't found, indicate that there's a link
// parameter issue so that the requested URL won't be generated.
//
if ($cName === '') {
$this->parameters_valid = false;
} elseif (is_array($this->cache)) {
$this->cache['CATEGORIES'][$full_cPath] = $cName;
}
$return = $cName;
break;
}
$cID = $full_cPath;
return $return;
}
/**
* Function to get the manufacturer name. Use evaluated cache, per page cache,
* or database query in that order of precedent
* @param integer $mID
* @return string manufacturer name
*/
protected function get_manufacturer_name($mID)
{
global $db;
$mID = (int)$mID;
switch (true) {
case (defined('MANUFACTURER_NAME_' . $mID)):
$return = constant('MANUFACTURER_NAME_' . $mID);
break;
case (is_array($this->cache) && isset($this->cache['MANUFACTURERS'][$mID])):
$return = $this->cache['MANUFACTURERS'][$mID];
break;
default:
$sql =
"SELECT manufacturers_name as `mName`
FROM " . TABLE_MANUFACTURERS . "
WHERE manufacturers_id = $mID
LIMIT 1";
$result = $db->Execute($sql, false, true, 43200);
$mName = ($result->EOF) ? '' : $this->filter($result->fields['mName']);
// -----
// If the manufacturer's name wasn't found, indicate that there's a link
// parameter issue so that the requested URL won't be generated.
//
if ($mName === '') {
$this->parameters_valid = false;
} elseif (is_array($this->cache)) {
$this->cache['MANUFACTURERS'][$mID] = $mName;
}
$return = $mName;
break;
}
return $return;
}
/**
* Function to get the expage name. Use evaluated cache, per page cache,
* or database query in that order of precedent
* @param integer $ezpID
* @return string expage name
*/
protected function get_ezpages_name($ezpID)
{
global $db;
$ezpID = (int)$ezpID;
switch (true) {
case (defined('EZPAGES_NAME_' . $ezpID)):
$return = constant('EZPAGES_NAME_' . $ezpID);
break;
case (is_array($this->cache) && isset($this->cache['EZPAGES'][$ezpID])):
$return = $this->cache['EZPAGES'][$ezpID];
break;
default:
// -----
// Note: The ez-pages' database structure changed in zc156, incorporating
// multi-lingual ez-pages. First, check for the zc156 implementation, then for
// the pre-base plugin's version and finally for the pre-zc156 implementation.
//
if (defined('TABLE_EZPAGES_TEXT')) {
$sql =
"SELECT pages_title AS ezpName
FROM " . TABLE_EZPAGES_TEXT . "
WHERE pages_id = $ezpID
AND languages_id = {$this->languages_id}
LIMIT 1";
} elseif (defined('TABLE_EZPAGES_CONTENT')) {
$sql =
"SELECT pages_title AS ezpName
FROM " . TABLE_EZPAGES_CONTENT . "
WHERE pages_id = $ezpID
AND languages_id = {$this->languages_id}
LIMIT 1";
} else {
$sql =
"SELECT pages_title AS ezpName
FROM " . TABLE_EZPAGES . "
WHERE pages_id = $ezpID
LIMIT 1";
}
$sql = $db->bindVars($sql, ':ezpage:', $ezpID, 'integer');
$result = $db->Execute($sql, false, true, 43200);
$ezpName = ($result->EOF) ? '' : $this->filter($result->fields['ezpName']);
// -----
// If the EZ-Page's name wasn't found, indicate that there's a link
// parameter issue so that the requested URL won't be generated.
//
if ($ezpName === '') {
$this->parameters_valid = false;
} elseif (is_array($this->cache)) {
$this->cache['EZPAGES'][$ezpID] = $ezpName;
}
$return = $ezpName;
break;
}
return $return;
}
/**
* Function to retrieve full cPath from category ID
* @author Bobby Easland
* @version 1.1
* @param mixed $cID Could contain cPath or single category_id
* @param integer $original Single category_id passed back by reference
* @return string Full cPath string
*/
protected function get_full_cPath($cID, &$original)
{
if (strpos($cID, '_') !== false) {
$temp = explode('_', $cID);
$original = array_pop($temp);
return $cID;
} else {
$c = array();
zen_get_parent_categories($c, $cID);
$c = array_reverse($c);
$c[] = $cID;
$original = $cID;
$cID = implode('_', $c);
return $cID;
}
} # end function
/**
* Recursion function to retrieve parent categories from category ID.
*
* @author Andrew Ballanger
* @param mixed $path Passed by reference
* @param integer $categories_id
*/
protected function get_parent_categories_path(&$path, $categories_id, &$cPath = array())
{
global $db;
$categories_id = (int)$categories_id;
$sql =
"SELECT c.parent_id AS p_id, cd.categories_name AS name
FROM " . TABLE_CATEGORIES . " AS c
LEFT JOIN " . TABLE_CATEGORIES_DESCRIPTION . " AS cd
ON c.categories_id = cd.categories_id
AND cd.language_id = {$this->languages_id}
WHERE c.categories_id = $categories_id";
$parent = $db->Execute($sql, false, true, 43200);
if (!$parent->EOF) {
// Recurse if the parent id is not empty or equal the passed categories id
if ($parent->fields['p_id'] != 0 && $parent->fields['p_id'] != $categories_id) {
$this->get_parent_categories_path($path, $parent->fields['p_id'], $cPath);
}
// Add category id to cPath and name to path
$cPath[] = $categories_id;
$path[] = $this->filter($parent->fields['name']) . $this->reg_anchors['cPath'] . implode('_', $cPath);
}
}
protected function is_attribute_string($params)
{
return (preg_match('/products_id=([0-9]+):([a-zA-z0-9]{32})/', $params)) ? true : false;
}
protected function is_product_string($params)
{
return (strpos($params, 'products_id=') !== false);
}
protected function is_cPath_string($params)
{
return (strpos($params, 'cPath=') !== false);
}
/**
* Function to filter a string and remove punctuation and white space.
*
* @param string $string input text
* @return string filtered text
*/
protected function filter($string)
{
$retval = trim(zen_clean_html($string));
// First filter using PCRE Rules
if (is_array($this->filter_pcre)) {
$retval = preg_replace(array_keys($this->filter_pcre), array_values($this->filter_pcre), $retval);
}
// Next run character filters over the string
$pattern = '';
// Remove Special Characters from the strings
switch (USU_REMOVE_CHARS) {
case 'punctuation':
// Remove all punctuation
if (!self::$unicodeEnabled) {
// POSIX named classes are not supported by preg_replace
$pattern = '/[!"#$%&\'()*+,.\/:;<=>?@[\\\]^_`{|}~]/';
} else {
// Each language's punctuation.
$pattern = '/[\p{P}\p{S}]/u';
}
break;
case 'non-alphanumerical':
default:
// Remove all non alphanumeric characters
if (!self::$unicodeEnabled) {
// POSIX named classes are not supported by preg_replace
$pattern = '/[^a-zA-Z0-9\s]/';
} else {