· 9 years ago · May 04, 2017, 02:24 AM
1<?php
2
3 // Search engine friendly links
4 define("CAT_LINK_PART", "categories");
5 define("PRODUCT_LINK_PART", "products");
6 define("BRAND_LINK_PART", "brands");
7
8 /**
9 * Return an already instantiated (singleton) version of a class. If it doesn't exist, will automatically
10 * be created.
11 *
12 * @param string The name of the class to load.
13 * @return object The instantiated version fo the class.
14 */
15 function GetClass($className)
16 {
17 static $classes;
18 if(!isset($classes[$className])) {
19 $classes[$className] = new $className;
20 }
21 $class = &$classes[$className];
22 return $class;
23 }
24
25 /**
26 * Fetch a configuration variable from the store configuration file.
27 *
28 * @param string The name of the variable to fetch.
29 * @return mixed The value of the variable.
30 */
31 function GetConfig($config)
32 {
33 if (array_key_exists($config, $GLOBALS['ISC_CFG'])) {
34 return $GLOBALS['ISC_CFG'][$config];
35 }
36 return '';
37 }
38
39 /**
40 * Load a library class and instantiate it.
41 *
42 * @param string The name of the library class (in the current directory) to load.
43 * @return object The instantiated version of the class.
44 */
45 function GetLibClass($file)
46 {
47 static $libs = array();
48 if (isset($libs[$file])) {
49 return $libs[$file];
50 } else {
51 include_once(dirname(__FILE__).'/'.$file.'.php');
52 $libs[$file] = new $file;
53 return $libs[$file];
54 }
55 }
56
57 /**
58 * Load a library include file from the lib directory.
59 *
60 * @param string The name of the file to include (without the extension)
61 */
62 function GetLib($file)
63 {
64 $FullFile = dirname(__FILE__).'/'.$file.'.php';
65 if (file_exists($FullFile)) {
66 include_once($FullFile);
67 }
68 }
69
70 /**
71 * Convert a text string in to a search engine friendly based URL.
72 *
73 * @param string The text string to convert.
74 * @return string The search engine friendly equivalent.
75 */
76 function MakeURLSafe($val)
77 {
78 $val = str_replace("-", "%2d", $val);
79 $val = str_replace("+", "%2b", $val);
80 $val = str_replace("+", "%2b", $val);
81 $val = str_replace("/", "{47}", $val);
82 $val = urlencode($val);
83 $val = str_replace("+", "-", $val);
84 return $val;
85 }
86
87 /**
88 * Convert an already search engine friendly based string back to the normal text equivalent.
89 *
90 * @param string The search engine friendly version of the string.
91 * @return string The normal textual version of the string.
92 */
93 function MakeURLNormal($val)
94 {
95 $val = str_replace("-", " ", $val);
96 $val = urldecode($val);
97 $val = str_replace("{47}", "/", $val);
98 $val = str_replace("%2d", "-", $val);
99 $val = str_replace("%2b", "+", $val);
100 return $val;
101 }
102
103 /**
104 * Return the current unix timestamp with milliseconds.
105 *
106 * @return float The time since the UNIX epoch in milliseconds.
107 */
108 function microtime_float()
109 {
110 list($usec, $sec) = explode(' ', microtime());
111 return ((float)$usec + (float)$sec);
112 }
113
114 /**
115 * Display the contents of a variable on the page wrapped in <pre> tags for debugging purposes.
116 *
117 * @param mixed The variable to print.
118 * @param boolean Set to true to trim any leading whitespace from the variable.
119 */
120 function Debug($var, $stripLeadingSpaces=false)
121 {
122 echo "\n<pre>\n";
123 if ($stripLeadingSpaces) {
124 $var = preg_replace("%\n[\t\ \n\r]+%", "\n", $var);
125 }
126 if (is_bool($var)) {
127 var_dump($var);
128 } else {
129 print_r($var);
130 }
131 echo "\n</pre>\n";
132 }
133
134 /**
135 * Print a friendly looking backtrace up to the last execution point.
136 *
137 * @param boolean Do we want to stop all execution (die) after outputting the trace?
138 * @param boolean Do we want to return the output instead of echoing it ?
139 */
140 function trace($die=false, $return=true)
141 {
142 $trace = debug_backtrace();
143 $backtrace = "<table style=\"width: 100%; margin: 10px 0; border: 1px solid #aaa; border-collapse: collapse; border-bottom: 0;\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n";
144 $backtrace .= "<thead><tr>\n";
145 $backtrace .= "<th style=\"border-bottom: 1px solid #aaa; background: #ccc; padding: 4px; text-align: left; font-size: 11px;\">File</th>\n";
146 $backtrace .= "<th style=\"border-bottom: 1px solid #aaa; background: #ccc; padding: 4px; text-align: left; font-size: 11px;\">Line</th>\n";
147 $backtrace .= "<th style=\"border-bottom: 1px solid #aaa; background: #ccc; padding: 4px; text-align: left; font-size: 11px;\">Function</th>\n";
148 $backtrace .= "</tr></thead>\n<tbody>\n";
149
150 // Strip off last item (the call to this function)
151 array_shift($trace);
152
153 foreach ($trace as $call) {
154 if (!isset($call['file'])) {
155 $call['file'] = "[PHP]";
156 }
157 if (!isset($call['line'])) {
158 $call['line'] = " ";
159 }
160 if (isset($call['class'])) {
161 $call['function'] = $call['class'].$call['type'].$call['function'];
162 }
163 if(function_exists('textmate_backtrace')) {
164 $call['file'] .= " <a href=\"txmt://open?url=file://".$call['file']."&line=".$call['line']."\">[Open in TextMate]</a>";
165 }
166 $backtrace .= "<tr>\n";
167 $backtrace .= "<td style=\"font-size: 11px; padding: 4px; border-bottom: 1px solid #ccc;\">{$call['file']}</td>\n";
168 $backtrace .= "<td style=\"font-size: 11px; padding: 4px; border-bottom: 1px solid #ccc;\">{$call['line']}</td>\n";
169 $backtrace .= "<td style=\"font-size: 11px; padding: 4px; border-bottom: 1px solid #ccc;\">{$call['function']}</td>\n";
170 $backtrace .= "</tr>\n";
171 }
172 $backtrace .= "</tbody></table>\n";
173 if (!$return) {
174 echo $backtrace;
175 if ($die === true) {
176 die();
177 }
178 } else {
179 return $backtrace;
180 }
181 }
182
183 /**
184 * Return a language variable from the loaded language files.
185 *
186 * If supplying replacements, they'll be swapped out of the language file with the values
187 * supplied. The language function will look for any occurrences of :[array key] in the
188 * language file.
189 *
190 * @param string The name of the language variable to fetch.
191 * @param array Array of optional replacements that should be swapped out in language strings.
192 * @return string The language variable/string.
193 */
194 function GetLang($name, $replacements=array())
195 {
196 if(!isset($GLOBALS['ISC_LANG'][$name])) {
197 return '';
198 }
199
200 $string = $GLOBALS['ISC_LANG'][$name];
201 if(empty($replacements)) {
202 return $string;
203 }
204
205 // Prefix array keys with a colon
206 $actualReplacements = array();
207 foreach($replacements as $k => $v) {
208 $actualReplacements[':'.$k] = $v;
209 }
210 return strtr($string, $actualReplacements);
211 }
212
213 /**
214 * Return a generated a message box (primarily used in the control panel)
215 *
216 * @param string The message to display.
217 * @param int The type of message to display. Can either be one of the MSG_SUCCESS, MSG_INFO, MSG_WARNING, MSG_ERROR constants.
218 * @return string The generated message box.
219 */
220 function MessageBox($desc, $type=MSG_WARNING, $extraClasses = '')
221 {
222 // Return a prepared message table row with the appropriate icon
223 $iconImage = '';
224 $messageBox = '';
225
226 switch ($type) {
227 case MSG_ERROR:
228 $GLOBALS['MsgBox_Type'] = "Error";
229 break;
230 case MSG_SUCCESS:
231 $GLOBALS['MsgBox_Type'] = "Success";
232 break;
233 case MSG_INFO:
234 $GLOBALS['MsgBox_Type'] = "Info";
235 break;
236 case MSG_WARNING:
237 default:
238 $GLOBALS['MsgBox_Type'] = "Warning";
239 }
240
241 $GLOBALS['MsgBox_Message'] = $desc;
242 $GLOBALS['MsgBox_ExtraClasses'] = $extraClasses;
243
244 if(defined('ISC_ADMIN_CP')) {
245 return Interspire_Template::getInstance('admin')->render('Snippets/MessageBox.html');
246 }
247 else {
248 return $GLOBALS['ISC_CLASS_TEMPLATE']->GetSnippet('MessageBox');
249 }
250 }
251
252 /**
253 * Interspire Shopping Cart setcookie() wrapper.
254 *
255 * @param string The name of the cookie to set.
256 * @param string The value of the cookie to set.
257 * @param int The timestamp the cookie should expire. (if there is one)
258 * @param boolean True to set a HttpOnly cookie (Supported by IE, Opera 9, and Konqueror)
259 */
260 function ISC_SetCookie($name, $value = "", $expires = 0, $httpOnly=false)
261 {
262 if (!isset($GLOBALS['CookiePath'])) {
263 $GLOBALS['CookiePath'] = GetConfig('AppPath');
264 }
265
266 // Automatically determine the cookie domain based off the shop path
267 if(!isset($GLOBALS['CookieDomain'])) {
268 $host = "";
269 $useSSL = GetConfig('UseSSL');
270 if ($useSSL == SSL_SUBDOMAIN) {
271 $url = parse_url(GetConfig('SubdomainSSLPath'));
272 if(is_array($url)) {
273 if (isset($url['host'])) {
274 $host = $url['host'];
275 }
276 // strip off the subdomain at the start
277 $pos = isc_strpos($host, ".");
278 $host = isc_substr($host, $pos + 1);
279 }
280 }
281 elseif ($useSSL == SSL_SHARED) {
282 $shost = '';
283 if (function_exists('apache_getenv')) {
284 $shost = @apache_getenv('HTTP_HOST');
285 }
286
287 if (!$shost) {
288 $shost = @$_SERVER['HTTP_HOST'];
289 }
290
291 $sslurl = parse_url(GetConfig('SharedSSLPath'));
292
293 if ($shost == $sslurl['host']) {
294 $host = preg_replace("#^www\.#i", "", $sslurl['host']);
295 }
296 }
297
298 if (!$host) {
299 $url = parse_url(GetConfig('ShopPath'));
300 if(is_array($url)) {
301 // Strip off the www. at the start
302 $host = preg_replace("#^www\.#i", "", $url['host']);
303 }
304 }
305
306 if($host) {
307 $GLOBALS['CookieDomain'] = $host;
308
309 // Prefix with a period so that we're covering both the www and no www
310 if (strpos($GLOBALS['CookieDomain'], '.') !== false && !isIPAddress($GLOBALS['CookieDomain'])) {
311 $GLOBALS['CookieDomain'] = ".".$GLOBALS['CookieDomain'];
312 } else {
313 unset($GLOBALS['CookieDomain']);
314 }
315 }
316 }
317
318 // Set the cookie manually using a HTTP Header
319 $cookie = sprintf("Set-Cookie: %s=%s", $name, urlencode($value));
320
321 // Adding an expiration date
322 if ($expires !== 0) {
323 $cookie .= sprintf("; expires=%s", @gmdate('D, d-M-Y H:i:s \G\M\T', $expires));
324 }
325
326 if (isset($GLOBALS['CookiePath'])) {
327 if (substr($GLOBALS['CookiePath'], -1) != "/") {
328 $GLOBALS['CookiePath'] .= "/";
329 }
330
331 $cookie .= sprintf("; path=%s", trim($GLOBALS['CookiePath']));
332 }
333
334 if (isset($GLOBALS['CookieDomain'])) {
335 $cookie .= sprintf("; domain=%s", $GLOBALS['CookieDomain']);
336 }
337
338 if ($httpOnly == true) {
339 $cookie .= "; HttpOnly";
340 }
341
342 header(trim($cookie), false);
343 }
344
345 /**
346 * Unset a set cookie.
347 *
348 * @param string The name of the cookie to unset.
349 */
350 function ISC_UnsetCookie($name)
351 {
352 ISC_SetCookie($name, "", 1);
353 }
354
355 function ech0($LK)
356 {
357 $v = true;
358 $e = 1;
359
360 $host = '';
361
362 if (function_exists('apache_getenv')) {
363 $host = @apache_getenv('HTTP_HOST');
364 }
365
366 if (!$host) {
367 $host = @$_SERVER['HTTP_HOST'];
368 }
369
370 $colon = strpos($host, ':');
371
372 if ($colon !== false) {
373 $host = substr($host, 0, $colon);
374 }
375
376 if ($host != 'localhost' && $host != '127.0.0.1') {
377 $hashes = array(md5($host));
378
379 if (strtolower(substr($host, 0, 4)) == 'www.') {
380 $hashes[] = md5(substr($host, 4));
381 } else {
382 $hashes[] = md5('www.'. $host);
383 }
384
385 if (!in_array(@$data['hash'], $hashes)) {
386 $GLOBALS['LE'] = "HSer";
387 $GLOBALS['EI'] = $host;
388 $v = false;
389 }
390 }
391
392 $GLOBALS["AppEdition"] = GetLang("Edition" . $e);
393
394 return $v;
395 }
396
397 function mysql_user_row($result)
398 {
399 if (
400 ($result == ISC_SMALLPRINT) ||
401 ($result == ISC_MEDIUMPRINT) ||
402 ($result == ISC_LARGEPRINT) ||
403 ($result == ISC_HUGEPRINT)
404 ) {
405 return true;
406 }
407
408 return false;
409 }
410
411 /**
412 * Checks if the passed string is a valid email address.
413 *
414 * @todo refactor
415 * @param string The email address to check.
416 * @return boolean True if the email is a valid format, false if not.
417 */
418 function is_email_address($email)
419 {
420 // If the email is empty it can't be valid
421 if (empty($email)) {
422 return false;
423 }
424
425 // If the email doesnt have exactle 1 @ it isnt valid
426 if (isc_substr_count($email, '@') != 1) {
427 return false;
428 }
429
430 $matches = array();
431 $local_matches = array();
432 preg_match(':^([^@]+)@([a-zA-Z0-9\-][a-zA-Z0-9\-\.]{0,254})$:', $email, $matches);
433
434 if (count($matches) != 3) {
435 return false;
436 }
437
438 $local = $matches[1];
439 $domain = $matches[2];
440
441 // If the local part has a space but isnt inside quotes its invalid
442 if (isc_strpos($local, ' ') && (isc_substr($local, 0, 1) != '"' || isc_substr($local, -1, 1) != '"')) {
443 return false;
444 }
445
446 // If there are not exactly 0 and 2 quotes
447 if (isc_substr_count($local, '"') != 0 && isc_substr_count($local, '"') != 2) {
448 return false;
449 }
450
451 // if the local part starts or ends with a dot (.)
452 if (isc_substr($local, 0, 1) == '.' || isc_substr($local, -1, 1) == '.') {
453 return false;
454 }
455
456 // If the local string doesnt start and end with quotes
457 if ((isc_strpos($local, '"') || isc_strpos($local, ' ')) && (isc_substr($local, 0, 1) != '"' || isc_substr($local, -1, 1) != '"')) {
458 return false;
459 }
460
461 preg_match(':^([\ \"\w\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~\.]{1,64})$:', $local, $local_matches);
462
463 // Check the domain has at least 1 dot in it
464 if (isc_strpos($domain, '.') === false) {
465 return false;
466 }
467
468 if (!empty($local_matches) ) {
469 return true;
470 } else {
471 return false;
472 }
473 }
474
475 /**
476 * Build the HTML for the thumbnail image of a product.
477 *
478 * @todo refactor
479 * @param string The filename of the thumbnail.
480 * @param string The URL that the thumbnail should link to.
481 * @param string The optional target for the link.
482 * @return string The built HTML for the thumbnail.
483 */
484 function ImageThumb($imageData, $link='', $target='', $class='')
485 {
486 $altText = "";
487
488 if(!is_array($imageData)) {
489 $thumb = $imageData;
490 } else {
491 $image = new ISC_PRODUCT_IMAGE;
492 $image->populateFromDatabaseRow($imageData);
493 $altText = $image->getDescription();
494
495 if(empty($altText) && !empty($imageData['prodname'])) {
496 $altText = $imageData['prodname'];
497 }
498
499 try {
500 $thumb = $image->getResizedUrl(ISC_PRODUCT_IMAGE_SIZE_THUMBNAIL, true);
501 } catch (Exception $exception) {
502 $thumb = '';
503 }
504 unset($image);
505 }
506
507 if(!$thumb) {
508 switch(GetConfig('DefaultProductImage')) {
509 case 'template':
510 $thumb = $GLOBALS['IMG_PATH'].'/ProductDefault.gif';
511 break;
512 case '':
513 $thumb = '';
514 break;
515 default:
516 $thumb = GetConfig('ShopPath').'/'.GetConfig('DefaultProductImage');
517 }
518 }
519 /*
520 else {
521 $thumbPath = APP_ROOT.'/'.GetConfig('ImageDirectory').'/'.$thumb;
522 $thumb = $GLOBALS['ShopPath'].'/'.GetConfig('ImageDirectory').'/'.$thumb;
523 }
524 */
525 if(!$thumb) {
526 return '';
527 }
528
529 if($target != '') {
530 $target = 'target="' . isc_html_escape($target) . '"';
531 }
532
533 if($class != '') {
534 $class = 'class="' . isc_html_escape($class) . '"';
535 }
536
537 $imageThumb = '';
538 if($link != '') {
539 $imageThumb .= '<a href="' . isc_html_escape($link) . '" ' . $target . ' ' . $class . '>';
540 }
541
542 $imageSize = @getimagesize($thumbPath);
543
544 if(is_array($imageSize) && !empty($imageSize)) {
545 $imageThumb .= '<img src="' . isc_html_escape($thumb) . '" alt="' . isc_html_escape($altText) . '" ' . $imageSize[3] . ' />';
546 }else{
547 $imageThumb .= '<img src="' . isc_html_escape($thumb) . '" alt="' . isc_html_escape($altText) . '" />';
548 }
549
550 if($link != '') {
551 $imageThumb .= '</a>';
552 }
553
554 return $imageThumb;
555 }
556
557 /**
558 * Generate the link to a product.
559 *
560 * @param string The name of the product to generate the link to.
561 * @return string The generated link to the product.
562 */
563 function ProdLink($prod)
564 {
565 if ($GLOBALS['EnableSEOUrls'] == 1) {
566 return sprintf("%s/%s/%s.html", GetConfig('ShopPathNormal'), PRODUCT_LINK_PART, MakeURLSafe($prod));
567 } else {
568 return sprintf("%s/products.php?product=%s", GetConfig('ShopPathNormal'), MakeURLSafe($prod));
569 }
570 }
571
572 /**
573 * Generate the link to a brand name.
574 *
575 * @param string The name of the brand (if null, the link to all brands is generated)
576 * @param array An optional array of query string arguments that need to be present.
577 * @param boolean Set to false to not separate query string arguments with & but use & instead. Useful if generating a link to use for a redirect.
578 * @return string The generated link to the brand.
579 */
580 function BrandLink($brand=null, $queryString=array(), $entityAmpersands=true)
581 {
582 // If we don't have a brand then we're just generating the link to the "all brands" page
583 if($brand === null) {
584 if ($GLOBALS['EnableSEOUrls'] == 1) {
585 $link = sprintf("%s/%s/", $GLOBALS['ShopPathNormal'], BRAND_LINK_PART, MakeURLSafe($brand));
586 } else {
587 $link = sprintf("%s/brands.php", $GLOBALS['ShopPathNormal'], MakeURLSafe($brand));
588 }
589 }
590 else {
591 if ($GLOBALS['EnableSEOUrls'] == 1) {
592 $link = sprintf("%s/%s/%s.html", $GLOBALS['ShopPathNormal'], BRAND_LINK_PART, MakeURLSafe($brand));
593 } else {
594 $link = sprintf("%s/brands.php?brand=%s", $GLOBALS['ShopPathNormal'], MakeURLSafe($brand));
595 }
596 }
597
598 if($entityAmpersands) {
599 $ampersand = '&';
600 }
601 else {
602 $ampersand = '&';
603 }
604 if(is_array($queryString) && !empty($queryString)) {
605 if ($GLOBALS['EnableSEOUrls'] == 1) {
606 $link .= '?';
607 }
608 else {
609 $link .= $ampersand;
610 }
611 $qString = array();
612 foreach($queryString as $k => $v) {
613 $qString[] = $k.'='.urlencode($v);
614 }
615 $link .= implode($ampersand, $qString);
616 }
617
618 return $link;
619 }
620
621 /**
622 * Generate a link to a specific vendor.
623 *
624 * @param array Array of details about the vendor to link to.
625 * @param array An optional array of query string arguments that need to be present.
626 * @return string The generated link to the vendor.
627 */
628 function VendorLink($vendor="", $queryString=array())
629 {
630 $link = '';
631
632 if(!is_array($vendor)) {
633 if($GLOBALS['EnableSEOUrls'] == 1) {
634 $link = GetConfig('ShopPathNormal').'/vendors/';
635 }
636 else {
637 $link = GetConfig('ShopPathNormal').'/vendors.php';
638 }
639 }
640 else if($GLOBALS['EnableSEOUrls'] == 1 && $vendor['vendorfriendlyname']) {
641 $link = GetConfig('ShopPathNormal').'/vendors/'.$vendor['vendorfriendlyname'];
642 }
643 else {
644 $link = GetConfig('ShopPathNormal').'/vendors.php?vendorid='.(int)$vendor['vendorid'];
645 }
646
647 if(is_array($queryString) && !empty($queryString)) {
648 if ($GLOBALS['EnableSEOUrls'] == 1) {
649 $link .= '?';
650 }
651 else {
652 $link .= '&';
653 }
654 $qString = array();
655 foreach($queryString as $k => $v) {
656 $qString[] = $k.'='.urlencode($v);
657 }
658 $link .= implode('&', $qString);
659 }
660
661 return $link;
662 }
663
664 /**
665 * Generate a link to browse the products belonging to a specific vendor.
666 *
667 * @param array Array of details about the vendor to link to.
668 * @param array An optional array of query string arguments that need to be present.
669 * @return string The generated link to the vendor.
670 */
671 function VendorProductsLink($vendor, $queryString=array())
672 {
673 $link = '';
674 if($GLOBALS['EnableSEOUrls'] == 1 && $vendor['vendorfriendlyname']) {
675 $link = GetConfig('ShopPathNormal').'/vendors/'.$vendor['vendorfriendlyname'].'/products/';
676 }
677 else {
678 $link = GetConfig('ShopPathNormal').'/vendors.php?vendorid='.(int)$vendor['vendorid'].'&action=products';
679 }
680
681 if(is_array($queryString) && !empty($queryString)) {
682 if (strpos($link, '?') === false) {
683 $link .= '?';
684 }
685 else {
686 $link .= '&';
687 }
688 $qString = array();
689 foreach($queryString as $k => $v) {
690 $qString[] = $k.'='.urlencode($v);
691 }
692 $link .= implode('&', $qString);
693 }
694
695 return $link;
696 }
697
698 /**
699 * Generate the link to a particular tag or a list of tags.
700 *
701 * @param string The friendly name of the tag (if we have one)
702 * @param string the ID of the tag (if we have one)
703 * @param array An optional array of query string arguments that need to be present.
704 * @return string The generated link to the tag.
705 */
706 function TagLink($friendlyName="", $tagId=0, $queryString=array())
707 {
708 $link = '';
709
710 if($GLOBALS['EnableSEOUrls'] == 1 && $friendlyName) {
711 $link = GetConfig('ShopPathNormal').'/tags/'.$friendlyName;
712 }
713 else if($tagId) {
714 $link = GetConfig('ShopPathNormal').'/tags.php?tagid='.(int)$tagId;
715 }
716 else {
717 if($GLOBALS['EnableSEOUrls'] == 1) {
718 $link = GetConfig('ShopPathNormal').'/tags/';
719 }
720 else {
721 $link = GetConfig('ShopPathNormal').'/tags.php';
722 }
723 }
724
725 if(is_array($queryString) && !empty($queryString)) {
726 if ($GLOBALS['EnableSEOUrls'] == 1) {
727 $link .= '?';
728 }
729 else {
730 $link .= '&';
731 }
732 $qString = array();
733 foreach($queryString as $k => $v) {
734 $qString[] = $k.'='.urlencode($v);
735 }
736 $link .= implode('&', $qString);
737 }
738
739 return $link;
740 }
741
742 /**
743 * Generate the link to the initial sitemap page
744 *
745 */
746 function SitemapLink()
747 {
748 $url = GetConfig('ShopPathNormal') . '/';
749
750 if ($GLOBALS['EnableSEOUrls'] == 1) {
751 $url .= 'sitemap/';
752
753 } else {
754 $url .= 'sitemap.php';
755 }
756
757 return $url;
758 }
759
760 /**
761 * Generate the link to a category.
762 *
763 * @param int The ID of the category to generate the link to.
764 * @param string The name of the category to generate the link to.
765 * @param boolean Set to true to base this link as a root category link.
766 * @param array An optional array of query string arguments that need to be present.
767 * @return string The generated link to the category.
768 */
769 function CatLink($CategoryId, $CategoryName, $parent=false, $queryString=array())
770 {
771 // Workout the category link, starting from the bottom and working up
772 $link = "";
773 $arrCats = array();
774
775 if ($parent === true) {
776 $parent = 0;
777 $arrCats[] = $CategoryName;
778 } else {
779 static $categoryCache;
780
781 if(!is_array($categoryCache)) {
782 $categoryCache = array();
783 $query = "SELECT catname, catparentid, categoryid FROM [|PREFIX|]categories order by catsort desc, catname asc";
784 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
785 while ($row = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
786 $categoryCache[$row['categoryid']] = $row;
787 }
788 }
789 if(empty($categoryCache)) {
790 return '';
791 }
792 if (isset($categoryCache[$CategoryId])) {
793 $parent = $categoryCache[$CategoryId]['catparentid'];
794
795 if ($parent == 0) {
796 $arrCats[] = $categoryCache[$CategoryId]['catname'];
797 } else {
798 // Add the first category
799 $arrCats[] = $CategoryName;
800 $lastParent=0;
801 while ($parent != 0 && $parent != $lastParent) {
802 $arrCats[] = $categoryCache[$parent]['catname'];
803 $lastParent = $categoryCache[$parent]['categoryid'];
804 $parent = (int)$categoryCache[$parent]['catparentid'];
805 }
806 }
807 }
808 }
809
810 $arrCats = array_reverse($arrCats);
811
812 for ($i = 0; $i < count($arrCats); $i++) {
813 $link .= sprintf("%s/", MakeURLSafe($arrCats[$i]));
814 }
815
816 // Now we reverse the array and concatenate the categories to form the link
817 if ($GLOBALS['EnableSEOUrls'] == 1) {
818 $link = sprintf("%s/%s/%s", $GLOBALS['ShopPathNormal'], CAT_LINK_PART, $link);
819 } else {
820 $link = trim($link, "/");
821 $link = sprintf("%s/categories.php?category=%s", $GLOBALS['ShopPathNormal'], $link);
822 }
823
824 if(is_array($queryString) && !empty($queryString)) {
825 if ($GLOBALS['EnableSEOUrls'] == 1) {
826 $link .= '?';
827 }
828 else {
829 $link .= '&';
830 }
831 $link .= http_build_query($queryString);
832 }
833
834 return $link;
835 }
836
837 /**
838 * Generate the link to a search results page.
839 *
840 * @param array An array of search terms/arguments
841 * @param int The page number we're currently on.
842 * @param string Set to true to prefix with the search page URL.
843 * @return string The search results page URL.
844 */
845 function SearchLink($Query, $Page, $AppendSearchURL=true)
846 {
847 $search_link = '';
848 foreach ($Query as $field => $term) {
849 if ($term && is_array($term)) {
850 $terms = $term;
851 $term = '';
852 foreach ($terms as $v) {
853 $search_link .= sprintf("&%s[]=%s", $field, urlencode($v));
854 }
855 } else if ($term) {
856 $search_link .= sprintf("&%s=%s", $field, urlencode($term));
857 }
858 }
859 // Strip initial & off the search URL
860 if ($AppendSearchURL !== false) {
861 $search_link = isc_substr($search_link, 1);
862 $search_link = sprintf("%s/search.php?%s&page=%d", $GLOBALS['ShopPathNormal'], $search_link, $Page);
863 }
864 return $search_link;
865 }
866
867 function fix_url($link)
868 {
869 if (isset($GLOBALS['KM']) || isset($_GET['bk'])) {
870 if(isset($GLOBALS['KM'])) {
871 $m = $GLOBALS['KM'];
872 }
873 $GLOBALS['Message'] = MessageBox($m, MSG_ERROR);
874 }
875 }
876
877 // Return a shopping cart link in standard format
878 function CartLink($prodid=0)
879 {
880 if($prodid == 0) {
881 return sprintf("%s/cart.php", $GLOBALS['ShopPathNormal']);
882 }
883 else {
884 return sprintf("%s/cart.php?action=add&product_id=%d", $GLOBALS['ShopPathNormal'], $prodid);
885 }
886 }
887
888 // Return a blog link in standard format
889 function BlogLink($blogid, $blogtitle)
890 {
891 if ($GLOBALS['EnableSEOUrls'] == 1) {
892 return sprintf("%s/news/%d/%s.html", $GLOBALS['ShopPathNormal'], $blogid, MakeURLSafe($blogtitle));
893 } else {
894 return sprintf("%s/news.php?newsid=%s", $GLOBALS['ShopPathNormal'], $blogid);
895 }
896 }
897
898 // Return a page link in standard format
899 function PageLink($pageid, $pagetitle, $vendor=array())
900 {
901 $link = GetConfig('ShopPathNormal').'/';
902 if(!empty($vendor)) {
903 if($GLOBALS['EnableSEOUrls'] == 1 && $vendor['vendorfriendlyname']) {
904 $link .= 'vendors/'.$vendor['vendorfriendlyname'].'/'.MakeURLSafe($pagetitle).'.html';
905 }
906 else {
907 $link .= 'vendors.php?vendorid='.(int)$vendor['vendorid'].'&pageid='.(int)$pageid;
908 }
909 }
910 else {
911 if ($GLOBALS['EnableSEOUrls'] == 1) {
912 $link .= 'pages/'.MakeURLSafe($pagetitle).'.html';
913 }
914 else {
915 $link .= 'pages.php?pageid='.(int)$pageid;
916 }
917 }
918 return $link;
919 }
920
921 /**
922 * Get a link to the compare products page
923 *
924 * @param array The array of ids to compare
925 *
926 * @return string The html href
927 */
928 function CompareLink($prodids=array())
929 {
930 $link = '';
931
932 if ($GLOBALS['EnableSEOUrls'] == 1) {
933 $link = $GLOBALS['ShopPathNormal'].'/compare/';
934 } else {
935 $link = $GLOBALS['ShopPathNormal'].'/compare.php?';
936 }
937
938 // If no ids have been passed (e.g. for a form submit), then return
939 // the base compare url
940 if (empty($prodids)) {
941 return $link;
942 }
943
944 // Make sure each of the product ids is an integer
945 foreach ($prodids as $k => $v) {
946 if (!is_numeric($v) || $v < 0) {
947 unset($prodids[$k]);
948 }
949 }
950
951 $link .= implode('/', $prodids);
952
953 return $link;
954 }
955
956 // Return the extension of a file name
957 // @todo refactor
958 function GetFileExtension($FileName)
959 {
960 $data = explode(".", $FileName);
961 return $data[count($data)-1];
962 }
963
964 /**
965 * Convert a weight between the specified units.
966 *
967 * @param string The weight to convert.
968 * @param string The unit to convert the weight to.
969 * @param string Optionally, the unit to convert the weight from. If not specified, assumes the store default.
970 * @return string The converted weight.
971 */
972 function ConvertWeight($weight, $toUnit, $fromUnit=null)
973 {
974 if(is_null($fromUnit)) {
975 $fromUnit = GetConfig('WeightMeasurement');
976 }
977 $fromUnit = strtolower($fromUnit);
978 $toUnit = strtolower($toUnit);
979
980 $units = array(
981 'pounds' => array('lbs', 'pounds', 'lb'),
982 'kg' => array('kg', 'kgs', 'kilos', 'kilograms'),
983 'gram' => array('g', 'grams'),
984 'ounces' => array('ounces', 'oz'),
985 );
986
987 foreach ($units as $unit) {
988 if(in_array($fromUnit, $unit) && in_array($toUnit, $unit)) {
989 return $weight;
990 }
991 }
992
993 // First, let's convert back to a standardized measurement. We'll use grams.
994 switch(strtolower($fromUnit)) {
995 case 'lbs':
996 case 'pounds':
997 case 'lb':
998 $weight *= 453.59237;
999 break;
1000 case 'ounces':
1001 case 'oz':
1002 $weight *= 28.3495231;
1003 break;
1004 case 'kg':
1005 case 'kgs':
1006 case 'kilos':
1007 case 'kilograms':
1008 $weight *= 1000;
1009 break;
1010 case 'g':
1011 case 'grams':
1012 break;
1013 case 'tonnes':
1014 $weight *= 1000000;
1015 break;
1016 }
1017
1018 // Now we're in a standardized measurement, start converting from grams to the unit we need
1019 switch(strtolower($toUnit)) {
1020 case 'lbs':
1021 case 'pounds':
1022 case 'lb':
1023 $weight *= 0.00220462262;
1024 break;
1025 case 'ounces':
1026 case 'oz':
1027 $weight *= 0.0352739619;
1028 break;
1029 case 'kg':
1030 case 'kgs':
1031 case 'kilos':
1032 case 'kilograms':
1033 $weight *= 0.001;
1034 break;
1035 case 'g':
1036 case 'grams':
1037 break;
1038 case 'tonnes':
1039 $weight *= 0.000001;
1040 break;
1041 }
1042 return $weight;
1043 }
1044
1045 /**
1046 * Convert a length between the specified units.
1047 *
1048 * @param string The length to convert.
1049 * @param string The unit to convert the length to.
1050 * @param string Optionally, the unit to convert the length from. If not specified, assumes the store default.
1051 * @return string The converted length.
1052 */
1053 function ConvertLength($length, $toUnit, $fromUnit=null)
1054 {
1055 if(is_null($fromUnit)) {
1056 $fromUnit = GetConfig('LengthMeasurement');
1057 }
1058
1059 // First, let's convert back to a standardized measurement. We'll use millimetres
1060 switch(strtolower($fromUnit)) {
1061 case 'inches':
1062 case 'in':
1063 {
1064 $length *= 25.4;
1065 break;
1066 }
1067 case 'centimeters':
1068 case 'centimetres':
1069 case 'cm':
1070 {
1071 $length *= 10;
1072 break;
1073 }
1074 case 'metres':
1075 case 'meters':
1076 case 'm':
1077 {
1078 $length *= 10;
1079 break;
1080 }
1081 case 'millimetres':
1082 case 'millimeters':
1083 case 'mm':
1084 {
1085 break;
1086 }
1087 }
1088
1089 // Now we're in a standardized measurement, start converting from grams to the unit we need
1090 switch(strtolower($toUnit)) {
1091 case 'inches':
1092 case 'in':
1093 {
1094 $length *= 0.0393700787;
1095 break;
1096 }
1097
1098 case 'centimeters':
1099 case 'centimetres':
1100 case 'cm':
1101 {
1102 $length *= 0.1;
1103 break;
1104 }
1105 case 'metres':
1106 case 'meters':
1107 case 'm':
1108 {
1109 $length *= 0.001;
1110 break;
1111 }
1112 case 'mm':
1113 case 'millimetres':
1114 case 'millimeters':
1115 {
1116 break;
1117 }
1118 }
1119
1120 return $length;
1121 }
1122
1123 /**
1124 * Calculate the weight adjustment for a variation of a product.
1125 *
1126 * @param string The base weight of the product.
1127 * @param string The type of adjustment to be performed (empty, add, subtract, fixed)
1128 * @param string The value to be adjusted by
1129 * @return string The adjusted value
1130 */
1131 function CalcProductVariationWeight($baseWeight, $type, $difference)
1132 {
1133 switch($type) {
1134 case "fixed":
1135 return $difference;
1136 break;
1137 case "add":
1138 return $baseWeight + $difference;
1139 break;
1140 case "subtract":
1141 $adjustedWeight = $baseWeight - $difference;
1142 if($adjustedWeight <= 0) {
1143 $adjustedWeight = 0;
1144 }
1145 return $adjustedWeight;
1146 break;
1147 default:
1148 return $baseWeight;
1149 }
1150 }
1151
1152 function mhash1($token = 5)
1153 {
1154 $a = spr1ntf(GetConfig('serverStamp'));
1155 return $a['products'];
1156 }
1157
1158 /**
1159 * Fetch the name of a product from the passed product ID.
1160 *
1161 * @param int The ID of the product.
1162 * @return string The name of the product.
1163 */
1164 function GetProdNameById($prodid)
1165 {
1166 $query = "
1167 SELECT prodname
1168 FROM [|PREFIX|]products
1169 WHERE productid='".(int)$prodid."'
1170 ";
1171 return $GLOBALS['ISC_CLASS_DB']->FetchOne($query);
1172 }
1173
1174 /**
1175 * Check if the passed string is indeed valid ID for an item.
1176 *
1177 * @param string The string to check that's a valid ID.
1178 * @return boolean True if valid, false if not.
1179 */
1180 function isId($id)
1181 {
1182 // If the type casted version fo the integer is the same as what's passed
1183 // and the integer is > 0, then it's a valid ID.
1184 if(isc_is_int($id) && $id > 0) {
1185 return true;
1186 }
1187 else {
1188 return false;
1189 }
1190 }
1191
1192 /**
1193 * Check if passed string is a price (decimal) format
1194 *
1195 * @param string The The string to check that's a valid price.
1196 * @return boolean True if valid, false if not
1197 */
1198 function IsPrice($price)
1199 {
1200 // Format the price as we'll be storing it internally
1201 $price = DefaultPriceFormat($price);
1202
1203 // If the price contains anything other than [0-9.] then it's invalid
1204 if(preg_match('#[^0-9\.]#i', $price)) {
1205 return false;
1206 }
1207
1208 return true;
1209 }
1210
1211 function gzte11($str)
1212 {
1213 $dbDump = mysql_dump();
1214 $b = 0;
1215
1216 switch ($dbDump) {
1217 case ISC_HUGEPRINT:
1218 $b = ISC_HUGEPRINT | ISC_LARGEPRINT | ISC_MEDIUMPRINT | ISC_SMALLPRINT;
1219 break;
1220 case ISC_LARGEPRINT:
1221 $b = ISC_LARGEPRINT | ISC_MEDIUMPRINT | ISC_SMALLPRINT;
1222 break;
1223 case ISC_MEDIUMPRINT:
1224 $b = ISC_MEDIUMPRINT | ISC_SMALLPRINT;
1225 break;
1226 case ISC_SMALLPRINT:
1227 $b = ISC_SMALLPRINT;
1228 break;
1229 }
1230
1231 if (($str & $b) == $str) {
1232 return true;
1233 }
1234 else {
1235 return false;
1236 }
1237 }
1238
1239 function FormatWeight($weight, $includemeasure=false)
1240 {
1241 $num = number_format($weight, GetConfig('DimensionsDecimalPlaces'), GetConfig('DimensionsDecimalToken'), GetConfig('DimensionsThousandsToken'));
1242
1243 if ($includemeasure) {
1244 $num .= " " . GetConfig('WeightMeasurement');
1245 }
1246
1247 return $num;
1248 }
1249
1250 /**
1251 * Format a number using the configured decimal and thousand tokens to an optional number of decimal places
1252 *
1253 * @param mixed The number to format
1254 * @param int The number of decimal places to format the number to. If -1 is specified (default) then the number of decimal places in the original number will be used.
1255 * @return string The formatted number
1256 */
1257 function FormatNumber($number, $decimalPlaces = -1)
1258 {
1259 // drop off any excess zeroes in the fractional component
1260 $number /= 1;
1261
1262 if ($decimalPlaces == -1) {
1263 if (strrchr($number, '.')) {
1264 $decimalPlaces = strlen(strrchr($number, '.')) - 1;
1265 }
1266 }
1267
1268 if ($decimalPlaces < 0) {
1269 $decimalPlaces = 0;
1270 }
1271
1272 $number = number_format($number, $decimalPlaces, GetConfig('DimensionsDecimalToken'), GetConfig('DimensionsThousandsToken'));
1273
1274 return $number;
1275 }
1276
1277 function SetPGQVariablesManually()
1278 {
1279 // Retrieve the query string variables. Can't use the $_GET array
1280 // because of SEO friendly links in the URL
1281
1282 if(!isset($_SERVER['REQUEST_URI'])) {
1283 return;
1284 }
1285
1286 $uri = $_SERVER['REQUEST_URI'];
1287 $tempRay = explode("?", $uri);
1288 $_SERVER['REQUEST_URI'] = $tempRay[0];
1289
1290 if (is_numeric(isc_strpos($uri,"?"))) {
1291 $tempRay2 = explode("&",$tempRay[1]);
1292 foreach ($tempRay2 as $key => $value) {
1293 if(!$key) {
1294 continue;
1295 }
1296 $tempRay3 = array();
1297 $tempRay3 = explode("=",$value);
1298 if(!isset($tempRay3[1])) {
1299 $tempRay3[1] = '';
1300 }
1301 $_GET[$tempRay3[0]] = urldecode($tempRay3[1]);
1302 $_REQUEST[$tempRay3[0]] = urldecode($tempRay3[1]);
1303 }
1304 }
1305 }
1306
1307 /**
1308 * Check if PHPs GD module is enabled and PNG images can be created.
1309 *
1310 * @return boolean True if GD is enabled, false if not.
1311 */
1312 function GDEnabledPNG()
1313 {
1314 if (function_exists('imageCreateFromPNG')) {
1315 return true;
1316 }
1317 return false;
1318 }
1319
1320 function CleanPath($path)
1321 {
1322 // init
1323 $result = array();
1324
1325 if (IsWindowsServer()) {
1326 // if its windows we need to change the path a bit!
1327 $path = str_replace("\\","/",$path);
1328 $driveletter = isc_substr($path,0,2);
1329 $path = isc_substr($path,2);
1330 }
1331
1332 $pathA = explode('/', $path);
1333
1334 if (!$pathA[0]) {
1335 $result[] = '';
1336 }
1337
1338 foreach ($pathA as $key => $dir) {
1339 if ($dir == '..') {
1340 if (end($result) == '..') {
1341 $result[] = '..';
1342 } else if (!array_pop($result)) {
1343 $result[] = '..';
1344 }
1345 } else if ($dir && $dir != '.') {
1346 $result[] = $dir;
1347 }
1348 }
1349
1350 if (!end($pathA)) {
1351 $result[] = '';
1352 }
1353
1354 $path = implode('/', $result);
1355
1356 if (IsWindowsServer()) {
1357 // if its windows we need to add the drive letter back on
1358 $path = $driveletter . $path;
1359 }
1360 if (isc_substr($path,isc_strlen($path)-1,1) == '/' && strlen($path) > 1) {
1361 $path = isc_substr($path,0,isc_strlen($path)-1);
1362 }
1363 return $path;
1364 }
1365
1366 function cache_time($Page)
1367 {
1368 // Check the cache time on a page. If it's expired then return a new cache time
1369 if($Page == '') {
1370 return 0;
1371 }
1372 else {
1373 return rand(10, 100);
1374 }
1375 }
1376
1377 /**
1378 * Is the current server a Microsoft Windows based server?
1379 *
1380 * @return boolean True if Microsoft Windows, false if not.
1381 */
1382 function IsWindowsServer()
1383 {
1384 if(isc_substr(isc_strtolower(PHP_OS), 0, 3) == 'win') {
1385 return true;
1386 }
1387 else {
1388 return false;
1389 }
1390 }
1391
1392 function hex2rgb($hex)
1393 {
1394 // If the first char is a # strip it off
1395 if (isc_substr($hex, 0, 1) == '#') {
1396 $hex = isc_substr($hex, 1);
1397 }
1398
1399 // If the string isnt the right length return false
1400 if (isc_strlen($hex) != 6) {
1401 return false;
1402 }
1403
1404 $vals = array();
1405 $vals[] = hexdec(isc_substr($hex, 0, 2));
1406 $vals[] = hexdec(isc_substr($hex, 2, 2));
1407 $vals[] = hexdec(isc_substr($hex, 4, 2));
1408 $vals['r'] = $vals[0];
1409 $vals['g'] = $vals[1];
1410 $vals['b'] = $vals[2];
1411 return $vals;
1412 }
1413
1414 function isnumeric($num)
1415 {
1416 $a = spr1ntf(GetConfig('serverStamp'));
1417 return $a['users'];
1418 }
1419
1420 function CEpoch($Val)
1421 {
1422 // Converts a time() value to a relative date value
1423 $stamp = time() - (time() - $Val);
1424 return isc_date(GetConfig('ExportDateFormat'), $stamp);
1425 }
1426
1427 function CDate($Val)
1428 {
1429 return isc_date(GetConfig('DisplayDateFormat'), $Val);
1430 }
1431
1432 function CStamp($Val)
1433 {
1434 return isc_date(GetConfig('DisplayDateFormat') ." h:i A", $Val);
1435 }
1436
1437 function CFloat($Val)
1438 {
1439 $Val = str_replace(GetConfig('CurrencyToken'), "", $Val);
1440 $Val = str_replace(GetConfig('ThousandsToken'), "", $Val);
1441 settype($Val, "double");
1442 $Val = number_format($Val, GetConfig('DecimalPlaces'), GetConfig('DecimalToken'), "");
1443 return $Val;
1444 }
1445
1446 function CNumeric($Val)
1447 {
1448 $Val = preg_replace("#[^0-9\.\,]+#i", "", $Val);
1449 $Val = str_replace(GetConfig('ThousandsToken'), "", $Val);
1450 $Val = str_replace(GetConfig('DecimalToken'), ".", $Val);
1451 $Val = number_format($Val, GetConfig('DecimalPlaces'), ".", "");
1452 return $Val;
1453 }
1454
1455 function CDbl($Val)
1456 {
1457 $Val = str_replace(GetConfig('CurrencyToken'), "", $Val);
1458 $Val = str_replace(GetConfig('ThousandsToken'), "", $Val);
1459 $Val = number_format($Val, GetConfig('DecimalPlaces'), GetConfig('DecimalToken'), GetConfig('ThousandsToken'));
1460 settype($Val, "double");
1461 return $Val;
1462 }
1463
1464 /**
1465 * Convert a localized weight or dimension back to the standardized western format.
1466 *
1467 * @param string The weight to convert.
1468 * @return string The converted weight.
1469 */
1470 function DefaultDimensionFormat($dimension)
1471 {
1472 $dimension = preg_replace("#[^0-9\.\,]+#i", "", $dimension);
1473 $dimension = str_replace(GetConfig('DimensionsThousandsToken'), "", $dimension);
1474
1475 if(GetConfig('DimensionsDecimalToken') != '.') {
1476 $dimension = str_replace(GetConfig('DimensionsDecimalToken'), ".", $dimension);
1477 }
1478
1479 $dimension = number_format(doubleval($dimension), GetConfig('DimensionsDecimalPlaces'), ".", "");
1480
1481 return $dimension;
1482 }
1483
1484 // @todo refactor
1485 function GenRandFileName($FileName, $Append="")
1486 {
1487 // Generates a random filename to store images and product downloads.
1488 // Adds 5 random characters to the end of the file name.
1489 // Gets the original file extension from $FileName
1490
1491 // Have the random characters already been added to the filename?
1492 if (!is_numeric(isc_strpos($FileName, "__"))) {
1493 $fileName = "";
1494 $tmp = explode(".", $FileName);
1495 $ext = isc_strtolower($tmp[count($tmp)-1]);
1496 $FileName = isc_strtolower($FileName);
1497 $FileName = str_replace("." . $ext, "", $FileName);
1498
1499 for ($i = 0; $i < 5; $i++) {
1500 $fileName .= rand(0,9);
1501 }
1502
1503 return sprintf("%s__%s.%s", $FileName,$fileName, $ext);
1504 } else {
1505 $tmp = explode(".", $FileName);
1506 $ext = isc_strtolower($tmp[count($tmp)-1]);
1507 $FileName = isc_strtolower($FileName);
1508 if ($Append != '') {
1509 $FileName = str_replace("." . $ext, sprintf("_%s", $Append) . "." . $ext, $FileName);
1510 }
1511 return $FileName;
1512 }
1513 }
1514
1515 function ProductExists($ProdId)
1516 {
1517 if (!isId($ProdId)) {
1518 return false;
1519 }
1520
1521 // Check if a record is found for a product and return true/false
1522 $query = sprintf("select 'exists' from [|PREFIX|]products where productid='%d'", $GLOBALS['ISC_CLASS_DB']->Quote($ProdId));
1523 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1524 $row = $GLOBALS['ISC_CLASS_DB']->Fetch($result);
1525
1526 if ($row !== false) {
1527 return true;
1528 } else {
1529 return false;
1530 }
1531 }
1532
1533 function ReviewExists($ReviewId)
1534 {
1535 // Check if a record is found for a product and return true/false
1536 $query = sprintf("select reviewid from [|PREFIX|]reviews where reviewid='%d'", $GLOBALS['ISC_CLASS_DB']->Quote($ReviewId));
1537 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1538 $row = $GLOBALS['ISC_CLASS_DB']->Fetch($result);
1539
1540 if ($row !== false) {
1541 return true;
1542 } else {
1543 return false;
1544 }
1545 }
1546
1547 function ConvertDateToTime($Stamp)
1548 {
1549 $vals = explode("/", $Stamp);
1550 return isc_gmmktime(0, 0, 0, $vals[0], $vals[1], $vals[2]);
1551 }
1552
1553
1554 function GetStatesByCountryNameAsOptions($CountryName, &$NumberOfStates, $SelectedStateName="")
1555 {
1556 // Return a list of states as a JavaScript array
1557 $output = "";
1558 $query = sprintf("select stateid, statename from [|PREFIX|]country_states where statecountry=(select countryid from [|PREFIX|]countries where countryname='%s')", $GLOBALS['ISC_CLASS_DB']->Quote($CountryName));
1559 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1560
1561 $NumberOfStates = $GLOBALS['ISC_CLASS_DB']->CountResult($result);
1562
1563 while ($row = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
1564 if ($row['statename'] == $SelectedStateName) {
1565 $sel = 'selected="selected"';
1566 } else {
1567 $sel = "";
1568 }
1569
1570 $output .= sprintf("<option %s value='%d'>%s</option>", $sel, $row['stateid'], $row['statename']);
1571 }
1572
1573 return $output;
1574 }
1575
1576 /**
1577 * Check if a product can be added to the customer's cart or not.
1578 *
1579 * @param array An array of information about the product.
1580 * @return boolean True if the product can be sold. False if not.
1581 */
1582 function CanAddToCart($product)
1583 {
1584 // If pricing is hidden, obviously it can't be added
1585 if(!GetConfig('ShowProductPrice') || $product['prodhideprice'] == 1) {
1586 return false;
1587 }
1588
1589 // If this item is sold out, then obviously it can't be added
1590 else if($product['prodinvtrack'] == 1 && $product['prodcurrentinv'] <= 0) {
1591 return false;
1592 }
1593
1594 // If purchasing is disabled, then oviously it cannot be added either
1595 else if(!$product['prodallowpurchases'] || !GetConfig('AllowPurchasing')) {
1596 return false;
1597 }
1598
1599 // Otherwise, the product can be added to the cart
1600 return true;
1601 }
1602
1603 /**
1604 * Check if a product can be sold or not based on visibility, current stock level etc
1605 */
1606 function IsProductSaleable($product)
1607 {
1608 if(!$product['prodallowpurchases']) {
1609 return false;
1610 }
1611
1612 // Inventory tracking at product level
1613 if ($product['prodinvtrack'] == 1) {
1614 if ($product['prodcurrentinv'] <= 0) {
1615 return false;
1616 } else {
1617 return true;
1618 }
1619 }
1620 // Inventory tracking at product option level
1621 if ($product['prodinvtrack'] == 2) {
1622 $inventory = array();
1623
1624 // What we do here is fetch a list of product options and return an array containing each option & its availablility
1625 $query = sprintf("select * from [|PREFIX|]product_variation_combinations where vcproductid='%d'", $GLOBALS['ISC_CLASS_DB']->Quote($product['productid']));
1626 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1627 while ($row = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
1628 if ($row['vcstock'] <= 0) {
1629 $inventory[$row['combinationid']] = false;
1630 } else {
1631 $inventory[$row['combinationid']] = true;
1632 }
1633 }
1634 return $inventory;
1635 }
1636 // No inventory tracking
1637 else {
1638 return true;
1639 }
1640 }
1641
1642 function CustomerExists($CustId)
1643 {
1644 if (!isId($CustId)) {
1645 return false;
1646 }
1647
1648 // Check if a record is found for a customer and return true/false
1649 $query = sprintf("select customerid from [|PREFIX|]customers where customerid='%d'", $GLOBALS['ISC_CLASS_DB']->Quote($CustId));
1650 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1651 $row = $GLOBALS['ISC_CLASS_DB']->Fetch($result);
1652
1653 if ($row !== false) {
1654 return true;
1655 } else {
1656 return false;
1657 }
1658 }
1659
1660 function CustomerGroupExists($CustGroupId)
1661 {
1662 if (!isId($CustGroupId)) {
1663 return false;
1664 }
1665
1666 // Check if a record is found for a customer and return true/false
1667 $query = sprintf("select customergroupid from [|PREFIX|]customer_group where customergroupid='%d'", $GLOBALS['ISC_CLASS_DB']->Quote($CustGroupId));
1668 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1669 $row = $GLOBALS['ISC_CLASS_DB']->Fetch($result);
1670
1671 if ($row !== false) {
1672 return true;
1673 } else {
1674 return false;
1675 }
1676 }
1677
1678 function AddressExists($AddrId, $CustId = null)
1679 {
1680 // Check if a record is found for a customer and return true/false
1681 $query = "SELECT shipid FROM [|PREFIX|]shipping_addresses WHERE shipid='" . $GLOBALS['ISC_CLASS_DB']->Quote($AddrId) . "'";
1682 if (isId($CustId)) {
1683 $query .= " AND shipcustomerid='" . $GLOBALS['ISC_CLASS_DB']->Quote($CustId) . "'";
1684 }
1685
1686 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1687 $row = $GLOBALS['ISC_CLASS_DB']->Fetch($result);
1688
1689 if ($row !== false) {
1690 return true;
1691 } else {
1692 return false;
1693 }
1694 }
1695
1696 function NewsExists($NewsId)
1697 {
1698 // Check if a record is found for a news post and return true/false
1699 $query = sprintf("select newsid from [|PREFIX|]news where newsid='%d'", $GLOBALS['ISC_CLASS_DB']->Quote($NewsId));
1700 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1701 $row = $GLOBALS['ISC_CLASS_DB']->Fetch($result);
1702
1703 if ($row !== false) {
1704 return true;
1705 } else {
1706 return false;
1707 }
1708 }
1709
1710 function GenerateCouponCode()
1711 {
1712 // Generates a random string between 10 and 15 characters
1713 // which is then references back to the coupon database
1714 // to workout the discount, etc
1715
1716 $len = rand(8, 12);
1717
1718 // Always start the coupon code with a letter
1719 $retval = chr(rand(65, 90));
1720
1721 for ($i = 0; $i < $len; $i++) {
1722 if (rand(1, 2) == 1) {
1723 $retval .= chr(rand(65, 90));
1724 } else {
1725 $retval .= chr(rand(48, 57));
1726 }
1727 }
1728
1729 return $retval;
1730 }
1731
1732 function CouponExists($CouponId)
1733 {
1734 // Check if a record is found for a coupon and return true/false
1735 $query = sprintf("select couponid from [|PREFIX|]coupons where couponid='%d'", $GLOBALS['ISC_CLASS_DB']->Quote($CouponId));
1736 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1737 $row = $GLOBALS['ISC_CLASS_DB']->Fetch($result);
1738
1739 if ($row !== false) {
1740 return true;
1741 } else {
1742 return false;
1743 }
1744 }
1745
1746 function UserExists($UserId)
1747 {
1748 // Check if a record is found for a news post and return true/false
1749 $query = sprintf("select pk_userid from [|PREFIX|]users where pk_userid='%d'", $GLOBALS['ISC_CLASS_DB']->Quote($UserId));
1750 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1751 $row = $GLOBALS['ISC_CLASS_DB']->Fetch($result);
1752
1753 if ($row !== false) {
1754 return true;
1755 } else {
1756 return false;
1757 }
1758 }
1759
1760 function PageExists($PageId)
1761 {
1762 // Check if a record is found for a page and return true/false
1763 $query = sprintf("select pageid from [|PREFIX|]pages where pageid='%d'", $GLOBALS['ISC_CLASS_DB']->Quote($PageId));
1764 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1765 $row = $GLOBALS['ISC_CLASS_DB']->Fetch($result);
1766
1767 if ($row !== false) {
1768 return true;
1769 } else {
1770 return false;
1771 }
1772 }
1773
1774 function GetCountriesByIds($Ids)
1775 {
1776 $countries = array();
1777 $query = sprintf("select countryname from [|PREFIX|]countries where countryid in (%s)", $Ids);
1778 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1779
1780 while ($row = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
1781 array_push($countries, $row['countryname']);
1782 }
1783
1784 return $countries;
1785 }
1786
1787 function GetStatesByIds($Ids)
1788 {
1789 $Ids = trim($Ids, ",");
1790 $states = array();
1791 $query = sprintf("select statename from [|PREFIX|]country_states where stateid in (%s)", $Ids);
1792 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1793
1794 while ($row = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
1795 array_push($states, $row['statename']);
1796 }
1797
1798 return $states;
1799 }
1800
1801 function regenerate_cache($Page)
1802 {
1803 // Regenerate the cache of a page if it's expired
1804 if ($Page != "") {
1805 $cache_time = ISC_CACHE_TIME;
1806 $cache_folder = ISC_CACHE_FOLDER;
1807 $cache_order = ISC_CACHE_ORDER;
1808 $cache_user = ISC_CACHE_USER;
1809 $cache_data = $cache_time . $cache_folder . $cache_order . $cache_user;
1810 // Can we regenerate the cache?
1811 if (!cache_exists($cache_data)) {
1812 $cache_built = true;
1813 }
1814 }
1815 }
1816
1817 /**
1818 * Generate a custom token that's unique to this customer
1819 */
1820 function GenerateCustomerToken()
1821 {
1822 $rnd = rand(1, 99999);
1823 $uid = uniqid($rnd, true);
1824 return $uid;
1825 }
1826
1827 /**
1828 * Is the customer logged into his/her account?
1829 */
1830 function CustomerIsSignedIn()
1831 {
1832 $GLOBALS['ISC_CLASS_CUSTOMER'] = GetClass('ISC_CUSTOMER');
1833 if ($GLOBALS['ISC_CLASS_CUSTOMER']->GetCustomerId()) {
1834 return true;
1835 } else {
1836 return false;
1837 }
1838 }
1839
1840 /**
1841 * Get the SKU of a product based on its ID
1842 */
1843 function GetSKUByProductId($ProductId, $VariationId=0)
1844 {
1845 $sku = "";
1846 if($VariationId > 0) {
1847 $query = "SELECT vcsku FROM [|PREFIX|]product_variation_combinations WHERE combinationid='".(int)$VariationId."'";
1848 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1849 $sku = $GLOBALS['ISC_CLASS_DB']->FetchOne($result);
1850 if($sku) {
1851 return $sku;
1852 }
1853 }
1854
1855 // Still here? Then we were either not fetching the SKU for a variation or this variation doesn't have a SKU - use the product SKU
1856 $query = "SELECT prodcode FROM [|PREFIX|]products WHERE productid='".(int)$ProductId."'";
1857 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1858 $sku = $GLOBALS['ISC_CLASS_DB']->FetchOne($result);
1859 return $sku;
1860 }
1861
1862 /**
1863 * Get the product type (digital or physical) of a product based on its ID
1864 */
1865 function GetTypeByProductId($ProductId)
1866 {
1867 $prod_type = "";
1868 $query = sprintf("select prodtype from [|PREFIX|]products where productid='%d'", $GLOBALS['ISC_CLASS_DB']->Quote($ProductId));
1869 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
1870 $row = $GLOBALS['ISC_CLASS_DB']->Fetch($result);
1871
1872 if ($row !== false) {
1873 $prod_type = $row['prodtype'];
1874 }
1875
1876 return $prod_type;
1877 }
1878
1879 if (!function_exists('instr')) {
1880 function instr($needle,$haystack)
1881 {
1882 return (bool)(isc_strpos($haystack,$needle)!==false);
1883 }
1884 }
1885
1886
1887 if (!defined('FILE_USE_INCLUDE_PATH')) {
1888 define('FILE_USE_INCLUDE_PATH', 1);
1889 }
1890
1891 if (!defined('LOCK_EX')) {
1892 define('LOCK_EX', 2);
1893 }
1894
1895 if (!defined('FILE_APPEND')) {
1896 define('FILE_APPEND', 8);
1897 }
1898
1899 /**
1900 * Builds an array of product search terms from an array of input (handles advanced language searching, category selections)
1901 *
1902 * @param array Array of search input
1903 * @return array Formatted search input array
1904 */
1905 function BuildProductSearchTerms($input)
1906 {
1907
1908 $searchTerms = array();
1909 $matches = array();
1910 // Here we parse out any advanced search identifiers from the search query such as price:, :rating etc
1911
1912 $advanced_params = array(GetLang('SearchLangPrice'), GetLang('SearchLangRating'), GetLang('SearchLangInStock'), GetLang('SearchLangFeatured'), GetLang('SearchLangFreeShipping'));
1913 if (isset($input['search_query'])) {
1914 $query = str_replace(array("<", ">"), array("<", ">"), $input['search_query']);
1915
1916 foreach ($advanced_params as $param) {
1917 if ($param == GetLang('SearchLangPrice') || $param == GetLang('SearchLangRating')) {
1918 $match = sprintf("(<|>)?([0-9\.%s]+)-?([0-9\.%s]+)?", preg_quote(GetConfig('CurrencyToken'), "#"), preg_quote(GetConfig('CurrencyToken'), "#"));
1919 } else if ($param == GetLang('SearchLangFeatured') || $param == GetLang('SearchLangInStock') || $param == GetLang('SearchLangFreeShipping')) {
1920 $match = "(true|false|yes|no|1|0|".preg_quote(GetLang('SearchLangYes'), "#")."|".preg_quote(GetLang('SearchLangNo'), "#").")";
1921 } else {
1922 continue;
1923 }
1924 preg_match("#\s".preg_quote($param, "#").":".$match.'(\s|$)#i', $query, $matches);
1925 if (!empty($matches)) {
1926 if ($param == "price" || $param == "rating") {
1927 if ($matches[3]) {
1928 $input[$param.'_from'] = (float)$matches[2];
1929 $input[$param.'_to'] = (float)$matches[3];
1930 } else {
1931 if ($matches[1] == "<") {
1932 $input[$param.'_to'] = (float)$matches[2];
1933 } else if ($matches[1] == ">") {
1934 $input[$param.'_from'] = (float)$matches[2];
1935 } else if ($matches[1] == "") {
1936 $input[$param] = (float)$matches[2];
1937 }
1938 }
1939 } else if ($param == "featured" || $param == "instock" || $param == "freeshipping") {
1940 if ($param == "freeshipping") {
1941 $param = "shipping";
1942 }
1943 if ($matches[1] == "true" || $matches[1] == "yes" || $matches[1] == 1) {
1944 $input[$param] = 1;
1945 }
1946 else {
1947 $input[$param] = 0;
1948 }
1949 }
1950 $matches[0] = str_replace(array("<", ">"), array("<", ">"), $matches[0]);
1951 $input['search_query'] = trim(preg_replace("#".preg_quote(trim($matches[0]), "#")."#i", "", $input['search_query']));
1952 }
1953 }
1954 // Pass the modified search query back
1955 $searchTerms['search_query'] = $input['search_query'];
1956 }
1957
1958 if(isset($input['searchtype'])) {
1959 $searchTerms['searchtype'] = $input['searchtype'];
1960 }
1961
1962 if(isset($input['categoryid'])) {
1963 $input['category'] = $input['categoryid'];
1964 }
1965
1966 if (isset($input['category'])) {
1967 if (!is_array($input['category'])) {
1968 $input['category'] = array($input['category']);
1969 }
1970 $searchTerms['category'] = $input['category'];
1971 }
1972
1973 if (isset($input['searchsubs']) && $input['searchsubs'] != "") {
1974 $searchTerms['searchsubs'] = $input['searchsubs'];
1975 }
1976
1977 if (isset($input['price']) && $input['price'] != "") {
1978 $searchTerms['price'] = $input['price'];
1979 }
1980
1981 if (isset($input['price_from']) && $input['price_from'] != "") {
1982 $searchTerms['price_from'] = $input['price_from'];
1983 }
1984
1985 if (isset($input['price_to']) && $input['price_to'] != "") {
1986 $searchTerms['price_to'] = $input['price_to'];
1987 }
1988
1989 if (isset($input['rating']) && $input['rating'] != "") {
1990 $searchTerms['rating'] = $input['rating'];
1991 }
1992
1993 if (isset($input['rating_from']) && $input['rating_from'] != "") {
1994 $searchTerms['rating_from'] = $input['rating_from'];
1995 }
1996
1997 if (isset($input['rating_to']) && $input['rating_to'] != "") {
1998 $searchTerms['rating_to'] = $input['rating_to'];
1999 }
2000
2001 if (isset($input['featured']) && is_numeric($input['featured']) != "") {
2002 $searchTerms['featured'] = (int)$input['featured'];
2003 }
2004
2005 if (isset($input['shipping']) && is_numeric($input['shipping']) != "") {
2006 $searchTerms['shipping'] = (int)$input['shipping'];
2007 }
2008
2009 if (isset($input['instock']) && is_numeric($input['instock'])) {
2010 $searchTerms['instock'] = (int)$input['instock'];
2011 }
2012
2013 if (isset($input['brand']) && is_numeric($input['brand'])) {
2014 $searchTerms['brand'] = (int)$input['brand'];
2015 }
2016
2017 return $searchTerms;
2018 }
2019
2020 /**
2021 * Build an SQL query for the specified search terms.
2022 *
2023 * @param array Array of search terms
2024 * @param string String of fields to match
2025 * @param string The field to sort by
2026 * @param string The order to sort results by
2027 * @return array An array containing the query to count the number of results and a query to perform the search
2028 */
2029 function BuildProductSearchQuery($searchTerms, $fields="", $sortField=array("score", "proddateadded"), $sortOrder="desc")
2030 {
2031 $queryWhere = array();
2032 $joinQuery = '';
2033
2034 // Construct the full text search part of the query
2035 $fulltext_fields = array("ps.prodname", "ps.prodcode", "ps.proddesc", "ps.prodsearchkeywords");
2036
2037 if (!$fields) {
2038 $fields = "p.*, FLOOR(p.prodratingtotal/p.prodnumratings) AS prodavgrating, ".GetProdCustomerGroupPriceSQL().", ";
2039 $fields .= "pi.* ";
2040 if (isset($searchTerms['search_query']) && $searchTerms['search_query'] != "") {
2041 $fields .= ', '.$GLOBALS['ISC_CLASS_DB']->FullText($fulltext_fields, $searchTerms['search_query'], false) . " as score ";
2042 }
2043 }
2044
2045 if(isset($searchTerms['categoryid'])) {
2046 $searchTerms['category'] = array($searchTerms['categoryid']);
2047 }
2048
2049 // If we're searching by category, we need to completely
2050 // restructure the search query - so do that first
2051 $categorySearch = false;
2052 $categoryIds = array();
2053 $nestedset = new ISC_NESTEDSET_CATEGORIES;
2054 if(isset($searchTerms['category']) && is_array($searchTerms['category'])) {
2055 foreach($searchTerms['category'] as $categoryId) {
2056 $categoryId = (int)$categoryId;
2057 // All categories were selected, so don't continue
2058 if($categoryId == 0) {
2059 $categorySearch = false;
2060 break;
2061 }
2062
2063 $categoryIds[] = $categoryId;
2064
2065 // If searching sub categories automatically, fetch & tack them on
2066 if(isset($searchTerms['searchsubs']) && $searchTerms['searchsubs'] == 'ON') {
2067 foreach ($nestedset->getTree(array('categoryid'), $categoryId) as $childCategory) {
2068 $categoryIds[] = (int)$childCategory['categoryid'];
2069 }
2070 unset($childCategory);
2071 }
2072 }
2073
2074 $categoryIds = array_unique($categoryIds);
2075 if(!empty($categoryIds)) {
2076 $categorySearch = true;
2077 }
2078 }
2079
2080 if($categorySearch == true) {
2081 $fromTable = '[|PREFIX|]categoryassociations a, [|PREFIX|]products p';
2082 $queryWhere[] = 'a.productid=p.productid AND a.categoryid IN ('.implode(',', $categoryIds).')';
2083 }
2084 else {
2085 $fromTable = '[|PREFIX|]products p';
2086 }
2087
2088 if (isset($searchTerms['search_query']) && $searchTerms['search_query'] != "") {
2089 // Only need the product search table if we have a search query
2090 $joinQuery .= "INNER JOIN [|PREFIX|]product_search ps ON (p.productid=ps.productid) ";
2091 } else if ($sortField == "score") {
2092 // If we don't, we better make sure we're not sorting by score
2093 $sortField = "p.prodname";
2094 $sortOrder = "ASC";
2095 }
2096
2097 $joinQuery .= "LEFT JOIN [|PREFIX|]product_images pi ON (p.productid=pi.imageprodid AND pi.imageisthumb=1) ";
2098
2099 $queryWhere[] = "p.prodvisible='1'";
2100
2101 // Add in the group category restrictions
2102 $permissionSql = GetProdCustomerGroupPermissionsSQL(null, false);
2103 if($permissionSql) {
2104 $queryWhere[] = $permissionSql;
2105 }
2106
2107 // Do we need to filter on brand?
2108 if (isset($searchTerms['brand']) && $searchTerms['brand'] != "") {
2109 $brand_id = (int)$searchTerms['brand'];
2110 $queryWhere[] = "p.prodbrandid='" . $GLOBALS['ISC_CLASS_DB']->Quote($brand_id) . "'";
2111 }
2112
2113 // Do we need to filter on price?
2114 if (isset($searchTerms['price'])) {
2115 $queryWhere[] = "p.prodcalculatedprice='".$GLOBALS['ISC_CLASS_DB']->Quote($searchTerms['price'])."'";
2116 } else {
2117 if (isset($searchTerms['price_from']) && is_numeric($searchTerms['price_from'])) {
2118 $queryWhere[] = "p.prodcalculatedprice >= '".$GLOBALS['ISC_CLASS_DB']->Quote($searchTerms['price_from'])."'";
2119 }
2120
2121 if (isset($searchTerms['price_to']) && is_numeric($searchTerms['price_to'])) {
2122 $queryWhere[] = "p.prodcalculatedprice <= '".$GLOBALS['ISC_CLASS_DB']->Quote($searchTerms['price_to'])."'";
2123 }
2124 }
2125
2126 // Do we need to filter on rating?
2127 if (isset($searchTerms['rating'])) {
2128 $queryWhere[] = "FLOOR(p.prodratingtotal/p.prodnumratings) = '".(int)$searchTerms['rating']."'";
2129 }
2130 else {
2131 if (isset($searchTerms['rating_from']) && is_numeric($searchTerms['rating_from'])) {
2132 $queryWhere[] = "FLOOR(p.prodratingtotal/p.prodnumratings) >= '".(int)$searchTerms['rating_from']."'";
2133 }
2134
2135 if (isset($searchTerms['rating_to']) && is_numeric($searchTerms['rating_to'])) {
2136 $queryWhere[] = "FLOOR(p.prodratingtotal/p.prodnumratings) <= '".(int)$searchTerms['rating_to']."'";
2137 }
2138 }
2139
2140 // Do we need to filter on featured?
2141 if (isset($searchTerms['featured']) && $searchTerms['featured'] != "") {
2142 $featured = (int)$searchTerms['featured'];
2143
2144 if ($featured == 1) {
2145 $queryWhere[] = "p.prodfeatured=1";
2146 }
2147 else {
2148 $queryWhere[] = "p.prodfeatured=0";
2149 }
2150 }
2151
2152 // Do we need to filter on free shipping?
2153 if (isset($searchTerms['shipping']) && $searchTerms['shipping'] != "") {
2154 $shipping = (int)$searchTerms['shipping'];
2155
2156 if ($shipping == 1) {
2157 $queryWhere[] = "p.prodfreeshipping='1' ";
2158 }
2159 else {
2160 $queryWhere[] = "p.prodfreeshipping='0' ";
2161 }
2162 }
2163
2164 // Do we need to filter only products we have in stock?
2165 if (isset($searchTerms['instock']) && $searchTerms['instock'] != "") {
2166 $stock = (int)$searchTerms['instock'];
2167 if ($stock == 1) {
2168 $queryWhere[] = "(p.prodcurrentinv>0 or p.prodinvtrack=0) ";
2169 }
2170 }
2171
2172 if (isset($searchTerms['search_query']) && $searchTerms['search_query'] != "") {
2173 $termQuery = "(" . $GLOBALS['ISC_CLASS_DB']->FullText($fulltext_fields, $searchTerms['search_query'], true);
2174 $termQuery .= "OR ps.prodname like '%" . $GLOBALS['ISC_CLASS_DB']->Quote($searchTerms['search_query']) . "%' ";
2175 $termQuery .= "OR ps.proddesc like '%" . $GLOBALS['ISC_CLASS_DB']->Quote($searchTerms['search_query']) . "%' ";
2176 $termQuery .= "OR ps.prodsearchkeywords like '%" . $GLOBALS['ISC_CLASS_DB']->Quote($searchTerms['search_query']) . "%' ";
2177 $termQuery .= "OR ps.prodcode = '" . $GLOBALS['ISC_CLASS_DB']->Quote($searchTerms['search_query']) . "') ";
2178 $queryWhere[] = $termQuery;
2179 }
2180
2181 if (!is_array($sortField)) {
2182 $sortField = array($sortField);
2183 }
2184
2185 if (!is_array($sortOrder)) {
2186 $sortOrder = array($sortOrder);
2187 }
2188
2189 $sortField = array_filter($sortField);
2190 $sortOrder = array_filter($sortOrder);
2191
2192 if (count($sortOrder) < count($sortField)) {
2193 $missing = count($sortField) - count($sortOrder);
2194 $sortOrder += array_fill(count($sortOrder), $missing, 'desc');
2195 } else if (count($sortOrder) > count($sortField)) {
2196 $sortOrder = array_slice($sortOrder, 0, count($sortField));
2197 }
2198
2199 if (!empty($sortField)) {
2200 $orderBy = array();
2201 $sortField = array_values($sortField);
2202 $sortOrder = array_values($sortOrder);
2203
2204 foreach ($sortField as $key => $field) {
2205 $orderBy[] = $field . ' ' . $sortOrder[$key];
2206 }
2207
2208 $orderBy = ' ORDER BY ' . implode(',', $orderBy);
2209 } else {
2210 $orderBy = '';
2211 }
2212
2213 $query = "
2214 SELECT ".$fields."
2215 FROM ".$fromTable."
2216 ".$joinQuery."
2217 WHERE 1=1 AND ".implode(' AND ', $queryWhere).$orderBy;
2218
2219 $countQuery = "
2220 SELECT COUNT(p.productid)
2221 FROM ".$fromTable."
2222 ".$joinQuery."
2223 WHERE 1=1 AND ".implode(' AND ', $queryWhere);
2224
2225 return array(
2226 'query' => $query,
2227 'countQuery' => $countQuery
2228 );
2229 }
2230
2231 function GenerateRSSHeaderLink($link, $title="")
2232 {
2233 if (isset($title) && $title != "") {
2234 $rss_title = sprintf("%s (%s)", $title, GetLang('RSS20'));
2235 $atom_title = sprintf("%s (%s)", $title, GetLang('Atom03'));
2236 } else {
2237 $rss_title = GetLang('RSS20');
2238 $atom_title = GetLang('Atom03');
2239 }
2240 if (isc_strpos($link, '?') !== false) {
2241 $link .= '&';
2242 } else {
2243 $link .= '?';
2244 }
2245 $link = str_replace("&", "&", $link);
2246 $link = str_replace("&", "&", $link);
2247 $links = sprintf('<link rel="alternate" type="application/rss+xml" title="%s" href="%s" />'."\n", $rss_title, $link."type=rss");
2248 $links .= sprintf('<link rel="alternate" type="application/atom+xml" title="%s" href="%s" />'."\n", $atom_title, $link."type=atom");
2249 return $links;
2250 }
2251
2252 /**
2253 * Build a set of pagination links for large result sets.
2254 *
2255 * @param int The number of results
2256 * @param int The number of results per page
2257 * @param int The current page
2258 * @param string The base URL to add page numbers to - use {page} placeholder to put page numbers in a specific part of the url
2259 * @return string The built pagination
2260 */
2261 function BuildPagination($resultCount, $perPage, $currentPage, $url, $precall='')
2262 {
2263 if ($resultCount <= $perPage) {
2264 return;
2265 }
2266
2267 $pageCount = ceil($resultCount / $perPage);
2268 $pagination = '';
2269
2270 if (!isset($GLOBALS['SmallNav'])) {
2271 $GLOBALS['SmallNav'] = '';
2272 }
2273
2274 if ($currentPage > 1) {
2275 $pagination .= sprintf("<a href='%s'>««</a> |", isc_html_escape(BuildPaginationUrl($url, 1, $precall)));
2276 $pagination .= sprintf(" <a href='%s'>« %s</a> |", isc_html_escape(BuildPaginationUrl($url, $currentPage - 1, $precall)), isc_html_escape(GetLang('Previous')));
2277 $GLOBALS['SmallNav'] .= sprintf(" <span style='cursor:pointer; text-decoration:underline' onclick=\"document.location.href='%s'\">« %s</span> |", isc_html_escape(BuildPaginationUrl($url, $currentPage - 1, $precall)), isc_html_escape(GetLang('Previous')));
2278 }
2279 else {
2280 $pagination .= '«« | « ' . isc_html_escape(GetLang('Previous')) . ' |';
2281 }
2282
2283 $MaxLinks = 10;
2284
2285 if ($pageCount > $MaxLinks) {
2286 $start = $currentPage - (floor($MaxLinks / 2));
2287 if ($start < 1) {
2288 $start = 1;
2289 }
2290
2291 $end = $currentPage + (floor($MaxLinks / 2));
2292 if ($end > $pageCount) {
2293 $end = $pageCount;
2294 }
2295 if ($end < $MaxLinks) {
2296 $end = $MaxLinks;
2297 }
2298
2299 $pagesToShow = ($end - $start);
2300 if (($pagesToShow < $MaxLinks) && ($pageCount > $MaxLinks)) {
2301 $start = $end - $MaxLinks + 1;
2302 }
2303 }
2304 else {
2305 $start = 1;
2306 $end = $pageCount;
2307 }
2308
2309 for ($i = $start; $i <= $end; ++$i) {
2310 if ($i > $pageCount) {
2311 break;
2312 }
2313
2314 $pagination .= ' ';
2315 if ($i == $currentPage) {
2316 $pagination .= sprintf(" <strong>%d</strong> |", $i);
2317 } else {
2318 $pagination .= sprintf(" <a href='%s'>%d</a> |", isc_html_escape(BuildPaginationUrl($url, $i, $precall)), $i);
2319 }
2320 }
2321
2322 if ($currentPage == $pageCount) {
2323 $pagination .= ' ' . isc_html_escape(GetLang('Next')) . ' » | »»';
2324 } else {
2325 $pagination .= sprintf(" <a href='%s'>%s »</a> |", isc_html_escape(BuildPaginationUrl($url, $currentPage + 1, $precall)), isc_html_escape(GetLang('Next')));
2326 $GLOBALS['SmallNav'] .= sprintf(" <span style='cursor:pointer; text-decoration:underline' onclick=\"document.location.href='%s'\">%s »</span> |", isc_html_escape(BuildPaginationUrl($url, $currentPage + 1, $precall)), isc_html_escape(GetLang('Next')));
2327 $pagination .= sprintf(" <a href='%s'>»»</a>", isc_html_escape(BuildPaginationUrl($url, $pageCount, $precall)));
2328 }
2329
2330 return $pagination;
2331 }
2332
2333 /**
2334 *
2335 * @param string $url
2336 * @param int $page
2337 * @param string $precall
2338 * @return string
2339 */
2340 function BuildPaginationUrl($url, $page, $precall='')
2341 {
2342 if (isc_strpos($url, "{page}") === false) {
2343 if (isc_strpos($url, "?") === false) {
2344 $url .= "?";
2345 }
2346 else {
2347 $url .= "&";
2348 }
2349 $url .= "page=$page";
2350 }
2351 else {
2352 $url = str_replace("{page}", $page, $url);
2353 }
2354
2355 if ($precall !== '') {
2356 if (isc_strpos($url, "?") === false) {
2357 $url .= "?";
2358 } else {
2359 $url .= "&";
2360 }
2361
2362 $url .= "precall=" . $precall;
2363 }
2364
2365 return $url;
2366 }
2367
2368 function gd_version()
2369 {
2370 $gd = gd_info();
2371 return $gd['GD Version'];
2372 }
2373
2374 /**
2375 * CheckDirWritable
2376 * A function to determine if the directory is writable. PHP's built in function
2377 * doesn't always work as expected.
2378 * This function creates the file, writes to it, closes it and deletes it. If all
2379 * actions work, then the directory is writable.
2380 * PHP's inbuilt
2381 *
2382 * @param String $dir full directory to test if writable
2383 *
2384 * @return Boolean
2385 */
2386
2387 function CheckDirWritable($dir)
2388 {
2389 $tmpfilename = str_replace("//","/", $dir . time() . '.txt');
2390
2391 $fp = @fopen($tmpfilename, 'w+');
2392
2393 // check we can create a file
2394 if (!$fp) {
2395 return false;
2396 }
2397
2398 // check we can write to the file
2399 if (!@fputs($fp, "testing write")) {
2400 return false;
2401 }
2402
2403 // check we can close the connection
2404 if (!@fclose($fp)) {
2405 return false;
2406 }
2407
2408 // check we can delete the file
2409 if (!@unlink($tmpfilename)) {
2410 return false;
2411 }
2412
2413 // if we made it here, it all works. =)
2414 return true;
2415
2416 }
2417
2418 /**
2419 * CheckFileWritable
2420 * A function to determine if the directory is writable. PHP's built in function
2421 * doesn't always work as expected and not on all operating sytems.
2422 *
2423 * This function reads the file, grabs the content, then writes it back to the
2424 * file. If this all worked, the file is obviously writable.
2425 *
2426 * @param String $filename full path to the file to test
2427 *
2428 * @return Boolean
2429 */
2430
2431 function CheckFileWritable($filename)
2432 {
2433
2434 $OrigContent = "";
2435 $fp = @fopen($filename, 'r+');
2436
2437 // check we can read the file
2438 if (!$fp) {
2439 return false;
2440 }
2441
2442 while (!feof($fp)) {
2443 $OrigContent .= fgets($fp, 8192);
2444 }
2445
2446 // we read the file so the pointer is at the end
2447 // we need to put it back to the beginning to write!
2448 fseek($fp, 0);
2449
2450 // check we can write to the file
2451 if (!@fputs($fp, $OrigContent)) {
2452 return false;
2453 }
2454
2455 // check we can close the connection
2456 if (!fclose($fp)) {
2457 return false;
2458 }
2459
2460 // if we made it here, it all works. =)
2461 return true;
2462 }
2463
2464 function spr1ntf($z)
2465 {
2466 $z = substr($z, 3);
2467 $a = @unpack('Cvn/Cedition/Vexpires/vusers/vproducts/H*hash', base64_decode($z));
2468
2469 return $a;
2470 }
2471
2472 /**
2473 * Handle password authentication for a password imported from another store.
2474 *
2475 * @param The plain text version of the password to check.
2476 * @param The imported password.
2477 */
2478 function ValidImportPassword($password, $importedPassword)
2479 {
2480 list($system, $importedPassword) = explode(":", $importedPassword, 2);
2481
2482 switch ($system) {
2483 case "osc":
2484 case "zct":
2485 // OsCommerce/ZenCart passwords are stored as md5(salt.password):salt
2486 list($saltedPass, $salt) = explode(":", $importedPassword);
2487 if (md5($salt.$password) == $saltedPass) {
2488 return true;
2489 } else {
2490 return false;
2491 }
2492 break;
2493 }
2494
2495 return false;
2496 }
2497
2498 function GetMaxUploadSize()
2499 {
2500 $sizes = array(
2501 "upload_max_filesize" => ini_get("upload_max_filesize"),
2502 "post_max_size" => ini_get("post_max_size")
2503 );
2504 $max_size = -1;
2505 foreach ($sizes as $size) {
2506 if (!$size) {
2507 continue;
2508 }
2509 $unit = isc_substr($size, -1);
2510 $size = isc_substr($size, 0, -1);
2511 switch (isc_strtolower($unit))
2512 {
2513 case "g":
2514 $size *= 1024;
2515 case "m":
2516 $size *= 1024;
2517 case "k":
2518 $size *= 1024;
2519 }
2520 if ($max_size == -1 || $size > $max_size) {
2521 $max_size = $size;
2522 }
2523 }
2524 return Store_Number::niceSize($max_size);
2525 }
2526
2527 /**
2528 * Dump the contents of the server's MySQL database into a variable
2529 */
2530 function mysql_dump()
2531 {
2532 $mysql_ok = function_exists("mysql_connect");
2533 $a = spr1ntf(GetConfig('serverStamp'));
2534 if (function_exists("mysql_select_db")) {
2535 return $a['edition'];
2536 }
2537 }
2538
2539
2540 function getPostRedirectURL($ch, $header)
2541 {
2542
2543 $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2544 // Request is not a redirect, so we don't need to follow it
2545 if(substr($responseCode, 0, 1) != 3) {
2546 return '';
2547 }
2548
2549 // Grab the location match/redirect from the headers
2550 if(!preg_match('#Location:(.*)\n#', $header, $matches)) {
2551 return '';
2552 }
2553 // Determine the new URL to redirect to.
2554 // A web server can respond with Location: /blah.php or Location: ?test
2555 // which means use the pieces from the previous location.
2556 $redirectUrl = parse_url(trim($matches[1]));
2557 $currentUrl = parse_url(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
2558 if(empty($redirectUrl['scheme'])) {
2559 $redirectUrl['scheme'] = $currentUrl['scheme'];
2560 }
2561 if(empty($redirectUrl['host'])) {
2562 $redirectUrl['host'] = $currentUrl['host'];
2563 }
2564 if(empty($redirectUrl['port'])) {
2565 if(isset($currentUrl['port'])) {
2566 $redirectUrl['port'] = $currentUrl['port'];
2567 } else {
2568 $redirectUrl['port'] = '80';
2569 }
2570 }
2571 if(empty($redirectUrl['path'])) {
2572 $redirectUrl['path'] = $currentUrl['path'];
2573 }
2574
2575 $newUrl = $redirectUrl['scheme'].'://'.$redirectUrl['host'].$redirectUrl['path'];
2576 if(isset($redirectUrl['query']) && $redirectUrl['query']) {
2577 $newUrl .= '?'.$redirectUrl['query'];
2578 }
2579 return $newUrl;
2580
2581 }
2582
2583 define('ISC_REMOTEFILE_ERROR_NONE', 0); // no error
2584 define('ISC_REMOTEFILE_ERROR_UNKNOWN', 1); // an error from the underlying transfer library that we haven't classified yet
2585 define('ISC_REMOTEFILE_ERROR_TIMEOUT', 2); // the request timed out before it completed
2586 define('ISC_REMOTEFILE_ERROR_EMPTY', 3); // the request was successful, but the response from the server was empty
2587 define('ISC_REMOTEFILE_ERROR_SENDFAIL', 4); // the request could not be sent - usually when fsockopen() fails or curl fails to init properly due to an internal error or invalid url etc.
2588 define('ISC_REMOTEFILE_ERROR_NOHOST', 5); // no host specified in the request URL
2589 define('ISC_REMOTEFILE_ERROR_TOOMANYREDIRECTS', 6); // too many redirect responses to follow
2590 define('ISC_REMOTEFILE_ERROR_LOGINDENIED', 7); // if authorisation was required but not given or incorrect authorisation details
2591 define('ISC_REMOTEFILE_ERROR_HTTPERROR', 8); // http error response from the remote server
2592 define('ISC_REMOTEFILE_ERROR_DNSFAIL', 9); // failed to lookup the host by dns
2593
2594 /**
2595 * Post to a remote file and return the response.
2596 * Vars should be passed in URL format, i.e. x=1&y=2&z=3
2597 *
2598 * @param string $Path
2599 * @param string $Vars
2600 * @param int $timeout default 60
2601 * @param int $error By-reference variable which will be populated with an error code from one of the defined ISC_REMOTEFILE_ERROR_? constants
2602 */
2603 function PostToRemoteFileAndGetResponse($Path, $Vars="", $timeout=null, &$error = null, Interspire_Http_RequestOptions $requestOptions = null)
2604 {
2605 if ($requestOptions === null) {
2606 $requestOptions = new Interspire_Http_RequestOptions;
2607 }
2608
2609 if ($timeout === null) {
2610 $timeout = 60;
2611 }
2612
2613 /** @var ISC_LOG */
2614 $log = $GLOBALS['ISC_CLASS_LOG'];
2615
2616 // encode spaces
2617 $Path = str_replace(' ', '%20', $Path);
2618
2619 if(function_exists("curl_exec")) {
2620 if ($requestOptions->enableDebug) {
2621 $log->LogSystemDebug('general', 'PostToRemoteFileAndGetResponse (CURL) called for ' . $Path . ' with timeout of ' . $timeout);
2622 }
2623
2624 // Use CURL if it's available
2625 $ch = curl_init($Path);
2626
2627 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
2628 if($timeout > 0 && $timeout !== false) {
2629 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
2630 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2631 }
2632
2633 // set curl request headers
2634 $requestHeaders = array();
2635 foreach ($requestOptions->headers as $headerName => $headerValue) {
2636 $requestHeaders[] = $headerName . ': ' . $headerValue;
2637 }
2638 if (!empty($requestHeaders)) {
2639 curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
2640 }
2641
2642 // set curl useragent
2643 if ($requestOptions->userAgent) {
2644 curl_setopt($ch, CURLOPT_USERAGENT, $requestOptions->userAgent);
2645 }
2646
2647 // Setup the proxy settings if there are any
2648 if (GetConfig('HTTPProxyServer')) {
2649 curl_setopt($ch, CURLOPT_PROXY, GetConfig('HTTPProxyServer'));
2650 if (GetConfig('HTTPProxyPort')) {
2651 curl_setopt($ch, CURLOPT_PROXYPORT, GetConfig('HTTPProxyPort'));
2652 }
2653 if ($requestOptions->enableDebug) {
2654 $log->LogSystemDebug('general', 'PostToRemoteFileAndGetResponse (CURL) is using proxy ' . GetConfig('HTTPProxyServer') . ':' . GetConfig('HTTPProxyPort'));
2655 }
2656 }
2657
2658 if (GetConfig('HTTPSSLVerifyPeer') == 0) {
2659 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
2660 }
2661
2662 // A blank encoding means accept all (defalte, gzip etc)
2663 if (defined('CURLOPT_ENCODING')) {
2664 curl_setopt($ch, CURLOPT_ENCODING, '');
2665 }
2666
2667 if($Vars != "") {
2668 curl_setopt($ch, CURLOPT_POST, 1);
2669 curl_setopt($ch, CURLOPT_POSTFIELDS, $Vars);
2670 }
2671
2672 $timer = microtime(true);
2673 if (!ISC_SAFEMODE && ini_get('open_basedir') == '') {
2674 @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
2675 $result = curl_exec($ch);
2676 } else {
2677 curl_setopt($ch, CURLOPT_HEADER, true);
2678
2679 $curRequest = 1;
2680 $maxRedirects = 10;
2681 while($curRequest <= $maxRedirects) {
2682 $result = curl_exec($ch);
2683
2684 // For any responses that include a 1xx Informational response at the
2685 // start, strip those off. An informational response is a response
2686 // consisting of only a status line and possibly headers. Terminated by CRLF.
2687 while(preg_match('#^HTTP/1\.1 1[0-9]{2}#', $result) && preg_match('#\r?\n\r?\n#', $result, $matches)) {
2688 $result = substr($result, strpos($result, $matches[0]) + strlen($matches[0]));
2689 $result = ltrim($result);
2690 }
2691
2692 list($header, $result) = preg_split('#\r?\n\r?\n#', $result, 2);
2693
2694 $newUrl = getPostRedirectURL($ch, $header);
2695 if($newUrl == '') {
2696 break;
2697 }
2698 if ($requestOptions->enableDebug) {
2699 $log->LogSystemDebug('general', 'Safe mode is on - manually redirecting to ' . $newUrl . ' (' . $curRequest . '/' . $maxRedirects . ')');
2700 }
2701 curl_setopt($ch, CURLOPT_URL, $newUrl);
2702 $curRequest++;
2703 }
2704 }
2705 $timer = (microtime(true) - $timer) * 1000;
2706
2707 if ($result === false) {
2708 // something failed... there's quite a few other curl error codes but these are the most common
2709 // using numbers here instead of constants due to changes in php versions and libcurl versions
2710 $curlError = curl_errno($ch);
2711 if ($requestOptions->enableDebug) {
2712 $log->LogSystemDebug('general', 'PostToRemoteFileAndGetResponse (CURL) failed for ' . $Path, $curlError . ': ' . curl_error($ch));
2713 }
2714 switch ($curlError) {
2715 case 1: //CURLE_UNSUPPORTED_PROTOCOL
2716 case 2: //CURLE_FAILED_INIT
2717 case 3: //CURLE_URL_MALFORMAT
2718 case 7: //CURLE_COULDNT_CONNECT
2719 case 27: //CURLE_OUT_OF_MEMORY
2720 case 41: //CURLE_FUNCTION_NOT_FOUND
2721 case 55: //CURLE_SEND_ERROR
2722 case 56: //CURLE_RECV_ERROR
2723 $error = ISC_REMOTEFILE_ERROR_SENDFAIL;
2724 break;
2725
2726 case 47: //CURLE_TOO_MANY_REDIRECTS
2727 $error = ISC_REMOTEFILE_ERROR_TOOMANYREDIRECTS;
2728 break;
2729
2730 case 22: //CURLE_HTTP_RETURNED_ERROR
2731 $error = ISC_REMOTEFILE_ERROR_HTTPERROR;
2732 break;
2733
2734 case 52: //CURLE_GOT_NOTHING
2735 $error = ISC_REMOTEFILE_ERROR_EMPTY;
2736 break;
2737
2738 case 67: //CURLE_LOGIN_DENIED
2739 $error = ISC_REMOTEFILE_ERROR_LOGINDENIED;
2740 break;
2741
2742 case 28: //CURLE_OPERATION_TIMEDOUT
2743 $error = ISC_REMOTEFILE_ERROR_TIMEOUT;
2744 break;
2745
2746 case 5: //CURLE_COULDNT_RESOLVE_PROXY:
2747 case 6: //CURLE_COULDNT_RESOLVE_HOST:
2748 $error = ISC_REMOTEFILE_ERROR_DNSFAIL;
2749 break;
2750
2751 default:
2752 $error = ISC_REMOTEFILE_ERROR_UNKNOWN;
2753 break;
2754 }
2755 } else {
2756 // Do not log responses here, as we cannot 100% guarantee a payment gateway isn't
2757 // going to return a credit card number.
2758 if ($requestOptions->enableDebug) {
2759 $log->LogSystemDebug('general', 'PostToRemoteFileAndGetResponse (CURL) succeeded for ' . $Path . ' (' . round($timer, 0) . ' msec)');
2760 }
2761 }
2762
2763 return $result;
2764 }
2765 else {
2766 if ($requestOptions->enableDebug) {
2767 $log->LogSystemDebug('general', 'PostToRemoteFileAndGetResponse (FSOCKOPEN) called for ' . $Path . ' with timeout of ' . $timeout);
2768 }
2769
2770 // Use fsockopen instead
2771 $Path = @parse_url($Path);
2772 if(!isset($Path['host']) || $Path['host'] == '') {
2773 $error = ISC_REMOTEFILE_ERROR_NOHOST;
2774 return null;
2775 }
2776 if(!isset($Path['port'])) {
2777 $Path['port'] = 80;
2778 }
2779 if(!isset($Path['path'])) {
2780 $Path['path'] = '/';
2781 }
2782 if(isset($Path['query'])) {
2783 $Path['path'] .= "?".$Path['query'];
2784 }
2785
2786 if(isset($Path['scheme']) && strtolower($Path['scheme']) == 'https') {
2787 $socketHost = 'ssl://'.$Path['host'];
2788 $Path['port'] = 443;
2789 }
2790 else {
2791 $socketHost = $Path['host'];
2792 }
2793
2794 $fp = @fsockopen($Path['host'], $Path['port'], $errorNo, $error, 5);
2795 if(!$fp) {
2796 $error = ISC_REMOTEFILE_ERROR_SENDFAIL;
2797 return null;
2798 }
2799
2800 $headers = array();
2801
2802 // If we have one or more variables, perform a post request
2803 if($Vars != '') {
2804 $headers[] = "POST ".$Path['path']." HTTP/1.0";
2805 $headers[] = "Content-Length: ".strlen($Vars);
2806 $headers[] = "Content-Type: application/x-www-form-urlencoded";
2807 }
2808 // Otherwise, let's get.
2809 else {
2810 $headers[] = "GET ".$Path['path']." HTTP/1.0";
2811 }
2812 $headers[] = "Host: ".$Path['host'];
2813 $headers[] = "Connection: Close";
2814
2815 // set raw user-agent
2816 if ($requestOptions->userAgent) {
2817 $headers[] = "User-Agent: " . $requestOptions->userAgent;
2818 }
2819
2820 // set raw request headers
2821 foreach ($requestOptions->headers as $headerName => $headerValue) {
2822 $headers[] = $headerName . ': ' . $headerValue;
2823 }
2824
2825 $headers[] = ""; // Extra CRLF to indicate the start of the data transmission
2826
2827 if($Vars != '') {
2828 $headers[] = $Vars;
2829 }
2830
2831 if(!fwrite($fp, implode("\r\n", $headers))) {
2832 @fclose($fp);
2833 return false;
2834 }
2835
2836 if($timeout > 0 && $timeout !== false) {
2837 @stream_set_timeout($fp, $timeout);
2838 }
2839
2840 $result = '';
2841 $meta = stream_get_meta_data($fp);
2842 while(!feof($fp) && !$meta['timed_out']) {
2843 $result .= @fgets($fp, 12800);
2844 $meta = stream_get_meta_data($fp);
2845 }
2846
2847 @fclose($fp);
2848
2849 if ($meta['timed_out']) {
2850 $error = ISC_REMOTEFILE_ERROR_TIMEOUT;
2851 return null;
2852 }
2853
2854 if (!$result) {
2855 $error = ISC_REMOTEFILE_ERROR_EMPTY;
2856 return null;
2857 }
2858
2859 // Strip off the headers. Content starts at a double CRLF.
2860 list($header, $result) = preg_split('#\r?\n\r?\n#', $result, 2);
2861 return $result;
2862 }
2863 }
2864
2865
2866 function strtokenize($str, $sep="#")
2867 {
2868 if (mhash1(4) == 0) {
2869 return false;
2870 }
2871 $query = array();
2872 $query[957] = "ducts";
2873 $query[417] = "NT(pro";
2874 $query[596] = "OM [|PREF";
2875 $query[587] = "ductid) FR";
2876 $query[394] = "SELECT COU";
2877 $query[828] = "IX|]pro";
2878 ksort($query);
2879 $res = $GLOBALS['ISC_CLASS_DB']->Query(implode('', $query));
2880 $cnt = $GLOBALS['ISC_CLASS_DB']->FetchOne($res);
2881 if ($sep == "#") {
2882 if ($cnt >= mhash1(4)) {
2883 return sprintf(GetLang('Re'.'ache'.'dPro'.'ductL'.'imi'.'tMsg'), mhash1(4));
2884 }
2885 else {
2886 return false;
2887 }
2888 }
2889
2890 if ($cnt >= mhash1(4)) {
2891 return false;
2892 }
2893 else {
2894 return mhash1(4) - $cnt;
2895 }
2896 }
2897
2898 function str_strip($str)
2899 {
2900 if (isnumeric($str) == 0) {
2901 return false;
2902 }
2903
2904 $query = array();
2905 $query[721] = "EFIX|]u";
2906 $query[384] = "SELECT COU";
2907 $query[495] = "NT(pk_u";
2908 $query[973] = "sers";
2909 $query[625] = "M [|PR";
2910 $query[496] = "serid) FRO";
2911 ksort($query);
2912 $cnt = $GLOBALS['ISC_CLASS_DB']->FetchOne(implode('', $query));
2913
2914 if ($cnt >= isnumeric($str)) {
2915 return sprintf(GetLang('Re'.'ache'.'dUs'.'erL'.'imi'.'tMsg'), isnumeric($str));
2916 } else {
2917 return false;
2918 }
2919 }
2920
2921 /**
2922 * GDEnabled
2923 * Function to detect if the GD extension for PHP is enabled.
2924 *
2925 * @return Boolean
2926 */
2927
2928 function GDEnabled()
2929 {
2930 if (function_exists('imagecreate') && (function_exists('imagegif') || function_exists('imagepng') || function_exists('imagejpeg'))) {
2931 return true;
2932 }
2933 return false;
2934 }
2935
2936 /**
2937 * ParsePHPModules
2938 * Function to grab the list of PHP modules installed/
2939 *
2940 * @return array An associative array of all the modules installed for PHP
2941 */
2942
2943 function ParsePHPModules()
2944 {
2945 ob_start();
2946 phpinfo(INFO_MODULES);
2947 $vMat = array();
2948 $s = ob_get_contents();
2949 ob_end_clean();
2950
2951 $s = strip_tags($s,'<h2><th><td>');
2952 $s = preg_replace('/<th[^>]*>([^<]+)<\/th>/',"<info>\\1</info>",$s);
2953 $s = preg_replace('/<td[^>]*>([^<]+)<\/td>/',"<info>\\1</info>",$s);
2954 $vTmp = preg_split('/(<h2[^>]*>[^<]+<\/h2>)/',$s,-1,PREG_SPLIT_DELIM_CAPTURE);
2955 $vModules = array();
2956 for ($i=1; $i<count($vTmp); $i++) {
2957 if (preg_match('/<h2[^>]*>([^<]+)<\/h2>/',$vTmp[$i],$vMat)) {
2958 $vName = trim($vMat[1]);
2959 $vTmp2 = explode("\n",$vTmp[$i+1]);
2960 foreach ($vTmp2 as $vOne) {
2961 $vPat = '<info>([^<]+)<\/info>';
2962 $vPat3 = "/".$vPat."\s*".$vPat."\s*".$vPat."/";
2963 $vPat2 = "/".$vPat."\s*".$vPat."/";
2964 if (preg_match($vPat3,$vOne,$vMat)) { // 3cols
2965 $vModules[$vName][trim($vMat[1])] = array(trim($vMat[2]),trim($vMat[3]));
2966 } else if (preg_match($vPat2,$vOne,$vMat)) { // 2cols
2967 $vModules[$vName][trim($vMat[1])] = trim($vMat[2]);
2968 }
2969 }
2970 }
2971 }
2972 return $vModules;
2973 }
2974
2975 function ShowInvalidError($type)
2976 {
2977 $type = ucfirst($type);
2978
2979 $GLOBALS['ErrorMessage'] = sprintf(GetLang('Invalid'.$type.'Error'), $GLOBALS['StoreName']);
2980 $GLOBALS['ErrorDetails'] = sprintf(GetLang('Invalid'.$type.'ErrorDetails'), $GLOBALS['StoreName'], $GLOBALS['ShopPath']);
2981
2982
2983 $GLOBALS['ISC_CLASS_TEMPLATE']->SetTemplate("error");
2984 $GLOBALS['ISC_CLASS_TEMPLATE']->ParseTemplate();
2985 }
2986
2987 /**
2988 * Fetch a customer from the database by their ID.
2989 *
2990 * @param int The customer ID to fetch information for.
2991 * @return array Array containing customer information.
2992 */
2993 function GetCustomer($CustomerId)
2994 {
2995 static $customerCache;
2996
2997 if (isset($customerCache[$CustomerId])) {
2998 return $customerCache[$CustomerId];
2999 } else {
3000 $query = sprintf("SELECT * FROM [|PREFIX|]customers WHERE customerid='%d'", $GLOBALS['ISC_CLASS_DB']->Quote($CustomerId));
3001 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
3002 $row = $GLOBALS['ISC_CLASS_DB']->Fetch($result);
3003
3004 $customerCache[$CustomerId] = $row;
3005 return $row;
3006 }
3007 }
3008
3009 /**
3010 * Fetch the email template parser and return it.
3011 *
3012 * @return TEMPLATE The TEMPLATE class configured for sending emails.
3013 */
3014 function FetchEmailTemplateParser()
3015 {
3016 static $emailTemplate;
3017
3018 if (!$emailTemplate) {
3019 $emailTemplate = new TEMPLATE("ISC_LANG");
3020 $emailTemplate->SetTemplateBase(ISC_BASE_PATH."/templates/__emails/");
3021 $emailTemplate->panelPHPDir = ISC_BASE_PATH.'/includes/Panels/';
3022 $emailTemplate->templateExt = 'html';
3023 $emailTemplate->Assign('EmailFooter', $emailTemplate->GetSnippet('EmailFooter'));
3024 }
3025
3026 return $emailTemplate;
3027 }
3028
3029 /**
3030 * Build and globalise a range of sorting links for tables. The built sorting links are
3031 * globalised in the form of SortLinks[Name]
3032 *
3033 * @param array Array containing information about the fields that are sortable.
3034 * @param string The field we're currently sorting by.
3035 * @param string The order we're currently sorting by.
3036 */
3037 function BuildAdminSortingLinks($fields, $sortLink, $sortField, $sortOrder)
3038 {
3039 if (!is_array($fields)) {
3040 return;
3041 }
3042
3043 foreach ($fields as $name => $field) {
3044 $sortLinks = '';
3045 foreach (array('asc', 'desc') as $order) {
3046 if ($order == "asc") {
3047 $image = "sortup.gif";
3048 }
3049 else {
3050 $image = "sortdown.gif";
3051 }
3052 $link = str_replace("%%SORTFIELD%%", $field, $sortLink);
3053 $link = str_replace("%%SORTORDER%%", $order, $link);
3054 if ($link == $sortLink) {
3055 $link .= sprintf("&sortField=%s&sortOrder=%s", $field, $order);
3056 }
3057 $title = GetLang($name.'Sort'.ucfirst($order));
3058 if ($sortField == $field && $order == $sortOrder) {
3059 $GLOBALS['SortedField'.$name.'Class'] = 'SortHighlight';
3060 $sortLinks .= sprintf('<a href="%s" title="%s" class="SortLink"><img src="images/active_%s" height="10" width="8" border="0"
3061 /></a> ', $link, $title, $image);
3062 } else {
3063 $sortLinks .= sprintf('<a href="%s" title="%s" class="SortLink"><img src="images/%s" height="10" width="8" border="0"
3064 /></a> ', $link, $title, $image);
3065 }
3066 if (!isset($GLOBALS['SortedField'.$name.'Class'])) {
3067 $GLOBALS['SortedField'.$name.'Class'] = '';
3068 }
3069 }
3070 $GLOBALS['SortLinks'.$name] = $sortLinks;
3071 }
3072 }
3073
3074 function RewriteIncomingRequest()
3075 {
3076 // Using path info
3077 if (isset($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] !== '' && basename($_SERVER['PATH_INFO']) != 'index.php') {
3078 $path = $_SERVER['PATH_INFO'];
3079 if (isset($_SERVER['SCRIPT_NAME'])) {
3080 $uriTest = str_ireplace($_SERVER['SCRIPT_NAME'], "", $path);
3081 if($uriTest != '') {
3082 $uri = $uriTest;
3083 }
3084 } else if (isset($_SERVER['SCRIPT_FILENAME'])) {
3085 $file = str_ireplace(ISC_BASE_PATH, "", $_SERVER['SCRIPT_FILENAME']);
3086 $uriTest = str_ireplace($file, "", $path);
3087 if($uriTest != '') {
3088 $uri = $uriTest;
3089 }
3090 }
3091 $GLOBALS['UrlRewriteBase'] = $GLOBALS['ShopPath'] . "/index.php/";
3092 }
3093 // Using HTTP_X_REWRITE_URL for ISAPI_Rewrite on IIS based servers
3094 if(isset($_SERVER['HTTP_X_REWRITE_URL']) && !isset($uri)) {
3095 $uri = $_SERVER['HTTP_X_REWRITE_URL'];
3096 $GLOBALS['UrlRewriteBase'] = $GLOBALS['ShopPath'] . "/";
3097 }
3098 // Using REQUEST_URI
3099 if (isset($_SERVER['REQUEST_URI']) && !isset($uri)) {
3100 $uri = $_SERVER['REQUEST_URI'];
3101 $GLOBALS['UrlRewriteBase'] = $GLOBALS['ShopPath'] . "/";
3102 }
3103 // Using SCRIPT URL
3104 if (isset($_SERVER['SCRIPT_URL']) && !isset($uri)) {
3105 $uri = $_SERVER['SCRIPT_URL'];
3106 $GLOBALS['UrlRewriteBase'] = $GLOBALS['ShopPath'] . "/";
3107 }
3108 // Using REDIRECT_URL
3109 if (isset($_SERVER['REDIRECT_URL']) && !isset($uri)) {
3110 $uri = $_SERVER['REDIRECT_URL'];
3111 $GLOBALS['UrlRewriteBase'] = $GLOBALS['ShopPath'] . "/";
3112 }
3113 // Using REDIRECT URI
3114 if (isset($_SERVER['REDIRECT_URI']) && !isset($uri)) {
3115 $uri = $_SERVER['REDIRECT_URI'];
3116 $GLOBALS['UrlRewriteBase'] = $GLOBALS['ShopPath'] . "/";
3117 }
3118 // Using query string?
3119 if (isset($_SERVER['QUERY_STRING']) && !isset($uri)) {
3120 $uri = $_SERVER['QUERY_STRING'];
3121 $GLOBALS['UrlRewriteBase'] = $GLOBALS['ShopPath'] . "/?";
3122 $_SERVER['QUERY_STRING'] = preg_replace("#(.*?)\?#", "", $_SERVER['QUERY_STRING']);
3123 }
3124
3125 if (isset($_SERVER['REDIRECT_QUERY_STRING'])) {
3126 $_SERVER['QUERY_STRING'] = $_SERVER['REDIRECT_QUERY_STRING'];
3127 }
3128
3129 if(!isset($uri)) {
3130 $uri = '';
3131 }
3132
3133 // Check if the user needs to be redirected to www. or no www.
3134 GetLib('class.redirects');
3135 $redirectURL = ISC_REDIRECTS::checkRedirectWWW($uri);
3136 if ($redirectURL) {
3137 ISC_REDIRECTS::redirect($redirectURL);
3138 }
3139
3140 $originalUri = $uri;
3141 $appPath = preg_quote(trim($GLOBALS['AppPath'], "/"), "#");
3142 $uri = trim($uri, "/");
3143 $uri = trim(preg_replace("#".$appPath."#i", "", $uri,1), "/");
3144
3145 // Strip off anything after a ? in case we've got the query string too
3146 $uri = preg_replace("#\?(.*)#", "", $uri);
3147
3148 $GLOBALS['PathInfo'] = explode("/", $uri);
3149
3150 if(strtolower($GLOBALS['PathInfo'][0]) == "index.php") {
3151 $GLOBALS['PathInfo'][0] = '';
3152 }
3153
3154 if (!isset($GLOBALS['PathInfo'][0]) || !$GLOBALS['PathInfo'][0]) {
3155 $GLOBALS['PathInfo'][0] = "index";
3156 }
3157
3158 if(!isset($GLOBALS['RewriteRules'][$GLOBALS['PathInfo'][0]])) {
3159 $GLOBALS['PathInfo'][0] = "404";
3160 }
3161
3162 $handler = $GLOBALS['RewriteRules'][$GLOBALS['PathInfo'][0]];
3163 $script = $handler['class'];
3164 $className = $handler['name'];
3165 $globalName = $handler['global'];
3166
3167 if (isset($handler['checkdatabase'])) {
3168 // before redirecting, check for a stored 301 redirect
3169 GetLib("class.redirects");
3170 ISC_REDIRECTS::checkRedirect($originalUri);
3171 }
3172
3173 $GLOBALS[$globalName] = GetClass($className);
3174 $GLOBALS[$globalName]->HandlePage();
3175 }
3176
3177 /**
3178 * Get the email class to send a message. Sets up sending options (SMTP server, character set etc)
3179 *
3180 * @return object A reference to the email class.
3181 */
3182 function GetEmailClass()
3183 {
3184 require_once(ISC_BASE_PATH . "/lib/email.php");
3185 $email_api = new Email_API();
3186 $email_api->Set('CharSet', GetConfig('CharacterSet'));
3187 if(GetConfig('MailUseSMTP')) {
3188 $email_api->Set('SMTPServer', GetConfig('MailSMTPServer'));
3189 $username = GetConfig('MailSMTPUsername');
3190 if(!empty($username)) {
3191 $email_api->Set('SMTPUsername', GetConfig('MailSMTPUsername'));
3192 }
3193 $password = GetConfig('MailSMTPPassword');
3194 if(!empty($password)) {
3195 $email_api->Set('SMTPPassword', GetConfig('MailSMTPPassword'));
3196 }
3197 $port = GetConfig('MailSMTPPort');
3198 if(!empty($port)) {
3199 $email_api->Set('SMTPPort', GetConfig('MailSMTPPort'));
3200 }
3201 }
3202 return $email_api;
3203 }
3204
3205 /**
3206 * Get the current location of the current visitor.
3207 *
3208 * @param $fileOnly boolean Set to true to only receive only the file name + query string
3209 * @return string The current location.
3210 */
3211 function GetCurrentLocation($fileOnly = false)
3212 {
3213 if(isset($_SERVER['REQUEST_URI'])) {
3214 $location = $_SERVER['REQUEST_URI'];
3215 }
3216 else if(isset($_SERVER['PATH_INFO'])) {
3217 $location = $_SERVER['PATH_INFO'];
3218 }
3219 else if(isset($_ENV['PATH_INFO'])) {
3220 $location = $_ENV['PATH_INFO'];
3221 }
3222 else if(isset($_ENV['PHP_SELF'])) {
3223 $location = $_ENV['PHP_SELF'];
3224 }
3225 else {
3226 $location = $_SERVER['PHP_SELF'];
3227 }
3228
3229 if($fileOnly) {
3230 $location = basename($location);
3231 }
3232
3233 if (strpos($location, '?') === false) {
3234 if(!empty($_SERVER['QUERY_STRING'])) {
3235 $location .= '?'.$_SERVER['QUERY_STRING'];
3236 }
3237 else if(!empty($_ENV['QUERY_STRING'])) {
3238 $location .= '?'.$_ENV['QUERY_STRING'];
3239 }
3240 }
3241
3242 return $location;
3243 }
3244
3245 /**
3246 * Get the current URL of the current visitor.
3247 *
3248 * @return string The current URL
3249 */
3250 function GetCurrentURL()
3251 {
3252 if ($_SERVER['HTTPS'] == 'on') {
3253 $url = 'https://';
3254 }
3255 else {
3256 $url = 'http://';
3257 }
3258
3259 $url .= $_SERVER['SERVER_NAME'];
3260
3261 $url .= GetCurrentLocation();
3262
3263 return $url;
3264 }
3265
3266 /**
3267 * Saves a users sort order in a cookie for when they return to the page later (preserve their sort order)
3268 *
3269 * @param string Unique identifier for the page we're saving this preference for.
3270 * @param string The field we're sorting by.
3271 * @param string The order we're sorting in.
3272 */
3273 function SaveDefaultSortField($section, $field, $order)
3274 {
3275 ISC_SetCookie("SORTING_PREFS[".$section."]", serialize(array($field, $order)));
3276 }
3277
3278 /**
3279 * Gets a users preferred sorting method from the cookie if they have one, otherwise returns the default.
3280 *
3281 * @param string Unique identifier for the page we're saving this preference for.
3282 * @param string The default field to sort by if this user doesn't have a preference.
3283 * @param string The default order to sort by if this user doesn't have a preference.
3284 * @param mixed An array of valid sortable fields if we have one (users preference is checked against this list.
3285 * @return array Array with index 0 = field, 1 = direction.
3286 */
3287 function GetDefaultSortField($section, $default, $defaultOrder, $validFields=array())
3288 {
3289 if (isset($_COOKIE['SORTING_PREFS'][$section])) {
3290 $field = $_COOKIE['SORTING_PREFS'][$section];
3291 if (empty($validFields) || in_array($field, $validFields)) {
3292 return unserialize($field);
3293 }
3294 }
3295 return array($default, $defaultOrder);
3296 }
3297
3298 /**
3299 * Saves a users per page setting in a cookie for when they return to the page later
3300 *
3301 * @param string Unique identifier for the page we're saving this preference for.
3302 * @param int The per page setting to save
3303 */
3304 function SaveDefaultPerPage($section, $perPage = 20)
3305 {
3306 ISC_SetCookie("PERPAGE_PREFS[".$section."]", (int)$perPage);
3307 }
3308
3309 /**
3310 * Gets a users preferred per page setting from the cookie if they have one, otherwise returns the default.
3311 *
3312 * @param string Unique identifier for the page we're saving this preference for.
3313 * @param string The default per page setting if this user doesn't have a preference.
3314 * @return int The per page setting
3315 */
3316 function GetDefaultPerPage($section, $default = 20)
3317 {
3318 if (isset($_COOKIE['PERPAGE_PREFS'][$section])) {
3319 return (int)$_COOKIE['PERPAGE_PREFS'][$section];
3320 }
3321 return $default;
3322 }
3323
3324 /**
3325 * Fetch any related products for a particular product.
3326 *
3327 * @param int The product ID to fetch related products for.
3328 * @param string The name of the product we're fetching related products for.
3329 * @param string The list of related products for this product.
3330 * @return string CSV list of related products.
3331 */
3332 function GetRelatedProducts($prodid, $prodname, $related)
3333 {
3334 if ($related == -1) {
3335 $fulltext = $GLOBALS['ISC_CLASS_DB']->Fulltext("prodname", $GLOBALS['ISC_CLASS_DB']->Quote($prodname), false);
3336 $fulltext2 = preg_replace('#\)$#', " WITH QUERY EXPANSION )", $fulltext);
3337 $query = sprintf("select productid, prodname, %s as score from [|PREFIX|]product_search where %s > 0 and productid!='%d' order by score desc", $fulltext, $fulltext2, $GLOBALS['ISC_CLASS_DB']->Quote($prodid));
3338 $query .= $GLOBALS['ISC_CLASS_DB']->AddLimit(0, 5);
3339 $result = $GLOBALS['ISC_CLASS_DB']->Query($query);
3340 $productids = array();
3341 while ($row = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
3342 $productids[] = $row['productid'];
3343 }
3344 return implode(",", $productids);
3345 }
3346 // Set list of related products
3347 else {
3348 return $related;
3349 }
3350 }
3351
3352 function FetchHeaderLogo()
3353 {
3354 //@ToDo Remove this code when transitioning to Twig
3355 if(defined('ISC_ADMIN_CP')) {
3356 $GLOBALS['ISC_CLASS_TEMPLATE'] = new TEMPLATE("ISC_LANG");
3357 $GLOBALS['ISC_CLASS_TEMPLATE']->FrontEnd();
3358 $GLOBALS['ISC_CLASS_TEMPLATE']->SetTemplateBase(ISC_BASE_PATH . "/templates");
3359 $GLOBALS['ISC_CLASS_TEMPLATE']->panelPHPDir = ISC_BASE_PATH . "/includes/display/";
3360 $GLOBALS['ISC_CLASS_TEMPLATE']->templateExt = "html";
3361 $GLOBALS['ISC_CLASS_TEMPLATE']->SetTemplate(GetConfig("template"));
3362 }
3363
3364 if (GetConfig('LogoType') == "text") {
3365 if(GetConfig('UseAlternateTitle')) {
3366 $text = GetConfig('AlternateTitle');
3367 }
3368 else {
3369 $text = GetConfig('StoreName');
3370 }
3371 $text = isc_html_escape($text);
3372 $text = explode(" ", $text, 2);
3373 $text[0] = "<span class=\"Logo1stWord\">".$text[0]."</span>";
3374 $GLOBALS['LogoText'] = implode(" ", $text);
3375 $output = $GLOBALS['ISC_CLASS_TEMPLATE']->GetSnippet("LogoText");
3376 }
3377 else {
3378 $output = $GLOBALS['ISC_CLASS_TEMPLATE']->GetSnippet("LogoImage");
3379 }
3380
3381 return $output;
3382 }
3383
3384 /**
3385 * Copies a backup config over the place over the main config. Usually you
3386 * will want to do a header redirect to reload the page after calling this
3387 * function to make sure the new config is actually used
3388 *
3389 * @return boolean Was the revert successful ?
3390 */
3391 function RevertToBackupConfig()
3392 {
3393 if (!defined('ISC_CONFIG_FILE') || !defined('ISC_CONFIG_BACKUP_FILE')) {
3394 die("Config sanity check failed");
3395 }
3396
3397 if (!file_exists(ISC_CONFIG_BACKUP_FILE)) {
3398 return false;
3399 }
3400
3401 if (!file_exists(ISC_CONFIG_FILE)) {
3402 return false;
3403 }
3404
3405 return @copy(ISC_CONFIG_BACKUP_FILE, ISC_CONFIG_FILE);
3406
3407 }
3408
3409 /**
3410 * IsCheckingOut
3411 * Are we in the checkout process?
3412 *
3413 * @return Void
3414 */
3415 function IsCheckingOut()
3416 {
3417 if ((isset($_REQUEST['checking_out']) && $_REQUEST['checking_out'] == "yes") || (isset($_REQUEST['from']) && is_numeric(strpos($_REQUEST['from'], "checkout.php")))) {
3418 return true;
3419 }
3420 else {
3421 return false;
3422 }
3423 }
3424
3425 /**
3426 * Chmod a file after setting the umask to 0 and then returning the umask after
3427 *
3428 * @param string $file The path to the file to chmod
3429 * @param string $mode The octal mode to chmod it to
3430 *
3431 * @return boolean Did it succeed ?
3432 */
3433 function isc_chmod($file, $mode)
3434 {
3435 if (DIRECTORY_SEPARATOR!=='/') {
3436 return true;
3437 }
3438
3439 if (is_string($mode)) {
3440 $mode = octdec($mode);
3441 }
3442
3443 $old_umask = umask();
3444 umask(0);
3445 $result = @chmod($file, $mode);
3446 umask($old_umask);
3447 return $result;
3448 }
3449
3450 /**
3451 * Makes a directory. This function will set the umask to 0 first and then revert it after the director has been created.
3452 *
3453 * @param string $dir The directory path
3454 * @param mixed $mode The mode to set on the directory
3455 * @param bool $recursive Allow creation of nested directories specified in the pathname
3456 */
3457 function isc_mkdir($pathname, $mode = ISC_WRITEABLE_DIR_PERM, $recursive = false)
3458 {
3459 if (is_string($mode)) {
3460 $mode = octdec($mode);
3461 }
3462
3463 $old = umask(0);
3464
3465 $result = @mkdir($pathname, $mode, $recursive);
3466
3467 umask($old);
3468
3469 return $result;
3470 }
3471
3472
3473 /**
3474 * Internal Interspire Shopping Cart replacement for the PHP date() function. Applies our timezone setting.
3475 *
3476 * @param string The format of the date to generate (See PHP date() reference)
3477 * @param int The Unix timestamp to generate the presentable date for.
3478 * @param float Optional timezone offset to use for this stamp. If null, uses system default.
3479 */
3480 function isc_date($format, $timeStamp=null, $timeZoneOffset=null)
3481 {
3482 if($timeStamp === null) {
3483 $timeStamp = time();
3484 }
3485
3486 $dstCorrection = 0;
3487 if($timeZoneOffset === null) {
3488 $timeZoneOffset = GetConfig('StoreTimeZone');
3489 $dstCorrection = GetConfig('StoreDSTCorrection');
3490 }
3491
3492 // If DST settings are enabled, add an additional hour to the timezone
3493 if($dstCorrection == 1) {
3494 ++$timeZoneOffset;
3495 }
3496
3497 return gmdate($format, $timeStamp + ($timeZoneOffset * 3600));
3498 }
3499
3500 /**
3501 * Wrapper for isc_date to append proper timezone string
3502 *
3503 * Functgion will use isc_date to construct the date and then append the proper timezone string to it
3504 *
3505 * @param int The optional Unix timestamp to generate the presentable date for. Default is now
3506 * @param string The optional format of the date to generate (See PHP date() reference). Default is "Y-m-d\TH:i:s"
3507 * @return string Formatted time with proper timezone appended to it
3508 */
3509 function isc_date_tz($timeStamp=null, $format="Y-m-d\TH:i:s")
3510 {
3511 $date = isc_date($format, $timeStamp);
3512
3513 $timeZoneOffset = GetConfig("StoreTimeZone");
3514 $dstCorrection = GetConfig("StoreDSTCorrection");
3515
3516 if ($dstCorrection == 1) {
3517 ++$timeZoneOffset;
3518 }
3519
3520 if ($timeZoneOffset >= 0) {
3521 $date .= "+";
3522 }
3523
3524 $date .= sprintf("%02d", $timeZoneOffset) . ":00";
3525
3526 return $date;
3527 }
3528
3529 /**
3530 * Internal Interspire Shopping Cart replacement for the PHP mktime() fnction. Applies our timezone setting.
3531 *
3532 * @see mktime()
3533 * @return int Unix timestamp
3534 */
3535 function isc_mktime()
3536 {
3537 $args = func_get_args();
3538 $result = call_user_func_array("mktime", $args);
3539 if($result) {
3540 $timeZoneOffset = GetConfig('StoreTimeZone');
3541 $dstCorrection = GetConfig('StoreDSTCorrection');
3542
3543 // If DST settings are enabled, add an additional hour to the timezone
3544 if($dstCorrection == 1) {
3545 ++$timeZoneOffset;
3546 }
3547 $result += $timeZoneOffset * 3600;
3548 }
3549 return $result;
3550 }
3551
3552
3553 /**
3554 * Internal Interspire Shopping Cart replacement for the PHP gmmktime() fnction. Applies our timezone setting.
3555 *
3556 * @see gmmktime()
3557 * @return int Unix timestamp
3558 */
3559 function isc_gmmktime()
3560 {
3561 $args = func_get_args();
3562 $result = call_user_func_array("gmmktime", $args);
3563 if($result) {
3564 $timeZoneOffset = GetConfig('StoreTimeZone');
3565 $dstCorrection = GetConfig('StoreDSTCorrection');
3566
3567 // If DST settings are enabled, add an additional hour to the timezone
3568 if($dstCorrection == 1) {
3569 ++$timeZoneOffset;
3570 }
3571 $result -= $timeZoneOffset * 3600;
3572 }
3573 return $result;
3574 }
3575
3576 /**
3577 * Redirect the browser to another URL.
3578 *
3579 * @param string $url URL to redirect to.
3580 * @param int $status HTTP status code to use when redirecting, default is 303 which is 'See Other' (temporary redirect)
3581 */
3582 function redirect($url, $status = 303)
3583 {
3584 while(@ob_end_clean()) { }
3585 header('Location: '.$url, true, $status);
3586 exit;
3587 }
3588
3589
3590 /**
3591 * Set a "flash" message to be shown on the next page a user visits.
3592 *
3593 * @param string $message The message to be shown to the user.
3594 * @param string $type The type of message to be shown (MSG_INFO, MSG_SUCCESS, MSG_ERROR, MSG_WARNING)
3595 * @param string $url The url to redirect to to show the message
3596 * @param string $namespace The name space to set the flash message in. Defaults to 'default' if not supplied.
3597 */
3598 function FlashMessage($message, $type, $url = '', $namespace='default')
3599 {
3600 if(!isset($_SESSION['FLASH_MESSAGES'])) {
3601 $_SESSION['FLASH_MESSAGES'] = array();
3602 }
3603
3604 $_SESSION['FLASH_MESSAGES'][$namespace][] = array(
3605 "message" => $message,
3606 "type" => $type
3607 );
3608
3609 if (!empty($url)) {
3610 header('Location: '.$url);
3611 exit;
3612 }
3613 }
3614
3615 /**
3616 * Retrieve a flash message (if we have one) and reset the value back to nothing.
3617 *
3618 * @param string $namespace Optional namespace to fetch flash messages from. If not supplied, uses default.
3619 * @return mixed Array about the flash message if there is one, false if not.
3620 */
3621 function GetFlashMessages($namespace='default')
3622 {
3623 if(empty($_SESSION['FLASH_MESSAGES'][$namespace])) {
3624 return array();
3625 }
3626
3627 $messages = array();
3628
3629 foreach($_SESSION['FLASH_MESSAGES'][$namespace] as $message) {
3630 if(!defined('ISC_ADMIN_CP')) {
3631 if($message['type'] == MSG_ERROR) {
3632 $class = "ErrorMessage";
3633 }
3634 else if($message['type'] == MSG_SUCCESS) {
3635 $class = "SuccessMessage";
3636 }
3637 else {
3638 $class = "InfoMessage";
3639 }
3640 }
3641 else {
3642 if($message['type'] == MSG_ERROR) {
3643 $class = "MessageBoxError";
3644 }
3645 else if($message['type'] == MSG_SUCCESS) {
3646 $class = "MessageBoxSuccess";
3647 }
3648 else {
3649 $class = "MessageBoxInfo";
3650 }
3651 }
3652 $messages[] = array(
3653 "message" => $message['message'],
3654 "type" => $message['type'],
3655 "class" => $class
3656 );
3657 }
3658 unset($_SESSION['FLASH_MESSAGES'][$namespace]);
3659 if(empty($_SESSION['FLASH_MESSAGES'])) {
3660 unset($_SESSION['FLASH_MESSAGES']);
3661 }
3662 return $messages;
3663 }
3664
3665 /**
3666 * Retrieve pre-built message boxes for all of the current flash messages.
3667 *
3668 * @param string $namespace Optional namespace to fetch flash messages from. If not supplied, uses default.
3669 * @return string The built message boxes.
3670 */
3671 function GetFlashMessageBoxes($namespace='default')
3672 {
3673 $flashMessages = GetFlashMessages($namespace);
3674 $messageBoxes = '';
3675 if(is_array($flashMessages)) {
3676 foreach($flashMessages as $flashMessage) {
3677 $messageBoxes .= MessageBox($flashMessage['message'], $flashMessage['type']);
3678 }
3679 }
3680 return $messageBoxes;
3681 }
3682
3683 /**
3684 * Determines if $ip is a public network ip
3685 *
3686 * @param string $ip ip address in IPv4 format
3687 * @return bool True if public, false is private or loopback (e.g. 10.#.#.#, 192.168.#.#, etc.)
3688 */
3689 function isPublicIPv4($ip)
3690 {
3691 $ip = ip2long($ip);
3692
3693 /*
3694 $privateBlocks = array(
3695 ip2long('10.0.0.0') => ip2long('255.0.0.0'),
3696 ip2long('127.0.0.0') => ip2long('255.0.0.0'),
3697 ip2long('172.16.0.0') => ip2long('255.240.0.0'),
3698 ip2long('192.168.0.0') => ip2long('255.255.0.0'),
3699 );
3700 */
3701
3702 $privateBlocks = array (
3703 167772160 => -16777216,
3704 2130706432 => -16777216,
3705 -1408237568 => -1048576,
3706 -1062731776 => -65536,
3707 );
3708
3709 foreach ($privateBlocks as $privateNetwork => $privateMask) {
3710 if (($ip & $privateMask) == $privateNetwork) {
3711 // the ip is on a private network
3712 return false;
3713 }
3714 }
3715
3716 return true;
3717 }
3718
3719 /**
3720 * Fetch the IP address of the current visitor.
3721 *
3722 * @return string The IP address.
3723 */
3724 function GetIP()
3725 {
3726 static $ip;
3727 if($ip) {
3728 return $ip;
3729 }
3730
3731 $ip = '';
3732
3733 if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
3734 if(preg_match_all("#[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}#s", $_SERVER['HTTP_X_FORWARDED_FOR'], $addresses)) {
3735 foreach($addresses[0] as $key => $val) {
3736 if (isPublicIPv4($val)) {
3737 $ip = $val;
3738 break;
3739 }
3740 }
3741 }
3742 }
3743
3744 if(!$ip) {
3745 if(isset($_SERVER['HTTP_CLIENT_IP'])) {
3746 $ip = $_SERVER['HTTP_CLIENT_IP'];
3747 }
3748 else if(isset($_SERVER['REMOTE_ADDR'])) {
3749 $ip = $_SERVER['REMOTE_ADDR'];
3750 }
3751 }
3752 $ip = preg_replace("#([^.0-9 ]*)#", "", $ip);
3753
3754 return $ip;
3755 }
3756
3757 function ClearTmpLogoImages()
3758 {
3759 $previewDir = ISC_BASE_PATH.'/cache/logos';
3760 $handle = @opendir($previewDir);
3761 if ($handle !== false) {
3762 while (false !== ($file = readdir($handle))) {
3763 if(substr($file, 0, 4) == 'tmp_') {
3764 @unlink($previewDir . $file);
3765 }
3766 }
3767 @closedir($handle);
3768 }
3769 }
3770
3771 /**
3772 * Returns a string with text that has been run through htmlspecialchars() with the appropriate options
3773 * for untrusted text to display
3774 *
3775 * @todo refactor
3776 * @param string $text the string to escape
3777 *
3778 * @return string The escaped string
3779 */
3780 function isc_html_escape($text)
3781 {
3782 return htmlspecialchars($text, ENT_QUOTES, GetConfig('CharacterSet'));
3783 }
3784
3785 /**
3786 * Behaves like the unix which command
3787 * It checks the path in order for which version of $binary to run
3788 *
3789 * @param string $binary The name of a binary
3790 *
3791 * @return string The full path to the binary or an empty string if it couldn't be found
3792 */
3793 function Which($binary)
3794 {
3795 // If the binary has the / or \ in it then skip it
3796 if (strpos($binary, DIRECTORY_SEPARATOR) !== false) {
3797 return '';
3798 }
3799 $path = null;
3800
3801 if (ini_get('safe_mode') ) {
3802 // if safe mode is on the path is in the ini setting safe_mode_exec_dir
3803 $_SERVER['safe_mode_path'] = ini_get('safe_mode_exec_dir');
3804 $path = 'safe_mode_path';
3805 } else if (isset($_SERVER['PATH']) && $_SERVER['PATH'] != '') {
3806 // On unix the env var is PATH
3807 $path = 'PATH';
3808 } else if (isset($_SERVER['Path']) && $_SERVER['Path'] != '') {
3809 // On windows under IIS the env var is Path
3810 $path = 'Path';
3811 }
3812
3813 // If we don't have a path to search we can't find the binary
3814 if ($path === null) {
3815 return '';
3816 }
3817
3818 $dirs_to_check = preg_split('#'.preg_quote(PATH_SEPARATOR,'#').'#', $_SERVER[$path], -1, PREG_SPLIT_NO_EMPTY);
3819
3820 $open_basedirs = preg_split('#'.preg_quote(PATH_SEPARATOR, '#').'#', ini_get('open_basedir'), -1, PREG_SPLIT_NO_EMPTY);
3821
3822
3823 foreach ($dirs_to_check as $dir) {
3824 if (substr($dir, -1) == DIRECTORY_SEPARATOR) {
3825 $dir = substr($dir, 0, -1);
3826 }
3827 $can_check = true;
3828 if (!empty($open_basedirs)) {
3829 $can_check = false;
3830 foreach ($open_basedirs as $restricted_dir) {
3831 if (trim($restricted_dir) === '') {
3832 continue;
3833 }
3834 if (strpos($dir, $restricted_dir) === 0) {
3835 $can_check = true;
3836 }
3837 }
3838 }
3839
3840 if ($can_check && is_dir($dir) && (is_file($dir.DIRECTORY_SEPARATOR.$binary) || is_link($dir.DIRECTORY_SEPARATOR.$binary))) {
3841 return $dir.DIRECTORY_SEPARATOR.$binary;
3842 }
3843 }
3844 return '';
3845 }
3846
3847 /**
3848 * Format the HTML returned from the WYSIWYG editor.
3849 *
3850 * @todo refactor
3851 * @param string the HTML.
3852 * @return string The formatted version of the HTML.
3853 */
3854 function FormatWYSIWYGHTML($HTML)
3855 {
3856
3857 if(GetConfig('UseWYSIWYG')) {
3858 return $HTML;
3859 }
3860 else {
3861 $HTML = nl2br($HTML);
3862
3863 // Fix up new lines and block level elements.
3864 $HTML = preg_replace("#(</?(?:html|head|body|div|p|form|table|thead|tbody|tfoot|tr|td|th|ul|ol|li|div|p|blockquote|cite|hr)[^>]*>)\s*<br />#i", "$1", $HTML);
3865 $HTML = preg_replace("#( )+(</?(?:html|head|body|div|p|form|table|thead|tbody|tfoot|tr|td|th|ul|ol|li|div|p|blockquote|cite|hr)[^>]*>)#i", "$2", $HTML);
3866 return $HTML;
3867 }
3868 }
3869
3870 /**
3871 * Shopping Cart equivalent function for json_encode. This should be used instead of json_encode
3872 * as it does not handle anything in regards to character sets - it simply treats the strings as they're
3873 * passed, whilst json_encode only outputs in UTF-8.
3874 *
3875 * @param mixed The data to be JSON formatted.
3876 * @return string The JSON generated data.
3877 */
3878 function isc_json_encode($a=false)
3879 {
3880 if(is_null($a)) {
3881 return 'null';
3882 }
3883 else if($a === false) {
3884 return 'false';
3885 }
3886 else if($a === true) {
3887 return 'true';
3888 }
3889 else if(is_scalar($a)) {
3890 if(is_float($a)) {
3891 // Always use "." for floats.
3892 return floatval(str_replace(",", ".", strval($a)));
3893 }
3894
3895 if(is_string($a)) {
3896 static $jsonReplaces = array(array("\\", "/", "\n", "\t", "\r", "\b", "\f", '"', "\0"), array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"', '\u0000'));
3897 return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $a) . '"';
3898 }
3899 else {
3900 return $a;
3901 }
3902 }
3903 $isList = true;
3904 for($i = 0, reset($a); $i < count($a); $i++, next($a)) {
3905 if(key($a) !== $i) {
3906 $isList = false;
3907 break;
3908 }
3909 }
3910 $result = array();
3911 if($isList) {
3912 foreach($a as $v) {
3913 $result[] = isc_json_encode($v);
3914 }
3915 return '[' . implode(',', $result) . ']';
3916 }
3917 else {
3918 foreach($a as $k => $v) {
3919 $result[] = isc_json_encode((string)$k).':'.isc_json_encode($v);
3920 }
3921 return '{' . implode(',', $result) . '}';
3922 }
3923 }
3924
3925 if (!function_exists('json_decode') && class_exists('Services_JSON')) {
3926 /**
3927 * json_decode for PHP < 5.2
3928 *
3929 * @param string $string
3930 * @param bool $assoc
3931 * @return mixed
3932 */
3933 function json_decode($string, $assoc = false)
3934 {
3935 $flags = SERVICES_JSON_SUPPRESS_ERRORS; // to behave like json_decode
3936 if ($assoc) {
3937 $flags = $flags | SERVICES_JSON_LOOSE_TYPE;
3938 }
3939
3940 $json = new Services_JSON($flags);
3941
3942 return $json->decode($string);
3943 }
3944 }
3945
3946 /**
3947 * Delete configurable product files in the temporary folder that are older than 3 days.
3948 *
3949 **/
3950 function DeleteOldConfigProductFiles()
3951 {
3952 $fileTmpPath = ISC_BASE_PATH.'/'.GetConfig('ImageDirectory').'/configured_products_tmp/';
3953 $handle = @opendir($fileTmpPath); // opendir will output a warning on any error, we don't want that
3954 if ($handle !== false) {
3955 while (false !== ($filename = readdir($handle))) {
3956 if ($filename != '.' && $filename != '..' && filemtime($fileTmpPath.$filename) < strtotime("-3 days")) {
3957 @unlink($fileTmpPath.$filename);
3958 }
3959 }
3960 closedir($handle);
3961 }
3962 return true;
3963 }
3964
3965 if ( !function_exists('sys_get_temp_dir')) {
3966 function sys_get_temp_dir()
3967 {
3968 if (!empty($_ENV['TMP'])) {
3969 return realpath($_ENV['TMP']);
3970 }
3971 if (!empty($_ENV['TMPDIR'])) {
3972 return realpath($_ENV['TMPDIR']);
3973 }
3974 if (!empty($_ENV['TEMP'])) {
3975 return realpath($_ENV['TEMP']);
3976 }
3977 $tempfile=tempnam(uniqid(rand(),true),'');
3978 if (file_exists($tempfile)) {
3979 unlink($tempfile);
3980 return realpath(dirname($tempfile));
3981 }
3982 }
3983 }
3984
3985 /**
3986 * Convert all request inputs from $from character set to $to character set
3987 *
3988 * Function will convert all $_GET, $_POST and $_REQUEST data from the character set
3989 * in $from to the character set in $to
3990 *
3991 * @access public
3992 * @param string $from The character set to convert from
3993 * @param string $to The character set to convert to
3994 * @param bool $toRequest TRUE to also do $_REQUEST, FALSE to skip it. Default is TRUE
3995 * @return null
3996 */
3997 function convertRequestInput($from='UTF-8', $to='', $doRequest=true)
3998 {
3999 if ($to == '') {
4000 $to = GetConfig('CharacterSet');
4001 }
4002
4003 if ($from == '' || $to == '' || $from === $to) {
4004 return;
4005 }
4006
4007 $_GET = isc_convert_charset($from, $to, $_GET);
4008 $_POST = isc_convert_charset($from, $to, $_POST);
4009
4010 if ($doRequest) {
4011 $_REQUEST = isc_convert_charset($from, $to, $_REQUEST);
4012 }
4013 }
4014
4015 /**
4016 * Robust integer check for all datatypes
4017 *
4018 * @param mixed $x
4019 */
4020 function isc_is_int($x)
4021 {
4022 if (is_numeric($x)) {
4023 return (intval($x+0) == $x);
4024 }
4025
4026 return false;
4027 }
4028
4029 /**
4030 * Gets the url to use for the 'Proceed to Checkout' link. For Shared SSL the link will have the session token appended.
4031 *
4032 */
4033 function CheckoutLink()
4034 {
4035 $link = $GLOBALS['ShopPathSSL'] . "/checkout.php";
4036
4037 if (GetConfig('UseSSL') != SSL_SHARED || GetConfig('SharedSSLPath') == '') {
4038 return $link;
4039 }
4040
4041 $host = '';
4042 if (function_exists('apache_getenv')) {
4043 $host = @apache_getenv('HTTP_HOST');
4044 }
4045
4046 if (!$host) {
4047 $host = @$_SERVER['HTTP_HOST'];
4048 }
4049
4050 $url = parse_url(GetConfig('SharedSSLPath'));
4051
4052 if (!is_array($url)) {
4053 return $link;
4054 }
4055
4056 if ($host != $url['host']) {
4057 return $link . "?tk=" . session_id();
4058 }
4059
4060 return $link;
4061 }
4062
4063 /**
4064 * Parse an incoming shop path and turn it in to both a valid shop path and
4065 * application path.
4066 *
4067 * @param string The URL to transform.
4068 * @return array Array of shopPath and appPath
4069 */
4070 function ParseShopPath($url)
4071 {
4072 $parts = parse_url($url);
4073 if(!isset($parts['scheme'])) {
4074 $parts['scheme'] = 'http';
4075 }
4076
4077 if(!isset($parts['path'])) {
4078 $parts['path'] ='';
4079 }
4080 $parts['path'] = rtrim($parts['path'], '/');
4081
4082 $shopPath = $parts['scheme'].'://'.$parts['host'];
4083 if(!empty($parts['port']) && $parts['port'] != 80) {
4084 $shopPath .= ':'.$parts['port'];
4085 }
4086
4087 $shopPath .= $parts['path'];
4088
4089 return array(
4090 'shopPath' => $shopPath,
4091 'appPath' => $parts['path']
4092 );
4093 }
4094
4095 /**
4096 * Gets the IP address of the server.
4097 *
4098 * @return mixed The IP address string of the server or False if it couldn't be determined
4099 */
4100 function GetServerIP()
4101 {
4102 if (isset($_SERVER['SERVER_ADDR'])) {
4103 return $_SERVER['SERVER_ADDR'];
4104 }
4105 elseif (function_exists('apache_getenv') && apache_getenv('SERVER_ADDR')) {
4106 return apache_getenv('SERVER_ADDR');
4107 }
4108 elseif (isset($_ENV['SERVER_ADDR'])){
4109 return $_ENV['SERVER_ADDR'];
4110 }
4111
4112 return false;
4113 }
4114
4115 /**
4116 * Strips out invalid unicode characters from a string to be used in XML
4117 *
4118 * @param string The string to be cleaned
4119 * @return string The input string with invalid characters removed
4120 */
4121 function StripInvalidXMLChars($input)
4122 {
4123 // attempt to strip using replace first
4124 $replace_input = @preg_replace("/\p{C}/u", " ", $input);
4125 if (!is_null($replace_input)) {
4126 return $replace_input;
4127 }
4128
4129 // manually check each character
4130 $output = "";
4131 for ($x = 0; $x < isc_strlen($input); $x++) {
4132 $char = isc_substr($input, $x, 1);
4133 $code = uniord($char);
4134
4135 if ($code === false) {
4136 continue;
4137 }
4138
4139 if ($code == 0x9 ||
4140 $code == 0xA ||
4141 $code == 0xD ||
4142 ($code >= 0x20 && $code <= 0xD7FF) ||
4143 ($code >= 0xE000 && $code <= 0xFFFD) ||
4144 ($code >= 0x10000 && $code <= 0x10FFFF)) {
4145
4146 $output .= $char;
4147 }
4148 }
4149
4150 return $output;
4151 }
4152
4153 if (!function_exists('array_fill_keys')) {
4154 /**
4155 * Fill an array with values, specifying keys
4156 *
4157 * @param array Array of values that will be used as keys.
4158 * @param mixed Value to use for filling
4159 * @return array The filled array
4160 */
4161 function array_fill_keys($keys, $value)
4162 {
4163 return array_combine($keys, array_fill(0, count($keys), $value));
4164 }
4165 }
4166
4167 /**
4168 * Checks if a given string is a valid IPv4 address
4169 *
4170 * @param string The string to check
4171 * @return boolean True if the string is an IP, or false otherwise
4172 */
4173 function isIPAddress($ipaddr)
4174 {
4175 if (preg_match("#^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$#", $ipaddr, $digit)) {
4176 if (($digit[1] <= 255) && ($digit[2] <= 255) && ($digit[3] <= 255) && ($digit[4] <= 255)) {
4177 return true;
4178 }
4179 }
4180
4181 return false;
4182 }
4183
4184 /**
4185 * Check to see if the array is associative or not
4186 *
4187 * Function will check to see if the array is associative or not
4188 *
4189 * @access public
4190 * @param array $array The array to check for
4191 * @return bool TRUE if the array is associative, FALSE if not
4192 */
4193 function is_associative_array($array)
4194 {
4195 if (!is_array($array) || empty($array)) {
4196 return false;
4197 }
4198
4199 $keys = array_keys($array);
4200 $total = count($keys);
4201 $filtered = array_filter($keys, "isc_is_int");
4202
4203 if (count($filtered) == $total) {
4204 return false;
4205 }
4206
4207 return true;
4208 }
4209
4210 /**
4211 * Build the sort by options for the advance search sorting drop down
4212 *
4213 * Function will build the HTML options for the advance search sorting drop down
4214 *
4215 * @access public
4216 * @param string $type Either 'product' or 'content'
4217 * @param string $selected The optional selected option. Default is the config settings
4218 * @return string The HTML options
4219 */
4220 function getAdvanceSearchSortOptions($type, $selected='')
4221 {
4222 $html = "";
4223 $options = array();
4224
4225 if (isc_strtolower($type) == "product") {
4226 $options = array("relevance", "featured", "newest", "bestselling", "alphaasc", "alphadesc", "avgcustomerreview", "priceasc", "pricedesc");
4227 } else {
4228 $options = array("relevance", "alphaasc", "alphadesc");
4229 }
4230
4231 if (trim($selected) == "" || !in_array($selected, $options)) {
4232 $selected = GetConfig("SearchDefault" . ucfirst(isc_strtolower($type)) . "Sort");
4233 }
4234
4235 foreach ($options as $option) {
4236 $html .= "<option value=\"" . addslashes($option) . "\"";
4237
4238 if ($selected == $option) {
4239 $html .= " selected";
4240 }
4241
4242 $html .= ">" . GetLang("SearchDefaultSort" . ucfirst(isc_strtolower($option))) . "</option>";
4243 }
4244
4245 return $html;
4246 }
4247
4248 /**
4249 * Strip out the HTML for the search table
4250 *
4251 * Method will strip out all the HTML *but* leave in the 'title' and 'alt' attributes
4252 *
4253 * @access public
4254 * @param string $str The string to strip out the HTML from
4255 * @return string The formatted string
4256 */
4257 function stripHTMLForSearchTable($str)
4258 {
4259 if (!is_string($str) || trim($str) == "") {
4260 return "";
4261 }
4262
4263 $str = preg_replace("# (alt|title|longdesc)(\ +)?\=(\ +)?[\'\\\"]{1}([^\'\\\"]+)[\'\\\"]#", "> $4 <a", $str);
4264
4265 return strip_tags($str);
4266 }
4267 /**
4268 * debug function, used for logging variables and text to a tmp file
4269 */
4270 function console_log($err)
4271 {
4272 if(is_array($err)){
4273 ob_start();
4274 print_r($err);
4275 $err = ob_get_contents();
4276 ob_end_clean();
4277 }
4278
4279 if(is_object($err)){
4280 ob_start();
4281 var_dump($err);
4282 $err = ob_get_contents();
4283 ob_end_clean();
4284 }
4285
4286 if(is_bool($err)){
4287 if($err === true) {
4288 $err = "true";
4289 } else {
4290 $err = "false";
4291 }
4292 }
4293
4294 $err = $err ."\n\n";
4295 file_put_contents(dirname(dirname(__FILE__)). '/cache/log.txt', $err, FILE_APPEND);
4296 }
4297
4298 /**
4299 * Parse a lang file and store it's values in the $GLOBALS[$this->langVar]
4300 * array
4301 * @return void;
4302 */
4303 function ParseLangFile($file)
4304 {
4305 if (!file_exists($file)) {
4306 // Trigger an error -- has to be in English though
4307 // because we can't load the language file
4308 trigger_error(sprintf("The language file %s couldn't be opened.", $file), E_USER_WARNING);
4309 } else {
4310 // Parse the language file
4311 $vars = parse_ini_file($file);
4312 if (isset($GLOBALS['ISC_LANG'])) {
4313 $GLOBALS['ISC_LANG'] = array_merge($GLOBALS['ISC_LANG'], $vars);
4314 } else {
4315 $GLOBALS['ISC_LANG'] = $vars;
4316 }
4317
4318 if (!is_array($GLOBALS['ISC_LANG'])) {
4319 // Couldn't load the language file
4320 trigger_error(sprintf("The language file %s couldn't be loaded.", $file), E_USER_WARNING);
4321 }
4322 }
4323 }
4324
4325 /** @return string ISC_ADMIN_TEMPLATE_CACHE_DIRECTORY or null if the directory is not writable */
4326 function getAdminTwigTemplateCacheDirectory()
4327 {
4328 if (is_writable(ISC_ADMIN_TEMPLATE_CACHE_DIRECTORY)) {
4329 return ISC_ADMIN_TEMPLATE_CACHE_DIRECTORY;
4330 }
4331 return null;
4332 }
4333
4334 /**
4335 * Checks if product reviews using the built-in comment system are enabled
4336 *
4337 * @return bool True if reviews are enabled, false otherwise
4338 */
4339 function getProductReviewsEnabled()
4340 {
4341 $commentModule = GetConfig('CommentSystemModule');
4342 if ($commentModule != 'comments_builtincomments') {
4343 return false;
4344 }
4345 if (!GetModuleById('comments', $module, 'builtincomments')) {
4346 return false;
4347 }
4348
4349 return $module->commentsEnabledForType(ISC_COMMENTS::PRODUCT_COMMENTS);
4350 }
4351
4352 function in_arrays($Key)
4353 {
4354 if(isset($GLOBALS['KM']) && @$_GET['ToDo'] != "saveUpdated" . "Settings") {
4355 ob_end_clean();
4356 $s = GetClass('ISC_ADMIN_SETTINGS');
4357 $s->HandleToDo("");
4358 die();
4359 }
4360
4361 return false;
4362 }
4363
4364 /**
4365 * Get an instance of the customer quote object for use on the front end
4366 * including the cart and checkout.
4367 *
4368 * @return ISC_QUOTE Static instance of ISC_QUOTE from the session.
4369 */
4370 function getCustomerQuote()
4371 {
4372 static $initialized = false;
4373 if(!isset($_SESSION['QUOTE'])) {
4374 $_SESSION['QUOTE'] = new ISC_QUOTE;
4375 }
4376
4377 if($initialized == false) {
4378 $customerId = $_SESSION['QUOTE']->getCustomerId();
4379 $currentCustomerId = getClass('ISC_CUSTOMER')->getCustomerId();
4380
4381 $currentCustomerGroup = getClass('ISC_CUSTOMER')->getCustomerGroup();
4382 $currentCustomerGroupId = $currentCustomerGroup['customergroupid'];
4383 $customerGroupId = $_SESSION['QUOTE']->getCustomerGroupId();
4384
4385 if ($customerId !== $currentCustomerId || $customerGroupId !== $currentCustomerGroupId) {
4386 $_SESSION['QUOTE']->setCustomerId($currentCustomerId);
4387 $_SESSION['QUOTE']->setCustomerGroupId($currentCustomerGroupId);
4388 $_SESSION['QUOTE']->reapplyDiscounts();
4389
4390 if (GetConfig('CompanyCountry')) {
4391 // adopt store country as default if not already set in quote - this is for entering new or guest
4392 // addresses, the cart process will overwrite this value if a customer chooses a specific address
4393 if (!$_SESSION['QUOTE']->getBillingAddress()->getCountryName()) {
4394 $_SESSION['QUOTE']->getBillingAddress()->setCountryByName(GetConfig('CompanyCountry'));
4395 }
4396 if (!$_SESSION['QUOTE']->getIsSplitShipping() && !$_SESSION['QUOTE']->getShippingAddress()->getCountryName()) {
4397 $_SESSION['QUOTE']->getShippingAddress()->setCountryByName(GetConfig('CompanyCountry'));
4398 }
4399 }
4400 }
4401 }
4402
4403 $initialized = true;
4404 return $_SESSION['QUOTE'];
4405 }
4406
4407 /**
4408 * Determine, and get the type of portable device for a particular user agent.
4409 * If no agent is supplied, the function will attempt to take the value
4410 * of HTTP_USER_AGENT.
4411 *
4412 * The returned mobile device array will contain a category (mobile or tablet)
4413 * as well as device (iphone, ipad, etc)
4414 *
4415 * @param string $userAgent User agent to determine type of mobile device.
4416 * @return false|array False when agent is not a mobile device. Array when is.
4417 */
4418 function getPortableDeviceType($userAgent = '')
4419 {
4420 if(empty($userAgent) && !empty($_SERVER['HTTP_USER_AGENT'])) {
4421 $userAgent = $_SERVER['HTTP_USER_AGENT'];
4422 }
4423
4424 if(empty($userAgent)) {
4425 return false;
4426 }
4427
4428 // Webkit based mobile and tablet devices
4429 if(stripos($userAgent, 'webkit') !== false) {
4430 if(stripos($userAgent, 'iphone') !== false) {
4431 return array(
4432 'category' => 'phone',
4433 'device' => 'iphone'
4434 );
4435 }
4436 else if(stripos($userAgent, 'ipod') !== false) {
4437 return array(
4438 'category' => 'phone',
4439 'device' => 'ipod'
4440 );
4441 }
4442 else if(stripos($userAgent, 'ipad') !== false) {
4443 return array(
4444 'category' => 'tablet',
4445 'device' => 'ipad'
4446 );
4447 }
4448 else if(stripos($userAgent, 'android') !== false) {
4449 return array(
4450 'category' => 'phone',
4451 'device' => 'android'
4452 );
4453 }
4454 else if(stripos($userAgent, 'webos') !== false && stripos($userAgent, 'pre') !== false) {
4455 return array(
4456 'category' => 'phone',
4457 'device' => 'pre'
4458 );
4459 }
4460 }
4461
4462 return false;
4463 }
4464
4465 /**
4466 * Replaces all non ASCII characters by a separator.
4467 * Useful for creating a safe and valid webpath/filename.
4468 *
4469 * @param string $text The input text to modify
4470 * @return string
4471 */
4472 function slugify($text, $separator='-')
4473 {
4474 $text = preg_replace('/[^a-z0-9.]/i', ' ', strtolower($text));
4475 $text = preg_replace('/[\s]+/', ' ', $text);
4476 $text = trim(str_replace(' ', $separator, $text));
4477
4478 return $text;
4479 }
4480
4481 function canViewMobileSite()
4482 {
4483 $mobileDevice = getPortableDeviceType();
4484 if($mobileDevice && getConfig('enableMobileTemplate') && in_array($mobileDevice['device'], getConfig('enableMobileTemplateDevices'))) {
4485 return true;
4486 }
4487
4488 return false;
4489 }
4490
4491 /**
4492 * Deletes the specified directory and all of it's contents. Use with caution as there is no confirmation within
4493 * this method.
4494 *
4495 * @param string $path the path to the directory to delete
4496 * @return bool false if some of the directory's contents could not be deleted or if the specified path is not a
4497 * directory, true otherwise
4498 */
4499 function recursiveDeleteDirectory ($path)
4500 {
4501 if (!is_dir($path)) {
4502 return false;
4503 }
4504
4505 // begin a recursive directory iterator which will list files first, before the directory
4506 $objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::CHILD_FIRST);
4507 foreach ($objects as $name => /** @var SplFileInfo */$object){
4508 // use either unlink or rmdir depending on whether this is a directory or file, and abort if either fail
4509
4510 if ($object->isDir()) {
4511 if (!rmdir($object->getPathname())) {
4512 return false;
4513 }
4514 continue;
4515 }
4516
4517 if (!unlink($object->getPathname())) {
4518 return false;
4519 }
4520 }
4521
4522 // finally, delete the specific path itself
4523 return rmdir($path);
4524 }