1<?php 2/** 3 * basic functions used by zenphoto 4 * 5 * @package core 6 * @subpackage functions\functions-main 7 * 8 */ 9// force UTF-8 Ø 10 11global $_zp_current_context_stack, $_zp_HTML_cache; 12 13if (!function_exists("json_encode")) { 14 // load the drop-in replacement library 15 require_once(dirname(__FILE__) . '/lib-json.php'); 16} 17 18require_once(dirname(__FILE__) . '/functions-basic.php'); 19require_once(dirname(__FILE__) . '/functions-filter.php'); 20require_once(SERVERPATH . '/' . ZENFOLDER . '/lib-kses.php'); 21require_once dirname(__FILE__) . '/lib-htmLawed.php'; 22 23$_zp_captcha = new _zp_captcha(); // this will be overridden by the plugin if enabled. 24$_zp_HTML_cache = new _zp_HTML_cache(); // this will be overridden by the plugin if enabled. 25//setup session before checking for logon cookie 26require_once(dirname(__FILE__) . '/functions-i18n.php'); 27 28if (GALLERY_SESSION) { 29 zp_session_start(); 30} 31 32define('ZENPHOTO_LOCALE', setMainDomain()); 33 34require_once(dirname(__FILE__) . '/load_objectClasses.php'); 35 36$_zp_current_context_stack = array(); 37 38$_zp_albumthumb_selector = array(array('field' => '', 'direction' => '', 'desc' => 'random'), 39 array('field' => 'id', 'direction' => 'DESC', 'desc' => gettext('most recent')), 40 array('field' => 'mtime', 'direction' => '', 'desc' => gettext('oldest')), 41 array('field' => 'title', 'direction' => '', 'desc' => gettext('first alphabetically')), 42 array('field' => 'hitcounter', 'direction' => 'DESC', 'desc' => gettext('most viewed')) 43); 44 45$_zp_missing_album = new AlbumBase(gettext('missing'), false); 46$_zp_missing_image = new Transientimage($_zp_missing_album, SERVERPATH . '/' . ZENFOLDER . '/images/err-imagenotfound.png'); 47 48if (extensionEnabled('zenpage')) { 49 if (getOption('enabled-zenpage-items') == 'news-and-pages' || getOption('enabled-zenpage-items') == 'news') { 50 define('ZP_NEWS_ENABLED', true); 51 } else { 52 define('ZP_NEWS_ENABLED', false); 53 } 54 if (getOption('enabled-zenpage-items') == 'news-and-pages' || getOption('enabled-zenpage-items') == 'pages') { 55 define('ZP_PAGES_ENABLED', true); 56 } else { 57 define('ZP_PAGES_ENABLED', false); 58 } 59} else { 60 define('ZP_NEWS_ENABLED', false); 61 define('ZP_PAGES_ENABLED', false); 62} 63 64zp_register_filter('content_macro', 'getCookieInfoMacro'); 65 66/** 67 * parses the allowed HTML tags for use by htmLawed 68 * 69 * @param string &$source by name, contains the string with the tag options 70 * @return array the allowed_tags array. 71 * @since 1.1.3 72 * */ 73function parseAllowedTags(&$source) { 74 $source = trim($source); 75 if (substr($source, 0, 1) != "(") { 76 return false; 77 } 78 $source = substr($source, 1); //strip off the open paren 79 $a = array(); 80 while ((strlen($source) > 1) && (substr($source, 0, 1) != ")")) { 81 $i = strpos($source, '=>'); 82 if ($i === false) { 83 return false; 84 } 85 $tag = trim(substr($source, 0, $i)); 86 //strip forbidden tags from list 87 if ($tag == 'script') { 88 return 0; 89 } 90 $source = trim(substr($source, $i + 2)); 91 if (substr($source, 0, 1) != "(") { 92 return false; 93 } 94 $x = parseAllowedTags($source); 95 if ($x === false) { 96 return false; 97 } 98 $a[$tag] = $x; 99 } 100 if (substr($source, 0, 1) != ')') { 101 return false; 102 } 103 $source = trim(substr($source, 1)); //strip the close paren 104 return $a; 105} 106 107/** 108 * Search for a thumbnail for the image 109 * 110 * @param $localpath local path of the image 111 * @return string 112 */ 113function checkObjectsThumb($localpath) { 114 global $_zp_supported_images; 115 $image = stripSuffix($localpath); 116 $candidates = safe_glob($image . '.*'); 117 foreach ($candidates as $file) { 118 $ext = substr($file, strrpos($file, '.') + 1); 119 if (in_array(strtolower($ext), $_zp_supported_images)) { 120 return basename($image . '.' . $ext); 121 } 122 } 123 return NULL; 124} 125 126/** 127 * Returns a truncated string 128 * 129 * @param string $string souirce string 130 * @param int $length how long it should be 131 * @param string $elipsis the text to tack on indicating shortening 132 * @return string 133 */ 134function truncate_string($string, $length, $elipsis = '...') { 135 if (mb_strlen($string) > $length) { 136 $string = mb_substr($string, 0, $length); 137 $pos = mb_strrpos(strtr($string, array('~' => ' ', '!' => ' ', '@' => ' ', '#' => ' ', '$' => ' ', '%' => ' ', '^' => ' ', '&' => ' ', '*' => ' ', '(' => ' ', ')' => ' ', '+' => ' ', '=' => ' ', '-' => ' ', '{' => ' ', '}' => ' ', '[' => ' ', ']' => ' ', '|' => ' ', ':' => ' ', ';' => ' ', '<' => ' ', '>' => ' ', '.' => ' ', '?' => ' ', '/' => ' ', '\\', '\\' => ' ', "'" => ' ', "`" => ' ', '"' => ' ')), ' '); 138 if ($pos === FALSE) { 139 $string .= $elipsis; 140 } else { 141 $string = mb_substr($string, 0, $pos) . $elipsis; 142 } 143 } 144 return $string; 145} 146 147/** 148 * Fixes unbalanced HTML tags. Uses the library htmlawed or if available the native PHP extension tidy 149 * 150 * @param string $html 151 * @return string 152 */ 153function tidyHTML($html) { 154 if (class_exists('tidy')) { 155 $options = array( 156 'new-blocklevel-tags' => 'article aside audio bdi canvas details dialog figcaption figure footer header main nav section source summary template track video', 157 'new-empty-tags' => 'command embed keygen source track wbr', 158 'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr srcset sizes', 159 'show-body-only' => true, 160 'indent' => true, 161 'wrap' => 0 162 ); 163 $tidy = new tidy(); 164 $tidy->parseString($html, $options, 'utf8'); 165 $tidy->cleanRepair(); 166 return trim($tidy); 167 } else { 168 return trim(htmLawed($html, array('tidy' => '2s2n'))); 169 } 170} 171 172/** 173 * Returns truncated html formatted content 174 * 175 * @param string $articlecontent the source string 176 * @param int $shorten new size 177 * @param string $shortenindicator 178 * @param bool $forceindicator set to true to include the indicator no matter what 179 * @return string 180 */ 181function shortenContent($articlecontent, $shorten, $shortenindicator, $forceindicator = false) { 182 global $_user_tags; 183 if ($shorten && ($forceindicator || (mb_strlen($articlecontent) > $shorten))) { 184 $allowed_tags = getAllowedTags('allowed_tags'); 185 $articlecontent = html_decode($articlecontent); 186 //remove script to be replaced later 187 $articlecontent = preg_replace('~<script.*?/script>~is', '', $articlecontent); 188 189 //remove HTML comments 190 $articlecontent = preg_replace('~<!--.*?-->~is', '', $articlecontent); 191 $short = mb_substr($articlecontent, 0, $shorten); 192 $short2 = kses($short . '</p>', $allowed_tags); 193 194 if (($l2 = mb_strlen($short2)) < $shorten) { 195 $c = 0; 196 $l1 = $shorten; 197 $delta = $shorten - $l2; 198 while ($l2 < $shorten && $c++ < 5) { 199 $open = mb_strrpos($short, '<'); 200 if ($open > mb_strrpos($short, '>')) { 201 $l1 = mb_strpos($articlecontent, '>', $l1 + 1) + $delta; 202 } else { 203 $l1 = $l1 + $delta; 204 } 205 $short = mb_substr($articlecontent, 0, $l1); 206 preg_match_all('/(<p>)/', $short, $open); 207 preg_match_all('/(<\/p>)/', $short, $close); 208 if (count($open) > count($close)) 209 $short .= '</p>'; 210 $short2 = kses($short, $allowed_tags); 211 $l2 = mb_strlen($short2); 212 } 213 $shorten = $l1; 214 } 215 $short = truncate_string($articlecontent, $shorten, ''); 216 if ($short != $articlecontent || $forceindicator) { // we actually did remove some stuff 217 // drop open tag strings 218 $open = mb_strrpos($short, '<'); 219 if ($open > mb_strrpos($short, '>')) { 220 $short = mb_substr($short, 0, $open); 221 } 222 $short = tidyHTML($short . $shortenindicator); 223 } 224 $articlecontent = $short; 225 } 226 if (isset($matches)) { 227 //replace the script text 228 foreach ($matches[0] as $script) { 229 $articlecontent = $script . $articlecontent; 230 } 231 } 232 return $articlecontent; 233} 234 235/** 236 * Returns the oldest ancestor of an alubm; 237 * 238 * @param string $album an album object 239 * @return object 240 */ 241function getUrAlbum($album) { 242 if (!is_object($album)) 243 return NULL; 244 while (true) { 245 $parent = $album->getParent(); 246 if (is_null($parent)) { 247 return $album; 248 } 249 $album = $parent; 250 } 251} 252 253/** 254 * Returns a sort field part for querying 255 * Note: $sorttype may be a comma separated list of field names. If so, 256 * these are peckmarked and returned otherwise unchanged. 257 * 258 * @param string $sorttype the 'Display" name of the sort 259 * @param string $default the default if $sorttype is empty 260 * @param string $table the database table being used. 261 * @return string 262 */ 263function lookupSortKey($sorttype, $default, $table) { 264 global $_zp_fieldLists; 265 switch (strtolower($sorttype)) { 266 case 'random': 267 return 'RAND()'; 268 case "manual": 269 return '`sort_order`'; 270 case "filename": 271 switch ($table) { 272 case 'images': 273 return '`filename`'; 274 case 'albums': 275 return '`folder`'; 276 } 277 default: 278 if (empty($sorttype)) { 279 return '`' . $default . '`'; 280 } 281 if (substr($sorttype, 0) == '(') { 282 return $sorttype; 283 } 284 if (is_array($_zp_fieldLists) && isset($_zp_fieldLists[$table])) { 285 $dbfields = $_zp_fieldLists[$table]; 286 } else { 287 $result = db_list_fields($table); 288 $dbfields = array(); 289 if ($result) { 290 foreach ($result as $row) { 291 $dbfields[strtolower($row['Field'])] = $row['Field']; 292 } 293 } 294 $_zp_fieldLists[$table] = $dbfields; 295 } 296 $sorttype = strtolower($sorttype); 297 $list = explode(',', $sorttype); 298 foreach ($list as $key => $field) { 299 if (array_key_exists($field, $dbfields)) { 300 $list[$key] = '`' . trim($dbfields[$field]) . '`'; 301 } 302 } 303 return implode(',', $list); 304 } 305} 306 307/** 308 * Returns a formated date for output 309 * 310 * @param string $format the "strftime" format string 311 * @param date $dt the date to be output 312 * @return string 313 */ 314function zpFormattedDate($format, $dt) { 315 global $_zp_UTF8; 316 $fdate = strftime($format, $dt); 317 $charset = 'ISO-8859-1'; 318 $outputset = LOCAL_CHARSET; 319 if (function_exists('mb_internal_encoding')) { 320 if (($charset = mb_internal_encoding()) == $outputset) { 321 return $fdate; 322 } 323 } 324 return $_zp_UTF8->convert($fdate, $charset, $outputset); 325} 326 327/** 328 * Simple SQL timestamp formatting function. 329 * 330 * @param string $format formatting template 331 * @param int $mytimestamp timestamp 332 * @return string 333 */ 334function myts_date($format, $mytimestamp) { 335 $timezoneadjust = getOption('time_offset'); 336 337 $month = substr($mytimestamp, 4, 2); 338 $day = substr($mytimestamp, 6, 2); 339 $year = substr($mytimestamp, 0, 4); 340 341 $hour = substr($mytimestamp, 8, 2); 342 $min = substr($mytimestamp, 10, 2); 343 $sec = substr($mytimestamp, 12, 2); 344 345 $epoch = mktime($hour + $timezoneadjust, $min, $sec, $month, $day, $year); 346 $date = zpFormattedDate($format, $epoch); 347 return $date; 348} 349 350/** 351 * Send an mail to the mailing list. We also attempt to intercept any form injection 352 * attacks by slime ball spammers. Returns error message if send failure. 353 * 354 * @param string $subject The subject of the email. 355 * @param string $message The message contents of the email. 356 * @param array $email_list a list of email addresses to send to 357 * @param array $cc_addresses a list of addresses to send copies to. 358 * @param array $bcc_addresses a list of addresses to send blind copies to. 359 * @param string $replyTo reply-to address 360 * 361 * @return string 362 * 363 * @author Todd Papaioannou (lucky@luckyspin.org) 364 * @since 1.0.0 365 */ 366function zp_mail($subject, $message, $email_list = NULL, $cc_addresses = NULL, $bcc_addresses = NULL, $replyTo = NULL) { 367 global $_zp_authority, $_zp_gallery, $_zp_UTF8; 368 $result = ''; 369 if ($replyTo) { 370 $t = $replyTo; 371 if (!isValidEmail($m = array_shift($t))) { 372 if (empty($result)) { 373 $result = gettext('Mail send failed.'); 374 } 375 $result .= sprintf(gettext('Invalid “reply-to” mail address %s.'), $m); 376 } 377 } 378 if (is_null($email_list)) { 379 $email_list = $_zp_authority->getAdminEmail(); 380 } else { 381 foreach ($email_list as $key => $email) { 382 if (!isValidEmail($email)) { 383 unset($email_list[$key]); 384 if (empty($result)) { 385 $result = gettext('Mail send failed.'); 386 } 387 $result .= ' ' . sprintf(gettext('Invalid “to” mail address %s.'), $email); 388 } 389 } 390 } 391 if (is_null($cc_addresses)) { 392 $cc_addresses = array(); 393 } else { 394 if (empty($email_list) && !empty($cc_addresses)) { 395 if (empty($result)) { 396 $result = gettext('Mail send failed.'); 397 } 398 $result .= ' ' . gettext('“cc” list provided without “to” address list.'); 399 return $result; 400 } 401 foreach ($cc_addresses as $key => $email) { 402 if (!isValidEmail($email)) { 403 unset($cc_addresses[$key]); 404 if (empty($result)) { 405 $result = gettext('Mail send failed.'); 406 } 407 $result = ' ' . sprintf(gettext('Invalid “cc” mail address %s.'), $email); 408 } 409 } 410 } 411 if (is_null($bcc_addresses)) { 412 $bcc_addresses = array(); 413 } else { 414 foreach ($bcc_addresses as $key => $email) { 415 if (!isValidEmail($email)) { 416 unset($bcc_addresses[$key]); 417 if (empty($result)) { 418 $result = gettext('Mail send failed.'); 419 } 420 $result = ' ' . sprintf(gettext('Invalid “bcc” mail address %s.'), $email); 421 } 422 } 423 } 424 if (count($email_list) + count($bcc_addresses) > 0) { 425 if (zp_has_filter('sendmail')) { 426 427 $from_mail = getOption('site_email'); 428 $from_name = get_language_string(getOption('site_email_name')); 429 430 // Convert to UTF-8 431 if (LOCAL_CHARSET != 'UTF-8') { 432 $subject = $_zp_UTF8->convert($subject, LOCAL_CHARSET); 433 $message = $_zp_UTF8->convert($message, LOCAL_CHARSET); 434 } 435 436 // we do not support rich text 437 $message = preg_replace('~<p[^>]*>~', "\n", $message); // Replace the start <p> or <p attr=""> 438 $message = preg_replace('~</p>~', "\n", $message); // Replace the end 439 $message = preg_replace('~<br[^>]*>~', "\n", $message); // Replace <br> or <br ...> 440 $message = preg_replace('~<ol[^>]*>~', "", $message); // Replace the start <ol> or <ol attr=""> 441 $message = preg_replace('~</ol>~', "", $message); // Replace the end 442 $message = preg_replace('~<ul[^>]*>~', "", $message); // Replace the start <ul> or <ul attr=""> 443 $message = preg_replace('~</ul>~', "", $message); // Replace the end 444 $message = preg_replace('~<li[^>]*>~', ".\t", $message); // Replace the start <li> or <li attr=""> 445 $message = preg_replace('~</li>~', "", $message); // Replace the end 446 $message = getBare($message); 447 $message = preg_replace('~\n\n\n+~', "\n\n", $message); 448 449 // Send the mail 450 if (count($email_list) > 0) { 451 $result = zp_apply_filter('sendmail', '', $email_list, $subject, $message, $from_mail, $from_name, $cc_addresses, $replyTo); // will be true if all mailers succeeded 452 } 453 if (count($bcc_addresses) > 0) { 454 foreach ($bcc_addresses as $bcc) { 455 $result = zp_apply_filter('sendmail', '', array($bcc), $subject, $message, $from_mail, $from_name, array(), $replyTo); // will be true if all mailers succeeded 456 } 457 } 458 } else { 459 $result = gettext('Mail send failed. There is no mail handler configured.'); 460 } 461 } else { 462 if (empty($result)) { 463 $result = gettext('Mail send failed.'); 464 } 465 $result .= ' ' . gettext('No “to” address list provided.'); 466 } 467 return $result; 468} 469 470/** 471 * Sorts the results of a DB search by the current locale string for $field 472 * 473 * @param array $dbresult the result of the DB query 474 * @param string $field the field name to sort on 475 * @param bool $descending the direction of the sort 476 * @return array the sorted result 477 */ 478function sortByMultilingual($dbresult, $field, $descending) { 479 $temp = array(); 480 foreach ($dbresult as $key => $row) { 481 $temp[$key] = get_language_string($row[$field]); 482 } 483 sortArray($temp); 484 if ($descending) { 485 $temp = array_reverse($temp, true); 486 } 487 $result = array(); 488 foreach ($temp as $key => $v) { 489 $result[] = $dbresult[$key]; 490 } 491 return $result; 492} 493 494/** 495 * Checks to see access is allowed to an album 496 * Returns true if access is allowed. 497 * There is no password dialog--you must have already had authorization via a cookie. 498 * 499 * @param string $album album object or name of the album 500 * @param string &$hint becomes populated with the password hint. 501 * @return bool 502 */ 503function checkAlbumPassword($album, &$hint = NULL) { 504 global $_zp_pre_authorization, $_zp_gallery; 505 if (is_object($album)) { 506 $albumname = $album->name; 507 } else { 508 $album = newAlbum($albumname = $album, true, true); 509 } 510 if (isset($_zp_pre_authorization[$albumname])) { 511 return $_zp_pre_authorization[$albumname]; 512 } 513 $hash = $album->getPassword(); 514 if (empty($hash)) { 515 $album = $album->getParent(); 516 while (!is_null($album)) { 517 $hash = $album->getPassword(); 518 $authType = "zpcms_auth_album_" . $album->getID(); 519 $saved_auth = zp_getCookie($authType); 520 521 if (!empty($hash)) { 522 if ($saved_auth == $hash) { 523 $_zp_pre_authorization[$albumname] = $authType; 524 return $authType; 525 } else { 526 $hint = $album->getPasswordHint(); 527 return false; 528 } 529 } 530 $album = $album->getParent(); 531 } 532 // revert all tlhe way to the gallery 533 $hash = $_zp_gallery->getPassword(); 534 $authType = 'zpcms_auth_gallery'; 535 $saved_auth = zp_getCookie($authType); 536 if (empty($hash)) { 537 $authType = 'zp_public_access'; 538 } else { 539 if ($saved_auth != $hash) { 540 $hint = $_zp_gallery->getPasswordHint(); 541 return false; 542 } 543 } 544 } else { 545 $authType = "zpcms_auth_album_" . $album->getID(); 546 $saved_auth = zp_getCookie($authType); 547 if ($saved_auth != $hash) { 548 $hint = $album->getPasswordHint(); 549 return false; 550 } 551 } 552 $_zp_pre_authorization[$albumname] = $authType; 553 return $authType; 554} 555 556/** 557 * Returns a consolidated list of plugins 558 * The array structure is key=plugin name, value=plugin path 559 * 560 * @param string $pattern File system wildcard matching pattern to limit the search 561 * @param string $folder subfolder within the plugin folders to search 562 * @param bool $stripsuffix set to true to remove the suffix from the key name in the array 563 * @return array 564 */ 565function getPluginFiles($pattern, $folder = '', $stripsuffix = true) { 566 if (!empty($folder) && substr($folder, -1) != '/') 567 $folder .= '/'; 568 $list = array(); 569 $curdir = getcwd(); 570 $basepath = SERVERPATH . "/" . USER_PLUGIN_FOLDER . '/' . $folder; 571 if (is_dir($basepath)) { 572 chdir($basepath); 573 $filelist = safe_glob($pattern); 574 foreach ($filelist as $file) { 575 $key = filesystemToInternal($file); 576 if ($stripsuffix) { 577 $key = stripSuffix($key); 578 } 579 $list[$key] = $basepath . $file; 580 } 581 } 582 $basepath = SERVERPATH . "/" . ZENFOLDER . '/' . PLUGIN_FOLDER . '/' . $folder; 583 if (file_exists($basepath)) { 584 chdir($basepath); 585 $filelist = safe_glob($pattern); 586 foreach ($filelist as $file) { 587 $key = filesystemToInternal($file); 588 if ($stripsuffix) { 589 $key = stripSuffix($key); 590 } 591 $list[$key] = $basepath . $file; 592 } 593 } 594 chdir($curdir); 595 return $list; 596} 597 598/** 599 * Returns the fully qualified file name of the plugin file. 600 * 601 * Note: order of selection is: 602 * 1-theme folder file (if $inTheme is set) 603 * 2-user plugin folder file 604 * 3-zp-extensions file 605 * first file found is used 606 * 607 * @param string $plugin is the name of the plugin file, typically something.php 608 * @param bool $inTheme tells where to find the plugin. 609 * true means look in the current theme 610 * false means look in the zp-core/plugins folder. 611 * @param bool $webpath return a WEBPATH rather than a SERVERPATH 612 * 613 * @return string 614 */ 615function getPlugin($plugin, $inTheme = false, $webpath = false) { 616 global $_zp_gallery; 617 $plugin = ltrim($plugin, './\\'); 618 $pluginFile = NULL; 619 if ($inTheme === true) { 620 $inTheme = $_zp_gallery->getCurrentTheme(); 621 } 622 if ($inTheme) { 623 $pluginFile = '/' . THEMEFOLDER . '/' . internalToFilesystem($inTheme . '/' . $plugin); 624 if (!file_exists(SERVERPATH . $pluginFile)) { 625 $pluginFile = false; 626 } 627 } 628 if (!$pluginFile) { 629 $pluginFile = '/' . USER_PLUGIN_FOLDER . '/' . internalToFilesystem($plugin); 630 if (!file_exists(SERVERPATH . $pluginFile)) { 631 $pluginFile = '/' . ZENFOLDER . '/' . PLUGIN_FOLDER . '/' . internalToFilesystem($plugin); 632 if (!file_exists(SERVERPATH . $pluginFile)) { 633 $pluginFile = false; 634 } 635 } 636 } 637 if ($pluginFile) { 638 if ($webpath) { 639 if (is_string($webpath)) { 640 return $webpath . filesystemToInternal($pluginFile); 641 } else { 642 return WEBPATH . filesystemToInternal($pluginFile); 643 } 644 } else { 645 return SERVERPATH . $pluginFile; 646 } 647 } 648 return false; 649} 650 651/** 652 * Returns an array of the currently enabled plugins 653 * 654 * @return array 655 */ 656function getEnabledPlugins() { 657 global $_EnabledPlugins; 658 if (is_array($_EnabledPlugins)) { 659 return $_EnabledPlugins; 660 } 661 $_EnabledPlugins = array(); 662 $sortlist = getPluginFiles('*.php'); 663 foreach ($sortlist as $extension => $path) { 664 $opt = 'zp_plugin_' . $extension; 665 if ($option = getOption($opt)) { 666 $_EnabledPlugins[$extension] = array('priority' => $option, 'path' => $path); 667 } 668 } 669 $_EnabledPlugins = sortMultiArray($_EnabledPlugins, 'priority', true); 670 return $_EnabledPlugins; 671} 672 673/** 674 * Returns if a plugin is enabled 675 * @param string $extension 676 * @return bool 677 */ 678function extensionEnabled($extension) { 679 return getOption('zp_plugin_' . $extension); 680} 681 682/** 683 * Enables a plugin 684 * @param string $extension 685 * @param int $priority 686 * @param bool $persistent 687 */ 688function enableExtension($extension, $priority, $persistent = true) { 689 setOption('zp_plugin_' . $extension, $priority, $persistent); 690} 691 692/** 693 * Disables an extension 694 * @param string $extension 695 * @param bool $persistent 696 * 697 * @since ZenphotoCMS 1.5.2 698 */ 699function disableExtension($extension, $persistent = true) { 700 setOption('zp_plugin_' . $extension, 0, $persistent); 701} 702 703/** 704 * Gets an array of comments for the current admin 705 * 706 * @param int $number how many comments desired 707 * @return array 708 */ 709function fetchComments($number) { 710 if ($number) { 711 $limit = " LIMIT $number"; 712 } else { 713 $limit = ''; 714 } 715 716 $comments = array(); 717 if (zp_loggedin(ADMIN_RIGHTS | COMMENT_RIGHTS)) { 718 if (zp_loggedin(ADMIN_RIGHTS | MANAGE_ALL_ALBUM_RIGHTS)) { 719 $sql = "SELECT *, (date + 0) AS date FROM " . prefix('comments') . " ORDER BY id DESC$limit"; 720 $comments = query_full_array($sql); 721 } else { 722 $albumlist = getManagedAlbumList(); 723 $albumIDs = array(); 724 foreach ($albumlist as $albumname) { 725 $subalbums = getAllSubAlbumIDs($albumname); 726 foreach ($subalbums as $ID) { 727 $albumIDs[] = $ID['id']; 728 } 729 } 730 if (count($albumIDs) > 0) { 731 $sql = "SELECT *, (`date` + 0) AS date FROM " . prefix('comments') . " WHERE "; 732 733 $sql .= " (`type`='albums' AND ("; 734 $i = 0; 735 foreach ($albumIDs as $ID) { 736 if ($i > 0) { 737 $sql .= " OR "; 738 } 739 $sql .= "(" . prefix('comments') . ".ownerid=$ID)"; 740 $i++; 741 } 742 $sql .= ")) "; 743 $sql .= " ORDER BY id DESC$limit"; 744 $albumcomments = query($sql); 745 if ($albumcomments) { 746 while ($comment = db_fetch_assoc($albumcomments)) { 747 $comments[$comment['id']] = $comment; 748 } 749 db_free_result($albumcomments); 750 } 751 $sql = "SELECT *, " . prefix('comments') . ".id as id, " . 752 prefix('comments') . ".name as name, (" . prefix('comments') . ".date + 0) AS date, " . 753 prefix('images') . ".`albumid` as albumid," . 754 prefix('images') . ".`id` as imageid" . 755 " FROM " . prefix('comments') . "," . prefix('images') . " WHERE "; 756 757 $sql .= "(`type` IN (" . zp_image_types("'") . ") AND ("; 758 $i = 0; 759 foreach ($albumIDs as $ID) { 760 if ($i > 0) { 761 $sql .= " OR "; 762 } 763 $sql .= "(" . prefix('comments') . ".ownerid=" . prefix('images') . ".id AND " . prefix('images') . ".albumid=$ID)"; 764 $i++; 765 } 766 $sql .= "))"; 767 $sql .= " ORDER BY " . prefix('images') . ".`id` DESC$limit"; 768 $imagecomments = query($sql); 769 if ($imagecomments) { 770 while ($comment = db_fetch_assoc($imagecomments)) { 771 $comments[$comment['id']] = $comment; 772 } 773 db_free_result($imagecomments); 774 } 775 krsort($comments); 776 if ($number) { 777 if ($number < count($comments)) { 778 $comments = array_slice($comments, 0, $number); 779 } 780 } 781 } 782 } 783 } 784 return $comments; 785} 786 787/** 788 * Populates and returns the $_zp_admin_album_list array 789 * @return array 790 */ 791function getManagedAlbumList() { 792 global $_zp_admin_album_list, $_zp_current_admin_obj; 793 $_zp_admin_album_list = array(); 794 if (zp_loggedin(MANAGE_ALL_ALBUM_RIGHTS)) { 795 $sql = "SELECT `folder` FROM " . prefix('albums') . ' WHERE `parentid` IS NULL'; 796 $albums = query($sql); 797 if ($albums) { 798 while ($album = db_fetch_assoc($albums)) { 799 $_zp_admin_album_list[$album['folder']] = 32767; 800 } 801 db_free_result($albums); 802 } 803 } else { 804 if ($_zp_current_admin_obj) { 805 $_zp_admin_album_list = array(); 806 $objects = $_zp_current_admin_obj->getObjects(); 807 foreach ($objects as $object) { 808 if ($object['type'] == 'album') { 809 $_zp_admin_album_list[$object['data']] = $object['edit']; 810 } 811 } 812 } 813 } 814 return array_keys($_zp_admin_album_list); 815} 816 817/** 818 * Returns a list of album names managed by $id 819 * 820 * @param string $type which kind of object 821 * @param int $id admin ID 822 * @param bool $rights set true for album sub-rights 823 * @return array 824 */ 825function populateManagedObjectsList($type, $id, $rights = false) { 826 if ($id <= 0) { 827 return array(); 828 } 829 $cv = array(); 830 if (empty($type) || substr($type, 0, 5) == 'album') { 831 $sql = "SELECT " . prefix('albums') . ".`folder`," . prefix('albums') . ".`title`," . prefix('admin_to_object') . ".`edit` FROM " . prefix('albums') . ", " . 832 prefix('admin_to_object') . " WHERE " . prefix('admin_to_object') . ".adminid=" . $id . 833 " AND " . prefix('albums') . ".id=" . prefix('admin_to_object') . ".objectid AND " . prefix('admin_to_object') . ".type LIKE 'album%'"; 834 $currentvalues = query($sql, false); 835 if ($currentvalues) { 836 while ($albumitem = db_fetch_assoc($currentvalues)) { 837 $folder = $albumitem['folder']; 838 $name = get_language_string($albumitem['title']); 839 if ($type && !$rights) { 840 $cv[$name] = $folder; 841 } else { 842 $cv[] = array('data' => $folder, 'name' => $name, 'type' => 'album', 'edit' => $albumitem['edit'] + 0); 843 } 844 } 845 db_free_result($currentvalues); 846 } 847 } 848 if (empty($type) || $type == 'pages') { 849 $sql = 'SELECT ' . prefix('pages') . '.`title`,' . prefix('pages') . '.`titlelink` FROM ' . prefix('pages') . ', ' . 850 prefix('admin_to_object') . " WHERE " . prefix('admin_to_object') . ".adminid=" . $id . 851 " AND " . prefix('pages') . ".id=" . prefix('admin_to_object') . ".objectid AND " . prefix('admin_to_object') . ".type='pages'"; 852 $currentvalues = query($sql, false); 853 if ($currentvalues) { 854 while ($item = db_fetch_assoc($currentvalues)) { 855 if ($type) { 856 $cv[get_language_string($item['title'])] = $item['titlelink']; 857 } else { 858 $cv[] = array('data' => $item['titlelink'], 'name' => $item['title'], 'type' => 'pages'); 859 } 860 } 861 db_free_result($currentvalues); 862 } 863 } 864 if (empty($type) || $type == 'news') { 865 $sql = 'SELECT ' . prefix('news_categories') . '.`titlelink`,' . prefix('news_categories') . '.`title` FROM ' . prefix('news_categories') . ', ' . 866 prefix('admin_to_object') . " WHERE " . prefix('admin_to_object') . ".adminid=" . $id . 867 " AND " . prefix('news_categories') . ".id=" . prefix('admin_to_object') . ".objectid AND " . prefix('admin_to_object') . ".type='news'"; 868 $currentvalues = query($sql, false); 869 if ($currentvalues) { 870 while ($item = db_fetch_assoc($currentvalues)) { 871 if ($type) { 872 $cv[get_language_string($item['title'])] = $item['titlelink']; 873 } else { 874 $cv[] = array('data' => $item['titlelink'], 'name' => $item['title'], 'type' => 'news'); 875 } 876 } 877 db_free_result($currentvalues); 878 } 879 } 880 return $cv; 881} 882 883/** 884 * Returns an array of album ids whose parent is the folder 885 * @param string $albumfolder folder name if you want a album different >>from the current album 886 * @return array 887 */ 888function getAllSubAlbumIDs($albumfolder = '') { 889 global $_zp_current_album; 890 if (empty($albumfolder)) { 891 if (isset($_zp_current_album)) { 892 $albumfolder = $_zp_current_album->getFileName(); 893 } else { 894 return null; 895 } 896 } 897 $query = "SELECT `id`,`folder`, `show` FROM " . prefix('albums') . " WHERE `folder` LIKE " . db_quote(db_LIKE_escape($albumfolder) . '%'); 898 $subIDs = query_full_array($query); 899 return $subIDs; 900} 901 902/** 903 * recovers search parameters from stored cookie, clears the cookie 904 * 905 * @param string $what the page type 906 * @param string $album Name of the album 907 * @param string $image Name of the image 908 */ 909function handleSearchParms($what, $album = NULL, $image = NULL) { 910 global $_zp_current_search, $zp_request, $_zp_last_album, $_zp_current_album, 911 $_zp_current_zenpage_news, $_zp_current_zenpage_page, $_zp_gallery, $_zp_loggedin, $_zp_gallery_page; 912 $_zp_last_album = zp_getCookie('zpcms_search_lastalbum'); 913 if (is_object($zp_request) && get_class($zp_request) == 'SearchEngine') { // we are are on a search 914 zp_setCookie('zpcms_search_parent', 'searchresults'); 915 return $zp_request->getAlbumList(); 916 } 917 $params = zp_getCookie('zpcms_search_params'); 918 if (!empty($params)) { 919 $searchparent = zp_getCookie('zpcms_search_parent'); 920 $context = get_context(); 921 $_zp_current_search = new SearchEngine(); 922 $_zp_current_search->setSearchParams($params); 923 // check to see if we are still "in the search context" 924 if (!is_null($image)) { 925 $dynamic_album = $_zp_current_search->getDynamicAlbum(); 926 if ($_zp_current_search->getImageIndex($album->name, $image->filename) !== false) { 927 if ($dynamic_album) { 928 $_zp_current_album = $dynamic_album; 929 } 930 $context = $context | ZP_SEARCH_LINKED | ZP_IMAGE_LINKED; 931 } 932 } 933 if (!is_null($album)) { 934 $albumname = $album->name; 935 zp_setCookie('zpcms_search_lastalbum', $albumname); 936 if ($_zp_gallery_page == 'album.php') { 937 $searchparent = 'searchresults_album'; // so we know we are in an album search result so any of its images that are also results don't throw us out of context 938 } 939 if (hasDynamicAlbumSuffix($albumname) && !is_dir(ALBUM_FOLDER_SERVERPATH . $albumname)) { 940 $albumname = stripSuffix($albumname); // strip off the suffix as it will not be reflected in the search path 941 } 942 // see if the album is within the search context. NB for these purposes we need to look at all albums! 943 $save_logon = $_zp_loggedin; 944 $_zp_loggedin = $_zp_loggedin | VIEW_ALL_RIGHTS; 945 $search_album_list = $_zp_current_search->getAlbums(0); 946 $_zp_loggedin = $save_logon; 947 foreach ($search_album_list as $searchalbum) { 948 if (strpos($albumname, $searchalbum) !== false) { 949 if ($searchparent == 'searchresults_album') { 950 $context = $context | ZP_SEARCH_LINKED | ZP_ALBUM_LINKED; 951 } else { 952 $context = $context | ZP_SEARCH_LINKED | ZP_IMAGE_LINKED; 953 } 954 break; 955 } 956 } 957 zp_setCookie('zpcms_search_parent', $searchparent); 958 } else { 959 zp_clearCookie('zpcms_search_parent'); 960 zp_clearCookie('zpcms_search_lastalbum'); 961 } 962 if (!is_null($_zp_current_zenpage_page)) { 963 $pages = $_zp_current_search->getPages(); 964 if (!empty($pages)) { 965 $tltlelink = $_zp_current_zenpage_page->getTitlelink(); 966 foreach ($pages as $apage) { 967 if ($apage == $tltlelink) { 968 $context = $context | ZP_SEARCH_LINKED; 969 break; 970 } 971 } 972 } 973 } 974 if (!is_null($_zp_current_zenpage_news)) { 975 $news = $_zp_current_search->getArticles(0, NULL, true); 976 if (!empty($news)) { 977 $tltlelink = $_zp_current_zenpage_news->getTitlelink(); 978 foreach ($news as $anews) { 979 if ($anews['titlelink'] == $tltlelink) { 980 $context = $context | ZP_SEARCH_LINKED; 981 break; 982 } 983 } 984 } 985 } 986 if (($context & ZP_SEARCH_LINKED)) { 987 set_context($context); 988 } else { // not an object in the current search path 989 $_zp_current_search = null; 990 rem_context(ZP_SEARCH); 991 if (!isset($_REQUEST['preserve_search_params'])) { 992 zp_clearCookie("zpcms_search_params"); 993 } 994 } 995 } 996} 997 998/** 999 * Returns the number of album thumbs that go on a gallery page 1000 * 1001 * @return int 1002 */ 1003function galleryAlbumsPerPage() { 1004 return max(1, getOption('albums_per_page')); 1005} 1006 1007/** 1008 * Returns the theme folder 1009 * If there is an album theme, loads the theme options. 1010 * 1011 * @param object $album album object if override desired 1012 * 1013 * @return string 1014 */ 1015function setupTheme($album = NULL) { 1016 global $_zp_gallery, $_zp_current_album, $_zp_current_search, $_zp_themeroot; 1017 $albumtheme = ''; 1018 if (is_null($album)) { 1019 if (in_context(ZP_SEARCH_LINKED)) { 1020 if (!$album = $_zp_current_search->getDynamicAlbum()) { 1021 $album = $_zp_current_album; 1022 } 1023 } else { 1024 $album = $_zp_current_album; 1025 } 1026 } 1027 $theme = $_zp_gallery->getCurrentTheme(); 1028 $id = 0; 1029 if (!is_null($album)) { 1030 $parent = getUrAlbum($album); 1031 $albumtheme = $parent->getAlbumTheme(); 1032 if (!empty($albumtheme)) { 1033 $theme = $albumtheme; 1034 $id = $parent->getID(); 1035 } 1036 } 1037 $theme = zp_apply_filter('setupTheme', $theme); 1038 $_zp_gallery->setCurrentTheme($theme); 1039 $themeindex = getPlugin('index.php', $theme); 1040 if (empty($theme) || empty($themeindex)) { 1041 header('Last-Modified: ' . ZP_LAST_MODIFIED); 1042 header('Content-Type: text/html; charset=' . LOCAL_CHARSET); 1043 ?> 1044 <!DOCTYPE html> 1045 <html> 1046 <head> 1047 </head> 1048 <body> 1049 <strong><?php printf(gettext('Zenphoto found no theme scripts. Please check the <em>%s</em> folder of your installation.'), THEMEFOLDER); ?></strong> 1050 </body> 1051 </html> 1052 <?php 1053 exitZP(); 1054 } else { 1055 loadLocalOptions($id, $theme); 1056 $_zp_themeroot = WEBPATH . "/" . THEMEFOLDER . "/$theme"; 1057 } 1058 return $theme; 1059} 1060 1061/** 1062 * Returns an array of unique tag names 1063 * 1064 * @param bool $checkaccess Set to true if you wish to exclude tags that are assigned to items (or are not assigned at all) the visitor is not allowed to see 1065 * Beware that this may cause overhead on large sites. Usage of the static_html_cache plugin is strongely recommended. 1066 * @return array 1067 */ 1068function getAllTagsUnique($checkaccess = false) { 1069 global $_zp_unique_tags, $_zp_unique_tags_excluded; 1070 if (zp_loggedin(VIEW_ALL_RIGHTS)) { 1071 $checkaccess = false; 1072 } 1073 //need to cache all and filtered tags indiviually 1074 if ($checkaccess) { 1075 if (!is_null($_zp_unique_tags_excluded)) { 1076 return $_zp_unique_tags_excluded; // cache them. 1077 } 1078 } else { 1079 if (!is_null($_zp_unique_tags)) { 1080 return $_zp_unique_tags; // cache them. 1081 } 1082 } 1083 $all_unique_tags = array(); 1084 $sql = "SELECT DISTINCT `name`, `id` FROM " . prefix('tags') . ' ORDER BY `name`'; 1085 $unique_tags = query($sql); 1086 if ($unique_tags) { 1087 while ($tagrow = db_fetch_assoc($unique_tags)) { 1088 if ($checkaccess) { 1089 if (getTagCountByAccess($tagrow) != 0) { 1090 $all_unique_tags[] = $tagrow['name']; 1091 } 1092 } else { 1093 $all_unique_tags[] = $tagrow['name']; 1094 } 1095 } 1096 db_free_result($unique_tags); 1097 } 1098 if ($checkaccess) { 1099 $_zp_unique_tags_excluded = $all_unique_tags; 1100 return $_zp_unique_tags_excluded; 1101 } else { 1102 $_zp_unique_tags = $all_unique_tags; 1103 return $_zp_unique_tags; 1104 } 1105} 1106 1107/** 1108 * Returns an array indexed by 'tag' with the element value the count of the tag 1109 * 1110 * @param bool $exclude_unassigned Set to true if you wish to exclude tags that are not assigne to any item 1111 * @param bool $checkaccess Set to true if you wish to exclude tags that are assigned to items (or are not assigned at all) the visitor is not allowed to see 1112 * If set to true it overrides the $exclude_unassigned parameter. 1113 * Beware that this may cause overhead on large sites. Usage of the static_html_cache plugin is strongely recommended. 1114 * @return array 1115 */ 1116function getAllTagsCount($exclude_unassigned = false, $checkaccess = false) { 1117 global $_zp_count_tags; 1118 if (!is_null($_zp_count_tags)) { 1119 return $_zp_count_tags; 1120 } 1121 if (zp_loggedin(VIEW_ALL_RIGHTS)) { 1122 $exclude_unassigned = false; 1123 $checkaccess = false; 1124 } 1125 $_zp_count_tags = array(); 1126 $sql = "SELECT DISTINCT tags.name, tags.id, (SELECT COUNT(*) FROM " . prefix('obj_to_tag') . " as object WHERE object.tagid = tags.id) AS count FROM " . prefix('tags') . " as tags ORDER BY `name`"; 1127 $tagresult = query($sql); 1128 if ($tagresult) { 1129 while ($tag = db_fetch_assoc($tagresult)) { 1130 if ($checkaccess) { 1131 $count = getTagCountByAccess($tag); 1132 if ($count != 0) { 1133 $_zp_count_tags[$tag['name']] = $count; 1134 } 1135 } else { 1136 if ($exclude_unassigned) { 1137 if ($tag['count'] != 0) { 1138 $_zp_count_tags[$tag['name']] = $tag['count']; 1139 } 1140 } else { 1141 $_zp_count_tags[$tag['name']] = $tag['count']; 1142 } 1143 } 1144 } 1145 db_free_result($tagresult); 1146 } 1147 return $_zp_count_tags; 1148} 1149 1150/** 1151 * Checks if a tag is assigned at all and if it can be viewed by the current visitor and returns the corrected count 1152 * Helper function used optionally within getAllTagsCount() and getAllTagsUnique() 1153 * 1154 * @global obj $_zp_zenpage 1155 * @param array $tag Array representing a tag containing at least its name and id 1156 * @return int 1157 */ 1158function getTagCountByAccess($tag) { 1159 global $_zp_zenpage, $_zp_object_to_tags; 1160 if (array_key_exists('count', $tag) && $tag['count'] == 0) { 1161 return $tag['count']; 1162 } 1163 $hidealbums = getNotViewableAlbums(); 1164 $hideimages = getNotViewableImages(); 1165 $hidenews = array(); 1166 $hidepages = array(); 1167 if (extensionEnabled('Zenpage')) { 1168 $hidenews = $_zp_zenpage->getNotViewableNews(); 1169 $hidepages = $_zp_zenpage->getNotViewablePages(); 1170 } 1171 //skip checks if there are no unviewable items at all 1172 if (empty($hidealbums) && empty($hideimages) && empty($hidenews) && empty($hidepages)) { 1173 if (array_key_exists('count', $tag)) { 1174 return $tag['count']; 1175 } 1176 return 0; 1177 } 1178 if (is_null($_zp_object_to_tags)) { 1179 $sql = "SELECT tagid, type, objectid FROM " . prefix('obj_to_tag') . " ORDER BY tagid"; 1180 $_zp_object_to_tags = query_full_array($sql); 1181 } 1182 $count = ''; 1183 if ($_zp_object_to_tags) { 1184 foreach ($_zp_object_to_tags as $tagcheck) { 1185 if ($tagcheck['tagid'] == $tag['id']) { 1186 switch ($tagcheck['type']) { 1187 case 'albums': 1188 if (!in_array($tagcheck['objectid'], $hidealbums)) { 1189 $count++; 1190 } 1191 break; 1192 case 'images': 1193 if (!in_array($tagcheck['objectid'], $hideimages)) { 1194 $count++; 1195 } 1196 break; 1197 case 'news': 1198 if (ZP_NEWS_ENABLED) { 1199 if (!in_array($tagcheck['objectid'], $hidenews)) { 1200 $count++; 1201 } 1202 } 1203 break; 1204 case 'pages': 1205 if (ZP_PAGES_ENABLED) { 1206 if (!in_array($tagcheck['objectid'], $hidepages)) { 1207 $count++; 1208 } 1209 } 1210 break; 1211 } 1212 } 1213 } 1214 } 1215 if (empty($count)) { 1216 $count = 0; 1217 } 1218 return $count; 1219} 1220 1221/** 1222 * Stores tags for an object 1223 * 1224 * @param array $tags the tag values 1225 * @param int $id the record id of the album/image 1226 * @param string $tbl database table of the object 1227 */ 1228function storeTags($tags, $id, $tbl) { 1229 if ($id) { 1230 $tagsLC = array(); 1231 foreach ($tags as $key => $tag) { 1232 $tag = trim($tag); 1233 if (!empty($tag)) { 1234 $lc_tag = mb_strtolower($tag); 1235 if (!in_array($lc_tag, $tagsLC)) { 1236 $tagsLC[$tag] = $lc_tag; 1237 } 1238 } 1239 } 1240 $sql = "SELECT `id`, `tagid` from " . prefix('obj_to_tag') . " WHERE `objectid`='" . $id . "' AND `type`='" . $tbl . "'"; 1241 $result = query($sql); 1242 $existing = array(); 1243 if ($result) { 1244 while ($row = db_fetch_assoc($result)) { 1245 $dbtag = query_single_row("SELECT `name` FROM " . prefix('tags') . " WHERE `id`='" . $row['tagid'] . "'"); 1246 $existingLC = mb_strtolower($dbtag['name']); 1247 if (in_array($existingLC, $tagsLC)) { // tag already set no action needed 1248 $existing[] = $existingLC; 1249 } else { // tag no longer set, remove it 1250 query("DELETE FROM " . prefix('obj_to_tag') . " WHERE `id`='" . $row['id'] . "'"); 1251 } 1252 } 1253 db_free_result($result); 1254 } 1255 $tags = array_diff($tagsLC, $existing); // new tags for the object 1256 foreach ($tags as $key => $tag) { 1257 $dbtag = query_single_row("SELECT `id` FROM " . prefix('tags') . " WHERE `name`=" . db_quote($key)); 1258 if (!is_array($dbtag)) { // tag does not exist 1259 query("INSERT INTO " . prefix('tags') . " (name) VALUES (" . db_quote($key) . ")", false); 1260 $dbtag = array('id' => db_insert_id()); 1261 } 1262 query("INSERT INTO " . prefix('obj_to_tag') . "(`objectid`, `tagid`, `type`) VALUES (" . $id . "," . $dbtag['id'] . ",'" . $tbl . "')"); 1263 } 1264 } 1265} 1266 1267/** 1268 * Retrieves the tags for an object 1269 * Returns them in an array 1270 * 1271 * @param int $id the record id of the album/image 1272 * @param string $tbl 'albums' or 'images', etc. 1273 * @return unknown 1274 */ 1275function readTags($id, $tbl) { 1276 $tags = array(); 1277 $result = query("SELECT `tagid` FROM " . prefix('obj_to_tag') . " WHERE `type`='" . $tbl . "' AND `objectid`='" . $id . "'"); 1278 if ($result) { 1279 while ($row = db_fetch_assoc($result)) { 1280 $dbtag = query_single_row("SELECT `name` FROM" . prefix('tags') . " WHERE `id`='" . $row['tagid'] . "'"); 1281 if ($dbtag) { 1282 $tags[] = $dbtag['name']; 1283 } 1284 } 1285 db_free_result($result); 1286 } 1287 sortArray($tags); 1288 return $tags; 1289} 1290 1291/** 1292 * Creates the body of a select list 1293 * 1294 * @param array $currentValue list of items to be flagged as checked 1295 * @param array $list the elements of the select list 1296 * @param bool $descending set true for a ascending order sort. Set to null to keep the array as it is passed. 1297 * @param bool $localize set true if the keys as description should be listed instead of the plain values 1298 */ 1299function generateListFromArray($currentValue, $list, $descending, $localize) { 1300 if ($localize) { 1301 $list = array_flip($list); 1302 if (!is_null($descending)) { 1303 if ($descending) { 1304 sortArray($list, true); 1305 } else { 1306 sortArray($list); 1307 } 1308 } 1309 $list = array_flip($list); 1310 } else { 1311 if (!is_null($descending)) { 1312 if ($descending) { 1313 sortArray($list, true); 1314 } else { 1315 sortArray($list); 1316 } 1317 } 1318 } 1319 1320 foreach ($list as $key => $item) { 1321 echo '<option value="' . html_encode($item) . '"'; 1322 if (in_array($item, $currentValue)) { 1323 echo ' selected="selected"'; 1324 } 1325 if ($localize) { 1326 $display = $key; 1327 } else { 1328 $display = $item; 1329 } 1330 echo '>' . $display . "</option>" . "\n"; 1331 } 1332} 1333 1334/** 1335 * Generates a selection list from files found on disk 1336 * 1337 * @param strig $currentValue the current value of the selector 1338 * @param string $root directory path to search 1339 * @param string $suffix suffix to select for 1340 * @param bool $descending set true to get a reverse order sort. Set to null to keep the array as it is passed. 1341 */ 1342function generateListFromFiles($currentValue, $root, $suffix, $descending = false) { 1343 if (is_dir($root)) { 1344 $curdir = getcwd(); 1345 chdir($root); 1346 $filelist = safe_glob('*' . $suffix); 1347 $list = array(); 1348 foreach ($filelist as $file) { 1349 $file = str_replace($suffix, '', $file); 1350 $list[] = filesystemToInternal($file); 1351 } 1352 generateListFromArray(array($currentValue), $list, $descending, false); 1353 chdir($curdir); 1354 } 1355} 1356 1357/** 1358 * Helper to generate attributename="attributevalue" for HTML elements based on an array 1359 * consisting of key => value pairs 1360 * 1361 * Returns a string with with prependend space to directly use with an HTML element. 1362 * 1363 * Note: 1364 * There is no check if these attributes are valid. Also values are not html_encoded as that could 1365 * break for example JS event handlers. Do this in your attribute definition array as needed. 1366 * Attributes with an empty value are skipped except the alt attribute or known boolean attributes (see in function definition) 1367 * 1368 * @since ZenphotoCMS 1.5.8 1369 * @param array $attributes key => value pairs of element attribute name and value. e.g. array('class' => 'someclass', 'id' => 'someid'); 1370 * @param array $exclude Names of attributes to exclude (in case already set otherwise) 1371 * @return string 1372 */ 1373function generateAttributesFromArray($attributes = array(), $exclude = array()) { 1374 $boolean_attr = array( 1375 'allowfullscreen', 1376 'allowpaymentrequest', 1377 'async', 1378 'autofocus', 1379 'autoplay', 1380 'checked', 1381 'controls', 1382 'default', 1383 'disabled', 1384 'formnovalidate', 1385 'hidden', 1386 'ismap', 1387 'itemscope', 1388 'loop', 1389 'multiple', 1390 'muted', 1391 'nomodule', 1392 'novalidate', 1393 'open', 1394 'playsinline', 1395 'readonly', 1396 'required', 1397 'reversed', 1398 'selected', 1399 'truespeed' 1400 ); 1401 $attr = ''; 1402 if (!empty($attributes) && is_array($attributes)) { 1403 foreach ($attributes as $key => $val) { 1404 if (!in_array($key, $exclude)) { 1405 if (empty($val)) { 1406 if (in_array($key, $boolean_attr)) { 1407 $attr .= ' ' . $key; 1408 } else if ($key == 'alt') { 1409 $attr .= ' ' . $key . '=""'; 1410 } 1411 } else { 1412 $attr .= ' ' . $key . '="' . $val . '"'; 1413 } 1414 } 1415 } 1416 } 1417 return $attr; 1418} 1419 1420/** 1421 * @param string $url The link URL 1422 * @param string $text The text to go with the link 1423 * @param string $title Text for the title tag 1424 * @param string $class optional class 1425 * @param string $id optional id 1426 * @param array $extra_attr Additional attributes as array of key => value pairs 1427 */ 1428function getLinkHTML($url, $text, $title = NULL, $class = NULL, $id = NULL, $extra_attr = array()) { 1429 $attr = array( 1430 'href' => html_encode($url), 1431 'title' => html_encode(getBare($title)), 1432 'class' => $class, 1433 'id' => $id 1434 ); 1435 $attr_final = array_merge($attr, $extra_attr); 1436 $attributes = generateAttributesFromArray($attr_final); 1437 return '<a' . $attributes . '>' . html_encode($text) . '</a>'; 1438} 1439 1440/** 1441 * General link printing function 1442 * @param string $url The link URL 1443 * @param string $text The text to go with the link 1444 * @param string $title Text for the title tag 1445 * @param string $class optional class 1446 * @param string $id optional id 1447 */ 1448function printLinkHTML($url, $text, $title = NULL, $class = NULL, $id = NULL) { 1449 echo getLinkHTML($url, $text, $title, $class, $id); 1450} 1451 1452/** 1453 * shuffles an array maintaining the keys 1454 * 1455 * @param array $array 1456 * @return boolean 1457 */ 1458function shuffle_assoc(&$array) { 1459 $keys = array_keys($array); 1460 shuffle($keys); 1461 foreach ($keys as $key) { 1462 $new[$key] = $array[$key]; 1463 } 1464 $array = $new; 1465 return true; 1466} 1467 1468/** 1469 * sorts the found albums (images) by the required key(s) 1470 * 1471 * NB: this sort is sensitive to the key(s) chosen and makes 1472 * the appropriate sorts based on same. Some multi-key sorts 1473 * will not make any sense and will give unexpected results. 1474 * Most notably any that contain the keys "title" or "desc" 1475 * as these require multi-lingual sorts. 1476 * 1477 * @param array $results 1478 * @param string $sortkey 1479 * @param string $order 1480 */ 1481function sortByKey($results, $sortkey, $order) { 1482 $sortkey = str_replace('`', '', $sortkey); 1483 switch ($sortkey) { 1484 case 'title': 1485 case 'desc': 1486 return sortByMultilingual($results, $sortkey, $order); 1487 case 'RAND()': 1488 shuffle($results); 1489 return $results; 1490 default: 1491 if (preg_match('`[\/\(\)\*\+\-!\^\%\<\>\=\&\|]`', $sortkey)) { 1492 return $results; // We cannot deal with expressions 1493 } 1494 } 1495 $indicies = explode(',', $sortkey); 1496 foreach ($indicies as $key => $index) { 1497 $indicies[$key] = trim($index); 1498 } 1499 $results = sortMultiArray($results, $indicies, $order, true, false, true); 1500 return $results; 1501} 1502 1503/** 1504 * multidimensional array column sort 1505 * 1506 * If the system's PHP has the native intl extension and its Collator class available 1507 * the sorting is locale aware (true natural order) and always case sensitive if $natsort is set to true 1508 * 1509 * @param array $array The multidimensional array to be sorted 1510 * @param mixed $index Which key(s) should be sorted by 1511 * @param string $descending true for descending sortorder 1512 * @param bool $natsort If natural order should be used. If available sorting will be locale aware. 1513 * @param bool $case_sensitive If the sort should be case sensitive. Note if $natsort is true and locale aware sorting is available sorting is always case sensitive 1514 * @param bool $preservekeys Default false, 1515 * @param array $remove_criteria Array of indices to remove. 1516 * @return array 1517 * 1518 * @author redoc (http://codingforums.com/showthread.php?t=71904) 1519 */ 1520function sortMultiArray($array, $index, $descending = false, $natsort = true, $case_sensitive = false, $preservekeys = false, $remove_criteria = array()) { 1521 if (is_array($array) && count($array) > 0) { 1522 if (is_array($index)) { 1523 $indicies = $index; 1524 } else { 1525 $indicies = array($index); 1526 } 1527 if ($descending) { 1528 $separator = '~~'; 1529 } else { 1530 $separator = ' '; 1531 } 1532 foreach ($array as $key => $row) { 1533 $temp[$key] = ''; 1534 foreach ($indicies as $index) { 1535 if (is_array($row) && array_key_exists($index, $row)) { 1536 $temp[$key] .= get_language_string($row[$index]) . $separator; 1537 if (in_array($index, $remove_criteria)) { 1538 unset($array[$key][$index]); 1539 } 1540 } 1541 } 1542 $temp[$key] .= $key; 1543 } 1544 sortArray($temp, $descending, $natsort, $case_sensitive); 1545 foreach (array_keys($temp) as $key) { 1546 if (!$preservekeys && is_numeric($key)) { 1547 $sorted[] = $array[$key]; 1548 } else { 1549 $sorted[$key] = $array[$key]; 1550 } 1551 } 1552 return $sorted; 1553 } 1554 return $array; 1555} 1556 1557/** 1558 * General one dimensional array sorting function. Key/value associations are preserved. 1559 * 1560 * If the system's PHP has the native intl extension and its Collator class available and $natsort is set to true 1561 * the sorting is locale sensitive (true natural order). 1562 * 1563 * The function follows native PHP array sorting functions (natcasesort() etc.) and uses the array by reference and returns true or false on success or failure. 1564 * 1565 * @since ZenphotoCMS 1.5.8 1566 * 1567 * @param array $array The array to sort. The array is passed by reference 1568 * @param string $descending true for descending sorts (default false) 1569 * @param bool $natsort If natural order should be used (default true). If available sorting will be locale sensitive. 1570 * @param bool $case_sensitive If the sort should be case sensitive (default false). Note if $natsort is true and locale aware sorting is available sorting is always case sensitive 1571 * @return boolean 1572 */ 1573function sortArray(&$array, $descending = false, $natsort = true, $case_sensitive = false) { 1574 $success = false; 1575 if (is_array($array) && count($array) > 0) { 1576 if ($natsort) { 1577 if (class_exists('collator')) { 1578 $locale = getUserLocale(); 1579 $collator = new Collator($locale); 1580 if ($case_sensitive) { 1581 $collator->setAttribute(Collator::CASE_FIRST, Collator::UPPER_FIRST); 1582 } 1583 $collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON); 1584 $success = $collator->asort($array, Collator::SORT_STRING); 1585 } else { 1586 if ($case_sensitive) { 1587 $success = natsort($array); 1588 } else { 1589 $success = natcasesort($array); 1590 } 1591 } 1592 if ($descending) { 1593 $array = array_reverse($array, true); 1594 } 1595 } else { 1596 if ($descending) { 1597 $success = arsort($array); 1598 } else { 1599 $success = asort($array); 1600 } 1601 } 1602 } 1603 return $success; 1604} 1605 1606/** 1607 * Returns a list of album IDs that the current viewer is not allowed to see 1608 * 1609 * @return array 1610 */ 1611function getNotViewableAlbums() { 1612 global $_zp_not_viewable_album_list; 1613 if (zp_loggedin(ADMIN_RIGHTS | MANAGE_ALL_ALBUM_RIGHTS)) 1614 return array(); //admins can see all 1615 if (is_null($_zp_not_viewable_album_list)) { 1616 $sql = 'SELECT `folder`, `id`, `password`, `show` FROM ' . prefix('albums') . ' WHERE `show`=0 OR `password`!=""'; 1617 $result = query($sql); 1618 if ($result) { 1619 $_zp_not_viewable_album_list = array(); 1620 while ($row = db_fetch_assoc($result)) { 1621 if (checkAlbumPassword($row['folder'])) { 1622 $album = newAlbum($row['folder']); 1623 if (!($row['show'] || $album->isMyItem(LIST_RIGHTS))) { 1624 $_zp_not_viewable_album_list[] = $row['id']; 1625 } 1626 } else { 1627 $_zp_not_viewable_album_list[] = $row['id']; 1628 } 1629 } 1630 db_free_result($result); 1631 } 1632 } 1633 return $_zp_not_viewable_album_list; 1634} 1635 1636/** 1637 * Returns a list of image IDs that the current viewer is not allowed to see 1638 * 1639 * @return array 1640 */ 1641function getNotViewableImages() { 1642 global $_zp_not_viewable_image_list; 1643 if (zp_loggedin(ADMIN_RIGHTS | MANAGE_ALL_ALBUM_RIGHTS)) { 1644 return array(); //admins can see all 1645 } 1646 $hidealbums = getNotViewableAlbums(); 1647 $where = ''; 1648 if (!is_null($hidealbums)) { 1649 $where = ' OR `albumid` in (' . implode(',', $hidealbums) . ')'; 1650 } 1651 if (is_null($_zp_not_viewable_image_list)) { 1652 $sql = 'SELECT DISTINCT `id` FROM ' . prefix('images') . ' WHERE `show` = 0' . $where; 1653 $result = query($sql); 1654 if ($result) { 1655 $_zp_not_viewable_image_list = array(); 1656 while ($row = db_fetch_assoc($result)) { 1657 $_zp_not_viewable_image_list[] = $row['id']; 1658 } 1659 } 1660 } 1661 return $_zp_not_viewable_image_list; 1662} 1663 1664/** 1665 * Checks to see if a URL is valid 1666 * 1667 * @param string $url the URL being checked 1668 * @return bool 1669 */ 1670function isValidURL($url) { 1671 if (filter_var($url, FILTER_VALIDATE_URL)) { 1672 return true; 1673 } 1674 /* 1675 * Above does not allow the newer UTF8 internation domain names. 1676 * @see Alexander Terehov https://github.com/terales/php-url-validation-example 1677 */ 1678 if (parse_url($url, PHP_URL_SCHEME) && parse_url($url, PHP_URL_HOST)) { 1679 return true; 1680 } 1681 return false; 1682} 1683 1684/** 1685 * pattern match function Works with characters with diacritical marks where the PHP one does not. 1686 * 1687 * @param string $pattern pattern 1688 * @param string $string haystack 1689 * @return bool 1690 */ 1691function safe_fnmatch($pattern, $string) { 1692 return @preg_match('/^' . strtr(addcslashes($pattern, '\\.+^$(){}=!<>|'), array('*' => '.*', '?' => '.?')) . '$/i', $string); 1693} 1694 1695/** 1696 * Returns true if the mail address passed is valid. 1697 * It uses PHP's internal `filter_var` functions to validate the syntax but not the existence. 1698 * 1699 * @since ZenphotoCMS 1.5.2 1700 * 1701 * @param string $email An email address 1702 * @return boolean 1703 */ 1704function isValidEmail($email) { 1705 if (filter_var($email, FILTER_VALIDATE_EMAIL)) { 1706 return true; 1707 } 1708 return false; 1709} 1710 1711/** 1712 * returns a list of comment record 'types' for "images" 1713 * @param string $quote quotation mark to use 1714 * 1715 * @return string 1716 */ 1717function zp_image_types($quote) { 1718 global $_zp_extra_filetypes; 1719 $typelist = $quote . 'images' . $quote . ',' . $quote . '_images' . $quote . ','; 1720 $types = array_unique($_zp_extra_filetypes); 1721 foreach ($types as $type) { 1722 $typelist .= $quote . strtolower($type) . 's' . $quote . ','; 1723 } 1724 return substr($typelist, 0, -1); 1725} 1726 1727/** 1728 1729 * Returns video argument of the current Image. 1730 * 1731 * @param object $image optional image object 1732 * @return bool 1733 */ 1734function isImageVideo($image = NULL) { 1735 if (is_null($image)) { 1736 if (!in_context(ZP_IMAGE)) 1737 return false; 1738 global $_zp_current_image; 1739 $image = $_zp_current_image; 1740 } 1741 return strtolower(get_class($image)) == 'video'; 1742} 1743 1744/** 1745 * Returns true if the image is a standard photo type 1746 * 1747 * @param object $image optional image object 1748 * @return bool 1749 */ 1750function isImagePhoto($image = NULL) { 1751 if (is_null($image)) { 1752 if (!in_context(ZP_IMAGE)) 1753 return false; 1754 global $_zp_current_image; 1755 $image = $_zp_current_image; 1756 } 1757 $class = strtolower(get_class($image)); 1758 return $class == 'image' || $class == 'transientimage'; 1759} 1760 1761/** 1762 * Copies a directory recursively 1763 * @param string $srcdir the source directory. 1764 * @param string $dstdir the destination directory. 1765 * @return the total number of files copied. 1766 */ 1767function dircopy($srcdir, $dstdir) { 1768 $num = 0; 1769 if (!is_dir($dstdir)) 1770 mkdir($dstdir); 1771 if ($curdir = opendir($srcdir)) { 1772 while ($file = readdir($curdir)) { 1773 if ($file != '.' && $file != '..') { 1774 $srcfile = $srcdir . '/' . $file; 1775 $dstfile = $dstdir . '/' . $file; 1776 if (is_file($srcfile)) { 1777 if (is_file($dstfile)) 1778 $ow = filemtime($srcfile) - filemtime($dstfile); 1779 else 1780 $ow = 1; 1781 if ($ow > 0) { 1782 if (copy($srcfile, $dstfile)) { 1783 touch($dstfile, filemtime($srcfile)); 1784 $num++; 1785 } 1786 } 1787 } else if (is_dir($srcfile)) { 1788 $num += dircopy($srcfile, $dstfile); 1789 } 1790 } 1791 } 1792 closedir($curdir); 1793 } 1794 return $num; 1795} 1796 1797/** 1798 * Returns a byte size from a size value (eg: 100M). 1799 * 1800 * @param int $bytes 1801 * @return string 1802 */ 1803function byteConvert($bytes) { 1804 if ($bytes <= 0) 1805 return gettext('0 Bytes'); 1806 $convention = 1024; //[1000->10^x|1024->2^x] 1807 $s = array('Bytes', 'kB', 'mB', 'GB', 'TB', 'PB', 'EB', 'ZB'); 1808 $e = floor(log($bytes, $convention)); 1809 return round($bytes / pow($convention, $e), 2) . ' ' . $s[$e]; 1810} 1811 1812/** 1813 * Converts a datetime to connoical form 1814 * 1815 * @param string $datetime input date/time string 1816 * @param bool $raw set to true to return the timestamp otherwise you get a string 1817 * @return mixed 1818 */ 1819function dateTimeConvert($datetime, $raw = false) { 1820 // Convert 'yyyy:mm:dd hh:mm:ss' to 'yyyy-mm-dd hh:mm:ss' for Windows' strtotime compatibility 1821 $datetime = preg_replace('/(\d{4}):(\d{2}):(\d{2})/', ' \1-\2-\3', $datetime); 1822 $time = strtotime($datetime); 1823 if ($time == -1 || $time === false) 1824 return false; 1825 if ($raw) 1826 return $time; 1827 return date('Y-m-d H:i:s', $time); 1828} 1829 1830/* * * Context Manipulation Functions ****** */ 1831/* * *************************************** */ 1832 1833/* Contexts are simply constants that tell us what variables are available to us 1834 * at any given time. They should be set and unset with those variables. 1835 */ 1836 1837function get_context() { 1838 global $_zp_current_context; 1839 return $_zp_current_context; 1840} 1841 1842function set_context($context) { 1843 global $_zp_current_context; 1844 $_zp_current_context = $context; 1845} 1846 1847function in_context($context) { 1848 return get_context() & $context; 1849} 1850 1851function add_context($context) { 1852 set_context(get_context() | $context); 1853} 1854 1855function rem_context($context) { 1856 global $_zp_current_context; 1857 set_context(get_context() & ~$context); 1858} 1859 1860// Use save and restore rather than add/remove when modifying contexts. 1861function save_context() { 1862 global $_zp_current_context, $_zp_current_context_stack; 1863 array_push($_zp_current_context_stack, $_zp_current_context); 1864} 1865 1866function restore_context() { 1867 global $_zp_current_context, $_zp_current_context_stack; 1868 $_zp_current_context = array_pop($_zp_current_context_stack); 1869} 1870 1871/** 1872 * checks password posting 1873 * 1874 * @param string $authType override of athorization type 1875 */ 1876function zp_handle_password($authType = NULL, $check_auth = NULL, $check_user = NULL) { 1877 global $_zp_loggedin, $_zp_login_error, $_zp_current_album, $_zp_current_zenpage_page, $_zp_current_category, $_zp_current_zenpage_news, $_zp_gallery; 1878 if (empty($authType)) { // not supplied by caller 1879 $check_auth = ''; 1880 if (isset($_GET['z']) && @$_GET['p'] == 'full-image' || isset($_GET['p']) && $_GET['p'] == '*full-image') { 1881 $authType = 'zpcms_auth_image'; 1882 $check_auth = getOption('protected_image_password'); 1883 $check_user = getOption('protected_image_user'); 1884 } else if (in_context(ZP_SEARCH)) { // search page 1885 $authType = 'zpcms_auth_search'; 1886 $check_auth = getOption('search_password'); 1887 $check_user = getOption('search_user'); 1888 } else if (in_context(ZP_ALBUM)) { // album page 1889 $authType = "zpcms_auth_album_" . $_zp_current_album->getID(); 1890 $check_auth = $_zp_current_album->getPassword(); 1891 $check_user = $_zp_current_album->getUser(); 1892 if (empty($check_auth)) { 1893 $parent = $_zp_current_album->getParent(); 1894 while (!is_null($parent)) { 1895 $check_auth = $parent->getPassword(); 1896 $check_user = $parent->getUser(); 1897 $authType = "zpcms_auth_album_" . $parent->getID(); 1898 if (!empty($check_auth)) { 1899 break; 1900 } 1901 $parent = $parent->getParent(); 1902 } 1903 } 1904 } else if (in_context(ZP_ZENPAGE_PAGE)) { 1905 $authType = "zpcms_auth_page_" . $_zp_current_zenpage_page->getID(); 1906 $check_auth = $_zp_current_zenpage_page->getPassword(); 1907 $check_user = $_zp_current_zenpage_page->getUser(); 1908 if (empty($check_auth)) { 1909 $pageobj = $_zp_current_zenpage_page; 1910 while (empty($check_auth)) { 1911 $parentID = $pageobj->getParentID(); 1912 if ($parentID == 0) 1913 break; 1914 $sql = 'SELECT `titlelink` FROM ' . prefix('pages') . ' WHERE `id`=' . $parentID; 1915 $result = query_single_row($sql); 1916 $pageobj = new ZenpagePage($result['titlelink']); 1917 $authType = "zpcms_auth_page_" . $pageobj->getID(); 1918 $check_auth = $pageobj->getPassword(); 1919 $check_user = $pageobj->getUser(); 1920 } 1921 } 1922 } else if (in_context(ZP_ZENPAGE_NEWS_CATEGORY) || in_context(ZP_ZENPAGE_NEWS_ARTICLE)) { 1923 $check_auth_user = array(); 1924 if (in_context(ZP_ZENPAGE_NEWS_CATEGORY)) { 1925 $checkcats = array($_zp_current_category); 1926 } else if (in_context(ZP_ZENPAGE_NEWS_ARTICLE)) { 1927 $checkcats = array(); 1928 $cats = $_zp_current_zenpage_news->getCategories(); 1929 foreach ($cats as $cat) { 1930 $checkcats[] = new ZenpageCategory($cat['titlelink']); 1931 } 1932 } 1933 if (!empty($checkcats)) { 1934 foreach ($checkcats as $obj) { 1935 $authType = "zpcms_auth_category_" . $obj->getID(); 1936 $check_auth = $obj->getPassword(); 1937 $check_user = $obj->getUser(); 1938 if (empty($check_auth)) { 1939 $catobj = $obj; 1940 while (empty($check_auth)) { 1941 $parentID = $catobj->getParentID(); 1942 if ($parentID == 0) 1943 break; 1944 $sql = 'SELECT `titlelink` FROM ' . prefix('news_categories') . ' WHERE `id`=' . $parentID; 1945 $result = query_single_row($sql); 1946 $catobj = new ZenpageCategory($result['titlelink']); 1947 $authType = "zpcms_auth_category_" . $catobj->getID(); 1948 $check_auth = $catobj->getPassword(); 1949 $check_user = $catobj->getUser(); 1950 } 1951 } 1952 if (!empty($check_auth)) { 1953 //collect passwords from all categories 1954 $check_auth_user[] = array( 1955 'authtype' => $authType, 1956 'check_auth' => $check_auth, 1957 'check_user' => $check_user 1958 ); 1959 } 1960 } 1961 } 1962 } 1963 if (empty($check_auth)) { // anything else is controlled by the gallery credentials 1964 $authType = 'zpcms_auth_gallery'; 1965 $check_auth = $_zp_gallery->getPassword(); 1966 $check_user = $_zp_gallery->getUser(); 1967 } 1968 } 1969 if (in_context(ZP_ZENPAGE_NEWS_ARTICLE)) { 1970 //check every category with password individually 1971 foreach ($check_auth_user as $check) { 1972 zp_handle_password_single($check['authtype'], $check['check_auth'], $check['check_user']); 1973 } 1974 } else { 1975 zp_handle_password_single($authType, $check_auth, $check_user); 1976 } 1977} 1978 1979/** 1980 * Handles a passwort 1981 * 1982 * @param string $authType override of authorization type 1983 * @param string $check_auth Password 1984 * @param string $check_user User 1985 * @return bool 1986 */ 1987function zp_handle_password_single($authType = NULL, $check_auth = NULL, $check_user = NULL) { 1988 // Handle the login form. 1989 if (DEBUG_LOGIN) 1990 debugLog("zp_handle_password: \$authType=$authType; \$check_auth=$check_auth; \$check_user=$check_user; "); 1991 1992 if (isset($_POST['password']) && isset($_POST['pass'])) { // process login form 1993 if (isset($_POST['user'])) { 1994 $post_user = sanitize($_POST['user']); 1995 } else { 1996 $post_user = ''; 1997 } 1998 $post_pass = $_POST['pass']; // We should not sanitize the password 1999 2000 foreach (Zenphoto_Authority::$hashList as $hash => $hi) { 2001 $auth = Zenphoto_Authority::passwordHash($post_user, $post_pass, $hi); 2002 $success = ($auth == $check_auth) && $post_user == $check_user; 2003 if (DEBUG_LOGIN) 2004 debugLog("zp_handle_password($success): \$post_user=$post_user; \$post_pass=$post_pass; \$check_auth=$check_auth; \$auth=$auth; \$hash=$hash;"); 2005 if ($success) { 2006 break; 2007 } 2008 } 2009 $success = zp_apply_filter('guest_login_attempt', $success, $post_user, $post_pass, $authType); 2010 if ($success) { 2011 // Correct auth info. Set the cookie. 2012 if (DEBUG_LOGIN) 2013 debugLog("zp_handle_password: valid credentials"); 2014 zp_setCookie($authType, $auth); 2015 if (isset($_POST['redirect'])) { 2016 $redirect_to = sanitizeRedirect($_POST['redirect']); 2017 if (!empty($redirect_to)) { 2018 redirectURL($redirect_to); 2019 } 2020 } 2021 } else { 2022 // Clear the cookie, just in case 2023 if (DEBUG_LOGIN) 2024 debugLog("zp_handle_password: invalid credentials"); 2025 zp_clearCookie($authType); 2026 $_zp_login_error = true; 2027 } 2028 return; 2029 } 2030 if (empty($check_auth)) { //no password on record or admin logged in 2031 return; 2032 } 2033 if (($saved_auth = zp_getCookie($authType)) != '') { 2034 if ($saved_auth == $check_auth) { 2035 if (DEBUG_LOGIN) 2036 debugLog("zp_handle_password: valid cookie"); 2037 return; 2038 } else { 2039 // Clear the cookie 2040 if (DEBUG_LOGIN) 2041 debugLog("zp_handle_password: invalid cookie"); 2042 zp_clearCookie($authType); 2043 } 2044 } 2045} 2046 2047/** 2048 * 2049 * Gets an option directly from the database. 2050 * @param string $key 2051 */ 2052function getOptionFromDB($key) { 2053 $sql = "SELECT `value` FROM " . prefix('options') . " WHERE `name`=" . db_quote($key) . " AND `ownerid`=0"; 2054 $optionlist = query_single_row($sql, false); 2055 return @$optionlist['value']; 2056} 2057 2058/** 2059 * Set options local to theme and/or album 2060 * 2061 * @param string $key 2062 * @param string $value 2063 * @param object $album 2064 * @param string $theme default theme 2065 * @param bool $default set to true for setting default theme options (does not set the option if it already exists) 2066 */ 2067function setThemeOption($key, $value, $album, $theme, $default = false) { 2068 global $_zp_gallery; 2069 if (is_null($album)) { 2070 $id = 0; 2071 } else { 2072 $id = $album->getID(); 2073 $theme = $album->getAlbumTheme(); 2074 } 2075 if (empty($theme)) { 2076 $theme = $_zp_gallery->getCurrentTheme(); 2077 } 2078 $creator = THEMEFOLDER . '/' . $theme; 2079 2080 $sql = 'INSERT INTO ' . prefix('options') . ' (`name`,`ownerid`,`theme`,`creator`,`value`) VALUES (' . db_quote($key) . ',0,' . db_quote($theme) . ',' . db_quote($creator) . ','; 2081 $sqlu = ' ON DUPLICATE KEY UPDATE `value`='; 2082 if (is_null($value)) { 2083 $sql .= 'NULL'; 2084 $sqlu .= 'NULL'; 2085 } else { 2086 $sql .= db_quote($value); 2087 $sqlu .= db_quote($value); 2088 } 2089 $sql .= ') '; 2090 if (!$default) { 2091 $sql .= $sqlu; 2092 } 2093 $result = query($sql, false); 2094} 2095 2096/** 2097 * Replaces/renames an option. If the old option exits, it creates the new option with the old option's value as the default 2098 * unless the new option has already been set otherwise. Independently it always deletes the old option. 2099 * 2100 * @param string $oldkey Old option name 2101 * @param string $newkey New option name 2102 * 2103 * @since Zenphoto 1.5.1 2104 */ 2105function replaceThemeOption($oldkey, $newkey) { 2106 $oldoption = getThemeOption($oldkey); 2107 if ($oldoption) { 2108 setThemeOptionDefault($newkey, $oldoption); 2109 purgeThemeOption($oldkey); 2110 } 2111} 2112 2113/** 2114 * Deletes an option from the database 2115 * 2116 * @global array $_zp_options 2117 * @param string $key 2118 * 2119 * @since Zenphoto 1.5.1 2120 */ 2121function purgeThemeOption($key, $album = NULL, $theme = NULL) { 2122 global $_set_theme_album, $_zp_gallery; 2123 if (is_null($album)) { 2124 $album = $_set_theme_album; 2125 } 2126 if (is_null($album)) { 2127 $id = 0; 2128 } else { 2129 $id = $album->getID(); 2130 $theme = $album->getAlbumTheme(); 2131 } 2132 if (empty($theme)) { 2133 $theme = $_zp_gallery->getCurrentTheme(); 2134 } 2135 $sql = 'DELETE FROM ' . prefix('options') . ' WHERE `name`=' . db_quote($key) . ' AND `ownerid`=' . $id . ' AND `theme`=' . db_quote($theme); 2136 query($sql, false); 2137} 2138 2139/** 2140 * Used to set default values for theme specific options 2141 * 2142 * @param string $key 2143 * @param mixed $value 2144 */ 2145function setThemeOptionDefault($key, $value) { 2146 $bt = debug_backtrace(); 2147 $b = array_shift($bt); 2148 $theme = basename(dirname($b['file'])); 2149 setThemeOption($key, $value, NULL, $theme, true); 2150} 2151 2152/** 2153 * Returns the value of a theme option 2154 * 2155 * @param string $option option key 2156 * @param object $album 2157 * @param string $theme default theme name 2158 * @return mixed 2159 */ 2160function getThemeOption($option, $album = NULL, $theme = NULL) { 2161 global $_set_theme_album, $_zp_gallery; 2162 if (is_null($album)) { 2163 $album = $_set_theme_album; 2164 } 2165 if (is_null($album)) { 2166 $id = 0; 2167 } else { 2168 $id = $album->getID(); 2169 $theme = $album->getAlbumTheme(); 2170 } 2171 if (empty($theme)) { 2172 $theme = $_zp_gallery->getCurrentTheme(); 2173 } 2174 2175 // album-theme 2176 $sql = "SELECT `value` FROM " . prefix('options') . " WHERE `name`=" . db_quote($option) . " AND `ownerid`=" . $id . " AND `theme`=" . db_quote($theme); 2177 $db = query_single_row($sql); 2178 if (!$db) { 2179 // raw theme option 2180 $sql = "SELECT `value` FROM " . prefix('options') . " WHERE `name`=" . db_quote($option) . " AND `ownerid`=0 AND `theme`=" . db_quote($theme); 2181 $db = query_single_row($sql); 2182 if (!$db) { 2183 // raw album option 2184 $sql = "SELECT `value` FROM " . prefix('options') . " WHERE `name`=" . db_quote($option) . " AND `ownerid`=" . $id . " AND `theme`=NULL"; 2185 $db = query_single_row($sql); 2186 if (!$db) { 2187 return getOption($option); 2188 } 2189 } 2190 } 2191 return $db['value']; 2192} 2193 2194/** 2195 * Returns true if all the right conditions are set to allow comments for the $type 2196 * 2197 * @param string $type Which comments 2198 * @return bool 2199 */ 2200function commentsAllowed($type) { 2201 return getOption($type) && (!MEMBERS_ONLY_COMMENTS || zp_loggedin(ADMIN_RIGHTS | POST_COMMENT_RIGHTS)); 2202} 2203 2204/** 2205 * Returns the viewer's IP address 2206 * Deals with transparent proxies 2207 * 2208 * @param bool $anonymize If null (default) the backend option setting is used. Override with anonymize levels 2209 * - 0 (No anonymizing) 2210 * - 1 (Last fourth anonymized) 2211 * - 2 (Last half anonymized) 2212 * - 3 (Last three fourths anonymized) 2213 * - 4 (Full anonymization, no IP stored) 2214 * @return string 2215 */ 2216function getUserIP($anonymize = null) { 2217 if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { 2218 $ip = sanitize($_SERVER['HTTP_X_FORWARDED_FOR']); 2219 if (filter_var($ip, FILTER_VALIDATE_IP)) { 2220 return getAnonymIP($ip, $anonymize); 2221 } 2222 } 2223 $ip = sanitize($_SERVER['REMOTE_ADDR']); 2224 if (filter_var($ip, FILTER_VALIDATE_IP)) { 2225 return getAnonymIP($ip, $anonymize); 2226 } 2227 return NULL; 2228} 2229 2230/** 2231 * Anonymizing of IP addresses 2232 * @param bool $anonymize If null (default) the backend option setting is used. Override with anonymize levels 2233 * - 0 (No anonymizing) 2234 * - 1 (Last fourth anonymized) 2235 * - 2 (Last half anonymized) 2236 * - 3 (Last three fourths anonymized) 2237 * - 4 (Full anonymization, no IP stored) 2238 * 2239 * @return string 2240 */ 2241function getAnonymIP($ip, $anonymize = null) { 2242 if (is_null($anonymize)) { 2243 $anonymize = getOption('anonymize_ip'); 2244 } 2245 $is_ipv6 = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); 2246 switch ($anonymize) { 2247 case 0; // No anonymizing 2248 return $ip; 2249 default: 2250 case 1; // Last fourth anonymized 2251 if ($is_ipv6) { 2252 return preg_replace('~[0-9a-zA-Z]*:[0-9a-zA-Z]+$~', '0:0', $ip); 2253 } else { 2254 return preg_replace('~[0-9a-zA-Z]+$~', '0', $ip); 2255 } 2256 case 2: // Last half anonymized 2257 if ($is_ipv6) { 2258 return preg_replace('~[0-9a-zA-Z]*:[0-9a-zA-Z]*:[0-9a-zA-Z]*:[0-9a-zA-Z]+$~', '0:0:0:0', $ip); 2259 } else { 2260 return preg_replace('~[0-9a-zA-Z]*.[0-9a-zA-Z]+$~', '0.0', $ip); 2261 } 2262 case 3: // Last three fourths anonymized 2263 if ($is_ipv6) { 2264 return preg_replace('~[0-9a-zA-Z]*:[0-9a-zA-Z]*:[0-9a-zA-Z]*:[0-9a-zA-Z]*:[0-9a-zA-Z]*:[0-9a-zA-Z]+$~', '0:0:0:0:0:0', $ip); 2265 } else { 2266 return preg_replace('~[0-9a-zA-Z]*.[0-9a-zA-Z]*.[0-9a-zA-Z]+$~', '0.0.0', $ip); 2267 } 2268 case 4: // Full anonymization, no IP stored 2269 if ($is_ipv6) { 2270 return '0:0:0:0:0:0:0:0'; 2271 } else { 2272 return '0.0.0.0'; 2273 } 2274 } 2275} 2276 2277/** 2278 * Strips out and/or replaces characters from the string that are not "soe" friendly 2279 * 2280 * @param string $string 2281 * @return string 2282 */ 2283function seoFriendly($string) { 2284 $string = trim(preg_replace('~\s+\.\s*~', '.', $string)); 2285 if (zp_has_filter('seoFriendly')) { 2286 $string = zp_apply_filter('seoFriendly', $string); 2287 } else { // no filter, do basic cleanup 2288 $string = trim($string); 2289 $string = preg_replace("/\s+/", "-", $string); 2290 $string = preg_replace("/[^a-zA-Z0-9_.-]/", "-", $string); 2291 $string = str_replace(array('---', '--'), '-', $string); 2292 } 2293 return $string; 2294} 2295 2296/** 2297 * 2298 * emit the javascript seojs() function 2299 */ 2300function seoFriendlyJS() { 2301 if (zp_has_filter('seoFriendly_js')) { 2302 echo zp_apply_filter('seoFriendly_js'); 2303 } else { 2304 ?> 2305 function seoFriendlyJS(fname) { 2306 fname=fname.trim(); 2307 fname=fname.replace(/\s+\.\s*/,'.'); 2308 fname = fname.replace(/\s+/g, '-'); 2309 fname = fname.replace(/[^a-zA-Z0-9_.-]/g, '-'); 2310 fname = fname.replace(/--*/g, '-'); 2311 return fname; 2312 } 2313 <?php 2314 } 2315} 2316 2317/** 2318 * Returns true if there is an internet connection 2319 * 2320 * @param string $host optional host name to test 2321 * 2322 * @return bool 2323 */ 2324function is_connected($host = 'www.zenphoto.org') { 2325 $err_no = $err_str = false; 2326 $connected = @fsockopen($host, 80, $errno, $errstr, 0.5); 2327 if ($connected) { 2328 fclose($connected); 2329 return true; 2330 } 2331 return false; 2332} 2333 2334/** 2335 * produce debugging information on 404 errors 2336 * @param string $album 2337 * @param string $image 2338 * @param string $theme 2339 */ 2340function debug404($album, $image, $theme) { 2341 if (DEBUG_404) { 2342 $list = explode('/', $album); 2343 if (array_shift($list) == 'cache') { 2344 return; 2345 } 2346 $ignore = array('/favicon.ico', '/zp-data/tést.jpg'); 2347 $target = getRequestURI(); 2348 foreach ($ignore as $uri) { 2349 if ($target == $uri) 2350 return; 2351 } 2352 $server = array(); 2353 foreach (array('REQUEST_URI', 'HTTP_REFERER', 'REMOTE_ADDR', 'REDIRECT_STATUS') as $key) { 2354 $server[$key] = @$_SERVER[$key]; 2355 } 2356 $request = $_REQUEST; 2357 $request['theme'] = $theme; 2358 if (!empty($image)) { 2359 $request['image'] = $image; 2360 } 2361 2362 trigger_error(sprintf(gettext('Zenphoto processed a 404 error on %s. See the debug log for details.'), $target), E_USER_NOTICE); 2363 ob_start(); 2364 var_dump($server); 2365 $server = preg_replace('~array\s*\(.*\)\s*~', '', html_decode(getBare(ob_get_contents()))); 2366 ob_end_clean(); 2367 ob_start(); 2368 var_dump($request); 2369 $request['theme'] = $theme; 2370 if (!empty($image)) { 2371 $request['image'] = $image; 2372 } 2373 $request = preg_replace('~array\s*\(.*\)\s*~', '', html_decode(getBare(ob_get_contents()))); 2374 ob_end_clean(); 2375 debugLog("404 error details\n" . $server . $request); 2376 } 2377} 2378 2379/** 2380 * Checks for Cross Site Request Forgeries 2381 * @param string $action 2382 */ 2383function XSRFdefender($action) { 2384 $token = getXSRFToken($action); 2385 if (!isset($_REQUEST['XSRFToken']) || $_REQUEST['XSRFToken'] != $token) { 2386 zp_apply_filter('admin_XSRF_access', false, $action); 2387 redirectURL(FULLWEBPATH . '/' . ZENFOLDER . '/admin.php?action=external&error&msg=' . sprintf(gettext('“%s” Cross Site Request Forgery blocked.'), $action), '302'); 2388 } 2389 unset($_REQUEST['XSRFToken']); 2390 unset($_POST['XSRFToken']); 2391 unset($_GET['XSRFToken']); 2392} 2393 2394/** 2395 * returns an XSRF token 2396 * @param striong $action 2397 */ 2398function getXSRFToken($action) { 2399 global $_zp_current_admin_obj; 2400 $admindata = ''; 2401 if (!is_null($_zp_current_admin_obj)) { 2402 $admindata = $_zp_current_admin_obj->getData(); 2403 unset($admindata['lastvisit']); 2404 } 2405 return sha1($action . prefix(ZENPHOTO_VERSION) . serialize($admindata) . session_id()); 2406} 2407 2408/** 2409 * Emits a "hidden" input for the XSRF token 2410 * @param string $action 2411 */ 2412function XSRFToken($action) { 2413 ?> 2414 <input type="hidden" name="XSRFToken" id="XSRFToken" value="<?php echo getXSRFToken($action); ?>" /> 2415 <?php 2416} 2417 2418/** 2419 * Starts a sechedule script run 2420 * @param string $script The script file to load 2421 * @param array $params "POST" parameters 2422 * @param bool $inline set to true to run the task "in-line". Set false run asynchronously 2423 */ 2424function cron_starter($script, $params, $offsetPath, $inline = false) { 2425 global $_zp_authority, $_zp_loggedin, $_zp_current_admin_obj, $_zp_HTML_cache; 2426 $admin = $_zp_authority->getMasterUser(); 2427 2428 if ($inline) { 2429 $_zp_current_admin_obj = $admin; 2430 $_zp_loggedin = $_zp_current_admin_obj->getRights(); 2431 foreach ($params as $key => $value) { 2432 if ($key == 'XSRFTag') { 2433 $key = 'XSRFToken'; 2434 $value = getXSRFToken($value); 2435 } 2436 $_POST[$key] = $_GET[$key] = $_REQUEST[$key] = $value; 2437 } 2438 require_once($script); 2439 } else { 2440 $auth = sha1($script . serialize($admin)); 2441 $paramlist = 'link=' . $script; 2442 foreach ($params as $key => $value) { 2443 $paramlist .= '&' . $key . '=' . $value; 2444 } 2445 $paramlist .= '&auth=' . $auth . '&offsetPath=' . $offsetPath; 2446 $_zp_HTML_cache->abortHTMLCache(); 2447 ?> 2448 <script type="text/javascript"> 2449 // <!-- <![CDATA[ 2450 $.ajax({ 2451 type: 'POST', 2452 cache: false, 2453 data: '<?php echo $paramlist; ?>', 2454 url: '<?php echo WEBPATH . '/' . ZENFOLDER; ?>/cron_runner.php' 2455 }); 2456 // ]]> --> 2457 </script> 2458 <?php 2459 } 2460} 2461 2462/** 2463 * 2464 * Check if logged in (with specific rights) 2465 * Returns a true value if there is a user logged on with the required rights 2466 * 2467 * @param bit $rights rights required by the caller 2468 * 2469 * @return bool 2470 */ 2471function zp_loggedin($rights = ALL_RIGHTS) { 2472 global $_zp_loggedin, $_zp_current_admin_obj; 2473 $loggedin = $_zp_loggedin & ($rights | ADMIN_RIGHTS); 2474 if ($loggedin && $_zp_current_admin_obj) { 2475 $_zp_current_admin_obj->updateLastVisit(); 2476 } 2477 return $loggedin; 2478} 2479 2480/** 2481 * Provides an error protected read of image EXIF/IPTC data 2482 * 2483 * @param string $path image path 2484 * @return array 2485 * 2486 */ 2487function read_exif_data_protected($path) { 2488 if (DEBUG_EXIF) { 2489 debugLog("Begin read_exif_data_protected($path)"); 2490 $start = microtime(true); 2491 } 2492 try { 2493 $rslt = read_exif_data_raw($path, false); 2494 } catch (Exception $e) { 2495 debugLog("read_exif_data($path) exception: " . $e->getMessage()); 2496 $rslt = array(); 2497 } 2498 if (DEBUG_EXIF) { 2499 $time = microtime(true) - $start; 2500 debugLog(sprintf("End read_exif_data_protected($path) [%f]", $time)); 2501 } 2502 return $rslt; 2503} 2504 2505/** 2506 * 2507 * fetches the path to the flag image 2508 * @param string $lang whose flag 2509 * @return string 2510 */ 2511function getLanguageFlag($lang) { 2512 if (file_exists(SERVERPATH . '/' . USER_PLUGIN_FOLDER . '/locale/' . $lang . '/flag.png')) { 2513 $flag = WEBPATH . '/' . USER_PLUGIN_FOLDER . '/locale/' . $lang . '/flag.png'; 2514 } else if (file_exists(SERVERPATH . '/' . ZENFOLDER . '/locale/' . $lang . '/flag.png')) { 2515 $flag = WEBPATH . '/' . ZENFOLDER . '/locale/' . $lang . '/flag.png'; 2516 } else { 2517 $flag = WEBPATH . '/' . ZENFOLDER . '/locale/missing_flag.png'; 2518 } 2519 return $flag; 2520} 2521 2522/** 2523 * Gets an item object by id 2524 * 2525 * @param string $table database table to search 2526 * @param int $id id of the item to get 2527 * @return mixed 2528 */ 2529function getItemByID($table, $id) { 2530 if ($result = query_single_row('SELECT * FROM ' . prefix($table) . ' WHERE id =' . (int) $id)) { 2531 switch ($table) { 2532 case 'images': 2533 if ($alb = getItemByID('albums', $result['albumid'])) { 2534 return newImage($alb, $result['filename'], true); 2535 } 2536 break; 2537 case 'albums': 2538 return newAlbum($result['folder'], false, true); 2539 case 'news': 2540 return new ZenpageNews($result['titlelink']); 2541 case 'pages': 2542 return new ZenpagePage($result['titlelink']); 2543 case 'news_categories': 2544 return new ZenpageCategory($result['titlelink']); 2545 } 2546 } 2547 return NULL; 2548} 2549 2550/** 2551 * uses down and up arrow links to show and hide sections of HTML 2552 * 2553 * @param string $content the id of the html section to be revealed 2554 * @param bool $visible true if the content is initially visible 2555 */ 2556function reveal($content, $visible = false) { 2557 ?> 2558 <span id="<?php echo $content; ?>_reveal"<?php if ($visible) echo 'style="display:none;"'; ?> class="icons"> 2559 <a href="javascript:reveal('<?php echo $content; ?>')" title="<?php echo gettext('Click to show content'); ?>"> 2560 <img src="../../images/arrow_down.png" alt="" class="icon-position-top4" /> 2561 </a> 2562 </span> 2563 <span id="<?php echo $content; ?>_hide"<?php if (!$visible) echo 'style="display:none;"'; ?> class="icons"> 2564 <a href="javascript:reveal('<?php echo $content; ?>')" title="<?php echo gettext('Click to hide content'); ?>"> 2565 <img src="../../images/arrow_up.png" alt="" class="icon-position-top4" /> 2566 </a> 2567 </span> 2568 <?php 2569} 2570 2571/** 2572 * Deals with the [macro parameters] substitutions 2573 * 2574 * See the macroList plugin for details 2575 * 2576 * @param string $text 2577 * @return string 2578 */ 2579function applyMacros($text) { 2580 $content_macros = getMacros(); 2581 preg_match_all('/\[(\w+)(.*?)\]/i', $text, $instances); 2582 foreach ($instances[0] as $instance => $macro_instance) { 2583 $macroname = strtoupper($instances[1][$instance]); 2584 if (array_key_exists($macroname, $content_macros)) { 2585 $macro = $content_macros[$macroname]; 2586 $p = $instances[2][$instance]; 2587 $data = NULL; 2588 $class = $macro['class']; 2589 if ($p) { 2590 $p = trim(utf8::sanitize(str_replace("\xC2\xA0", ' ', strip_tags($p)))); // remove hard spaces and invalid characters 2591 $p = preg_replace("~\s+=\s+(?=(?:[^\"]*+\"[^\"]*+\")*+[^\"]*+$)~", "=", $p); // deblank assignment operator 2592 preg_match_all("~'[^'\"]++'|\"[^\"]++\"|[^\s]++~", $p, $l); // parse the parameter list 2593 $parms = array(); 2594 $k = 0; 2595 foreach ($l[0] as $s) { 2596 if ($s != ',') { 2597 $parms[$k++] = trim($s, '\'"'); // remove any quote marks 2598 } 2599 } 2600 } else { 2601 $parms = array(); 2602 } 2603 $parameters = array(); 2604 if (!empty($macro['params'])) { 2605 $err = false; 2606 foreach ($macro['params'] as $key => $type) { 2607 $data = false; 2608 if (array_key_exists($key, $parms)) { 2609 switch (trim($type, '*')) { 2610 case 'int': 2611 if (is_numeric($parms[$key])) { 2612 $parameters[] = (int) $parms[$key]; 2613 } else { 2614 $data = '<span class="error">' . sprintf(gettext('<em>[%1$s]</em> parameter %2$d should be a number.'), trim($macro_instance, '[]'), $key + 1) . '</span>'; 2615 $class = 'error'; 2616 } 2617 break; 2618 case 'string': 2619 if (is_string($parms[$key])) { 2620 $parameters[] = $parms[$key]; 2621 } else { 2622 $data = '<span class="error">' . sprintf(gettext('<em>[%1$s]</em> parameter %2$d should be a string.'), trim($macro_instance, '[]'), $key + 1) . '</span>'; 2623 $class = 'error'; 2624 } 2625 break; 2626 case 'bool': 2627 switch (strtolower($parms[$key])) { 2628 case ("true"): 2629 $parameters[] = true; 2630 break; 2631 case ("false"): 2632 $parameters[] = false; 2633 break; 2634 default: 2635 $data = '<span class="error">' . sprintf(gettext('<em>[%1$s]</em> parameter %2$d should be <code>true</code> or <code>false</code>.'), trim($macro_instance, '[]'), $key + 1) . '</span>'; 2636 $class = 'error'; 2637 break; 2638 } 2639 break; 2640 case 'array': 2641 $l = array_slice($parms, $key); 2642 $parms = array(); 2643 foreach ($l as $key => $p) { 2644 $x = explode('=', $p); 2645 if (count($x) == 2) { 2646 $parms[$x[0]] = $x[1]; 2647 } else { 2648 $parms[$key] = $x[0]; 2649 } 2650 } 2651 $parameters[] = $parms; 2652 break; 2653 default: 2654 $data = '<span class="error">' . sprintf(gettext('<em>[%1$s]</em> parameter %2$d is incorrectly defined.'), trim($macro_instance, '[]'), $key + 1) . '</span>'; 2655 $class = 'error'; 2656 break; 2657 } 2658 } else { 2659 if (strpos($type, '*') === false) { 2660 $data = '<span class="error">' . sprintf(gettext('<em>[%1$s]</em> parameter %2$d is missing.'), trim($macro_instance, '[]'), $key + 1) . '</span>'; 2661 $class = 'error'; 2662 } 2663 break; 2664 } 2665 } 2666 } else { 2667 if (!empty($p)) { 2668 $class = 'error'; 2669 $data = '<span class="error">' . sprintf(gettext('<em>[%1$s]</em> macro does not take parameters'), trim($macro_instance, '[]')) . '</span>'; 2670 } 2671 } 2672 switch ($class) { 2673 case 'error': 2674 break; 2675 case 'function'; 2676 case 'procedure': 2677 if (is_callable($macro['value'])) { 2678 if ($class == 'function') { 2679 ob_start(); 2680 $data = call_user_func_array($macro['value'], $parameters); 2681 if (empty($data)) { 2682 $data = ob_get_contents(); 2683 } 2684 ob_end_clean(); 2685 } else { 2686 ob_start(); 2687 call_user_func_array($macro['value'], $parameters); 2688 $data = ob_get_contents(); 2689 ob_end_clean(); 2690 } 2691 if (empty($data)) { 2692 $data = '<span class="error">' . sprintf(gettext('<em>[%1$s]</em> retuned no data'), trim($macro_instance, '[]')) . '</span>'; 2693 } else { 2694 $data = "\n<!--Begin " . $macroname . "-->\n" . $data . "\n<!--End " . $macroname . "-->\n"; 2695 } 2696 } else { 2697 $data = '<span class="error">' . sprintf(gettext('<em>[%1$s]</em> <code>%2$s</code> is not callable'), trim($macro_instance, '[]'), $macro['value']) . '</span>'; 2698 } 2699 break; 2700 case 'constant': 2701 $data = "\n<!--Begin " . $macroname . "-->\n" . $macro['value'] . "\n<!--End " . $macroname . "-->\n"; 2702 break; 2703 case 'expression': 2704 $expression = '$data = ' . $macro['value']; 2705 $parms = array_reverse($parms, true); 2706 preg_match_all('/\$\d+/', $macro['value'], $replacements); 2707 foreach ($replacements as $rkey => $v) { 2708 if (empty($v)) 2709 unset($replacements[$rkey]); 2710 } 2711 if (count($parms) == count($replacements)) { 2712 2713 foreach ($parms as $key => $value) { 2714 $key++; 2715 $expression = preg_replace('/\$' . $key . '/', db_quote($value), $expression); 2716 } 2717 eval($expression); 2718 if (!isset($data) || is_null($data)) { 2719 $data = '<span class="error">' . sprintf(gettext('<em>[%1$s]</em> retuned no data'), trim($macro_instance, '[]')) . '</span>'; 2720 } else { 2721 $data = "\n<!--Begin " . $macroname . "-->\n" . $data . "\n<!--End " . $macroname . "-->\n"; 2722 } 2723 } else { 2724 $data = '<span class="error">' . sprintf(ngettext('<em>[%1$s]</em> takes %2$d parameter', '<em>[%1$s]</em> takes %2$d parameters', count($replacements)), trim($macro_instance, '[]'), count($replacements)) . '</span>'; 2725 } 2726 break; 2727 } 2728 $text = str_replace($macro_instance, $data, $text); 2729 } 2730 } 2731 return $text; 2732} 2733 2734function getMacros() { 2735 global $_zp_content_macros; 2736 if (is_null($_zp_content_macros)) { 2737 $_zp_content_macros = zp_apply_filter('content_macro', array()); 2738 } 2739 return $_zp_content_macros; 2740} 2741 2742/** 2743 * generates a nested list of albums for the album tab sorting 2744 * Returns an array of "albums" each element contains: 2745 * 'name' which is the folder name 2746 * 'sort_order' which is an array of the sort order set 2747 * 2748 * @param $subalbum root level album (NULL is the gallery) 2749 * @param $levels how far to nest 2750 * @param $checkalbumrights TRUE (Default) for album rights for backend usage, FALSE to skip for frontend usage 2751 * @param $level internal for keeping the sort order elements 2752 * @return array 2753 */ 2754function getNestedAlbumList($subalbum, $levels, $checkalbumrights = true, $level = array()) { 2755 global $_zp_gallery; 2756 $cur = count($level); 2757 $levels--; // make it 0 relative to sync with $cur 2758 if (is_null($subalbum)) { 2759 $albums = $_zp_gallery->getAlbums(); 2760 } else { 2761 $albums = $subalbum->getAlbums(); 2762 } 2763 $list = array(); 2764 foreach ($albums as $analbum) { 2765 $albumobj = newAlbum($analbum); 2766 $accessallowed = true; 2767 if ($checkalbumrights) { 2768 $accessallowed = $albumobj->isMyItem(ALBUM_RIGHTS); 2769 } 2770 if (!is_null($subalbum) || $accessallowed) { 2771 $level[$cur] = sprintf('%03u', $albumobj->getSortOrder()); 2772 $list[] = array('name' => $analbum, 'sort_order' => $level); 2773 if ($cur < $levels && ($albumobj->getNumAlbums()) && !$albumobj->isDynamic()) { 2774 $list = array_merge($list, getNestedAlbumList($albumobj, $levels + 1, $checkalbumrights, $level)); 2775 } 2776 } 2777 } 2778 return $list; 2779} 2780 2781/** 2782 * initializes the $_zp_exifvars array display state 2783 * 2784 */ 2785function setexifvars() { 2786 global $_zp_exifvars; 2787 /* 2788 * Note: If fields are added or deleted, setup should be run or the new data won't be stored 2789 * (but existing fields will still work; nothing breaks). 2790 * 2791 * This array should be ordered by logical associations as it will be the order that EXIF information 2792 * is displayed 2793 */ 2794 $_zp_exifvars = array( 2795 // Database Field => array('source', 'Metadata Key', 'ZP Display Text', Display?, size (ignored!), enabled, type) 2796 'EXIFMake' => array('IFD0', 'Make', gettext('Camera Maker'), true, 52, true, 'string'), 2797 'EXIFModel' => array('IFD0', 'Model', gettext('Camera Model'), true, 52, true, 'string'), 2798 'EXIFDescription' => array('IFD0', 'ImageDescription', gettext('Image Title'), false, 52, true, 'string'), 2799 'IPTCObjectName' => array('IPTC', 'ObjectName', gettext('Object Name'), false, 256, true, 'string'), 2800 'IPTCImageHeadline' => array('IPTC', 'ImageHeadline', gettext('Image Headline'), false, 256, true, 'string'), 2801 'IPTCImageCaption' => array('IPTC', 'ImageCaption', gettext('Image Caption'), false, 2000, true, 'string'), 2802 'IPTCImageCaptionWriter' => array('IPTC', 'ImageCaptionWriter', gettext('Image Caption Writer'), false, 32, true, 'string'), 2803 'EXIFDateTime' => array('SubIFD', 'DateTime', gettext('Time Taken'), true, 52, true, 'time'), 2804 'EXIFDateTimeOriginal' => array('SubIFD', 'DateTimeOriginal', gettext('Original Time Taken'), true, 52, true, 'time'), 2805 'EXIFDateTimeDigitized' => array('SubIFD', 'DateTimeDigitized', gettext('Time Digitized'), true, 52, true, 'time'), 2806 'IPTCDateCreated' => array('IPTC', 'DateCreated', gettext('Date Created'), false, 8, true, 'time'), 2807 'IPTCTimeCreated' => array('IPTC', 'TimeCreated', gettext('Time Created'), false, 11, true, 'time'), 2808 'IPTCDigitizeDate' => array('IPTC', 'DigitizeDate', gettext('Digital Creation Date'), false, 8, true, 'time'), 2809 'IPTCDigitizeTime' => array('IPTC', 'DigitizeTime', gettext('Digital Creation Time'), false, 11, true, 'time'), 2810 'EXIFArtist' => array('IFD0', 'Artist', gettext('Artist'), false, 52, true, 'string'), 2811 'IPTCImageCredit' => array('IPTC', 'ImageCredit', gettext('Image Credit'), false, 32, true, 'string'), 2812 'IPTCByLine' => array('IPTC', 'ByLine', gettext('Byline'), false, 32, true, 'string'), 2813 'IPTCByLineTitle' => array('IPTC', 'ByLineTitle', gettext('Byline Title'), false, 32, true, 'string'), 2814 'IPTCSource' => array('IPTC', 'Source', gettext('Image Source'), false, 32, true, 'string'), 2815 'IPTCContact' => array('IPTC', 'Contact', gettext('Contact'), false, 128, true, 'string'), 2816 'EXIFCopyright' => array('IFD0', 'Copyright', gettext('Copyright Holder'), false, 128, true, 'string'), 2817 'IPTCCopyright' => array('IPTC', 'Copyright', gettext('Copyright Notice'), false, 128, true, 'string'), 2818 'IPTCKeywords' => array('IPTC', 'Keywords', gettext('Keywords'), false, 0, true, 'string'), 2819 'EXIFExposureTime' => array('SubIFD', 'ExposureTime', gettext('Shutter Speed'), true, 52, true, 'string'), 2820 'EXIFFNumber' => array('SubIFD', 'FNumber', gettext('Aperture'), true, 52, true, 'number'), 2821 'EXIFISOSpeedRatings' => array('SubIFD', 'ISOSpeedRatings', gettext('ISO Sensitivity'), true, 52, true, 'number'), 2822 'EXIFExposureBiasValue' => array('SubIFD', 'ExposureBiasValue', gettext('Exposure Compensation'), true, 52, true, 'string'), 2823 'EXIFMeteringMode' => array('SubIFD', 'MeteringMode', gettext('Metering Mode'), true, 52, true, 'string'), 2824 'EXIFFlash' => array('SubIFD', 'Flash', gettext('Flash Fired'), true, 52, true, 'string'), 2825 'EXIFImageWidth' => array('SubIFD', 'ExifImageWidth', gettext('Original Width'), false, 52, true, 'number'), 2826 'EXIFImageHeight' => array('SubIFD', 'ExifImageHeight', gettext('Original Height'), false, 52, true, 'number'), 2827 'EXIFOrientation' => array('IFD0', 'Orientation', gettext('Orientation'), false, 52, true, 'string'), 2828 'EXIFSoftware' => array('IFD0', 'Software', gettext('Software'), false, 999, true, 'string'), 2829 'EXIFContrast' => array('SubIFD', 'Contrast', gettext('Contrast Setting'), false, 52, true, 'string'), 2830 'EXIFSharpness' => array('SubIFD', 'Sharpness', gettext('Sharpness Setting'), false, 52, true, 'string'), 2831 'EXIFSaturation' => array('SubIFD', 'Saturation', gettext('Saturation Setting'), false, 52, true, 'string'), 2832 'EXIFWhiteBalance' => array('SubIFD', 'WhiteBalance', gettext('White Balance'), false, 52, true, 'string'), 2833 'EXIFSubjectDistance' => array('SubIFD', 'SubjectDistance', gettext('Subject Distance'), false, 52, true, 'number'), 2834 'EXIFFocalLength' => array('SubIFD', 'FocalLength', gettext('Focal Length'), true, 52, true, 'number'), 2835 'EXIFLensType' => array('SubIFD', 'LensType', gettext('Lens Type'), false, 52, true, 'string'), 2836 'EXIFLensInfo' => array('SubIFD', 'LensInfo', gettext('Lens Info'), false, 52, true, 'string'), 2837 'EXIFFocalLengthIn35mmFilm' => array('SubIFD', 'FocalLengthIn35mmFilm', gettext('35mm Focal Length Equivalent'), false, 52, true, 'string'), 2838 'IPTCCity' => array('IPTC', 'City', gettext('City'), false, 32, true, 'string'), 2839 'IPTCSubLocation' => array('IPTC', 'SubLocation', gettext('Sub-location'), false, 32, true, 'string'), 2840 'IPTCState' => array('IPTC', 'State', gettext('Province/State'), false, 32, true, 'string'), 2841 'IPTCLocationCode' => array('IPTC', 'LocationCode', gettext('Country/Primary Location Code'), false, 3, true, 'string'), 2842 'IPTCLocationName' => array('IPTC', 'LocationName', gettext('Country/Primary Location Name'), false, 64, true, 'string'), 2843 'IPTCContentLocationCode' => array('IPTC', 'ContentLocationCode', gettext('Content Location Code'), false, 3, true, 'string'), 2844 'IPTCContentLocationName' => array('IPTC', 'ContentLocationName', gettext('Content Location Name'), false, 64, true, 'string'), 2845 'EXIFGPSLatitude' => array('GPS', 'Latitude', gettext('Latitude'), false, 52, true, 'number'), 2846 'EXIFGPSLatitudeRef' => array('GPS', 'Latitude Reference', gettext('Latitude Reference'), false, 52, true, 'string'), 2847 'EXIFGPSLongitude' => array('GPS', 'Longitude', gettext('Longitude'), false, 52, true, 'number'), 2848 'EXIFGPSLongitudeRef' => array('GPS', 'Longitude Reference', gettext('Longitude Reference'), false, 52, true, 'string'), 2849 'EXIFGPSAltitude' => array('GPS', 'Altitude', gettext('Altitude'), false, 52, true, 'number'), 2850 'EXIFGPSAltitudeRef' => array('GPS', 'Altitude Reference', gettext('Altitude Reference'), false, 52, true, 'string'), 2851 'IPTCOriginatingProgram' => array('IPTC', 'OriginatingProgram', gettext('Originating Program'), false, 32, true, 'string'), 2852 'IPTCProgramVersion' => array('IPTC', 'ProgramVersion', gettext('Program Version'), false, 10, true, 'string'), 2853 'VideoFormat' => array('VIDEO', 'fileformat', gettext('Video File Format'), false, 32, true, 'string'), 2854 'VideoSize' => array('VIDEO', 'filesize', gettext('Video File Size'), false, 32, true, 'number'), 2855 'VideoArtist' => array('VIDEO', 'artist', gettext('Video Artist'), false, 256, true, 'string'), 2856 'VideoTitle' => array('VIDEO', 'title', gettext('Video Title'), false, 256, true, 'string'), 2857 'VideoBitrate' => array('VIDEO', 'bitrate', gettext('Bitrate'), false, 32, true, 'number'), 2858 'VideoBitrate_mode' => array('VIDEO', 'bitrate_mode', gettext('Bitrate_Mode'), false, 32, true, 'string'), 2859 'VideoBits_per_sample' => array('VIDEO', 'bits_per_sample', gettext('Bits per sample'), false, 32, true, 'number'), 2860 'VideoCodec' => array('VIDEO', 'codec', gettext('Codec'), false, 32, true, 'string'), 2861 'VideoCompression_ratio' => array('VIDEO', 'compression_ratio', gettext('Compression Ratio'), false, 32, true, 'number'), 2862 'VideoDataformat' => array('VIDEO', 'dataformat', gettext('Video Dataformat'), false, 32, true, 'string'), 2863 'VideoEncoder' => array('VIDEO', 'encoder', gettext('File Encoder'), false, 10, true, 'string'), 2864 'VideoSamplerate' => array('VIDEO', 'Samplerate', gettext('Sample rate'), false, 32, true, 'number'), 2865 'VideoChannelmode' => array('VIDEO', 'channelmode', gettext('Channel mode'), false, 32, true, 'string'), 2866 'VideoFormat' => array('VIDEO', 'format', gettext('Format'), false, 10, true, 'string'), 2867 'VideoChannels' => array('VIDEO', 'channels', gettext('Channels'), false, 10, true, 'number'), 2868 'VideoFramerate' => array('VIDEO', 'framerate', gettext('Frame rate'), false, 32, true, 'number'), 2869 'VideoResolution_x' => array('VIDEO', 'resolution_x', gettext('X Resolution'), false, 32, true, 'number'), 2870 'VideoResolution_y' => array('VIDEO', 'resolution_y', gettext('Y Resolution'), false, 32, true, 'number'), 2871 'VideoAspect_ratio' => array('VIDEO', 'pixel_aspect_ratio', gettext('Aspect ratio'), false, 32, true, 'number'), 2872 'VideoPlaytime' => array('VIDEO', 'playtime_string', gettext('Play Time'), false, 10, true, 'number'), 2873 'XMPrating' => array('XMP', 'rating', gettext('XMP Rating'), false, 10, true, 'string'), 2874 ); 2875 foreach ($_zp_exifvars as $key => $item) { 2876 if (!is_null($disable = getOption($key . '-disabled'))) { 2877 $_zp_exifvars[$key][5] = !$disable; 2878 } 2879 $_zp_exifvars[$key][3] = getOption($key); 2880 } 2881} 2882 2883/** 2884 * 2885 * Returns true if the install is not a "clone" 2886 */ 2887function hasPrimaryScripts() { 2888 if (!defined('PRIMARY_INSTALLATION')) { 2889 if (function_exists('readlink') && ($zen = str_replace('\\', '/', @readlink(SERVERPATH . '/' . ZENFOLDER)))) { 2890 // no error reading the link info 2891 $os = strtoupper(PHP_OS); 2892 $sp = SERVERPATH; 2893 if (substr($os, 0, 3) == 'WIN' || $os == 'DARWIN') { // canse insensitive file systems 2894 $sp = strtolower($sp); 2895 $zen = strtolower($zen); 2896 } 2897 define('PRIMARY_INSTALLATION', $sp == dirname($zen)); 2898 } else { 2899 define('PRIMARY_INSTALLATION', true); 2900 } 2901 } 2902 return PRIMARY_INSTALLATION; 2903} 2904 2905/** 2906 * 2907 * Recursively clears and removes a folder 2908 * @param string $path 2909 * @return boolean 2910 */ 2911function removeDir($path, $within = false) { 2912 if (($dir = @opendir($path)) !== false) { 2913 while (($file = readdir($dir)) !== false) { 2914 if ($file != '.' && $file != '..') { 2915 if ((is_dir($path . '/' . $file))) { 2916 if (!removeDir($path . '/' . $file)) { 2917 return false; 2918 } 2919 } else { 2920 @chmod($path . $file, 0777); 2921 if (!@unlink($path . '/' . $file)) { 2922 return false; 2923 } 2924 } 2925 } 2926 } 2927 closedir($dir); 2928 if (!$within) { 2929 @chmod($path, 0777); 2930 if (!@rmdir($path)) { 2931 return false; 2932 } 2933 } 2934 return true; 2935 } 2936 return false; 2937} 2938 2939/** 2940 * inserts location independent WEB path tags in place of site path tags 2941 * @param string $text 2942 */ 2943function tagURLs($text) { 2944 if (is_string($text) && preg_match('/^a:[0-9]+:{/', $text)) { // serialized array 2945 $text = getSerializedArray($text); 2946 $serial = true; 2947 } else { 2948 $serial = false; 2949 } 2950 if (is_array($text)) { 2951 foreach ($text as $key => $textelement) { 2952 $text[$key] = tagURLs($textelement); 2953 } 2954 if ($serial) { 2955 $text = serialize($text); 2956 } 2957 } else { 2958 $text = str_replace(WEBPATH, '{*WEBPATH*}', str_replace(FULLWEBPATH, '{*FULLWEBPATH*}', $text)); 2959 } 2960 return $text; 2961} 2962 2963/** 2964 * reverses tagURLs() 2965 * @param string $text 2966 * @return string 2967 */ 2968function unTagURLs($text) { 2969 if (is_string($text) && preg_match('/^a:[0-9]+:{/', $text)) { // serialized array 2970 $text = getSerializedArray($text); 2971 $serial = true; 2972 } else { 2973 $serial = false; 2974 } 2975 if (is_array($text)) { 2976 foreach ($text as $key => $textelement) { 2977 $text[$key] = unTagURLs($textelement); 2978 } 2979 if ($serial) { 2980 $text = serialize($text); 2981 } 2982 } else { 2983 $text = str_replace('{*WEBPATH*}', WEBPATH, str_replace('{*FULLWEBPATH*}', FULLWEBPATH, $text)); 2984 } 2985 return $text; 2986} 2987 2988/** 2989 * Searches out i.php image links and replaces them with cache links if image is cached 2990 * @param string $text 2991 * @return string 2992 */ 2993function updateImageProcessorLink($text) { 2994 if (is_string($text) && preg_match('/^a:[0-9]+:{/', $text)) { // serialized array 2995 $text = getSerializedArray($text); 2996 $serial = true; 2997 } else { 2998 $serial = false; 2999 } 3000 if (is_array($text)) { 3001 foreach ($text as $key => $textelement) { 3002 $text[$key] = updateImageProcessorLink($textelement); 3003 } 3004 if ($serial) { 3005 $text = serialize($text); 3006 } 3007 } else { 3008 preg_match_all('|<\s*img.*?\ssrc\s*=\s*"([^"]*)?|', $text, $matches); 3009 foreach ($matches[1] as $key => $match) { 3010 preg_match('|.*i\.php\?(.*)|', $match, $imgproc); 3011 if ($imgproc) { 3012 $match = preg_split('~\&[amp;]*~', $imgproc[1]); 3013 $set = array(); 3014 foreach ($match as $v) { 3015 $s = explode('=', $v); 3016 $set[$s[0]] = $s[1]; 3017 } 3018 $args = getImageArgs($set); 3019 $imageuri = getImageURI($args, urldecode($set['a']), urldecode($set['i']), NULL); 3020 if (strpos($imageuri, 'i.php') === false) { 3021 $text = str_replace($matches[1][$key], $imageuri, $text); 3022 } 3023 } 3024 } 3025 } 3026 return $text; 3027} 3028 3029function pluginDebug($extension, $priority, $start) { 3030 list($usec, $sec) = explode(" ", microtime()); 3031 $end = (float) $usec + (float) $sec; 3032 $class = array(); 3033 if ($priority & CLASS_PLUGIN) { 3034 $class[] = 'CLASS'; 3035 } 3036 if ($priority & ADMIN_PLUGIN) { 3037 $class[] = 'ADMIN'; 3038 } 3039 if ($priority & FEATURE_PLUGIN) { 3040 $class[] = 'FEATURE'; 3041 } 3042 if ($priority & THEME_PLUGIN) { 3043 $class[] = 'THEME'; 3044 } 3045 if (empty($class)) 3046 $class[] = 'theme'; 3047 debugLog(sprintf(' ' . $extension . '(%s:%u)=>%.4fs', implode('|', $class), $priority & PLUGIN_PRIORITY, $end - $start)); 3048} 3049 3050/** 3051 * Removes a trailing slash from a string if one exists, otherwise just returns the string 3052 * Used primarily within date and tag searches and news date archive results 3053 * 3054 * @param string $string 3055 * @return string 3056 * @since 1.4.12 3057 */ 3058function removeTrailingSlash($string) { 3059 if (substr($string, -1) == '/') { 3060 $length = strlen($string) - 1; 3061 return substr($string, 0, $length); 3062 } 3063 return $string; 3064} 3065 3066/** 3067 * Returns an array the data privacy policy page and the data usage confirmation text as defined on Options > Security 3068 * array( 3069 * 'notice' => '<The defined text>', 3070 * 'url' => '<url to the define page either custom page url or Zenpage page>', 3071 * 'linktext' => '<The defined text>' 3072 * ) 3073 * 3074 * @since Zenphoto 1.5 3075 * 3076 * @return array 3077 */ 3078function getDataUsageNotice() { 3079 $array = array('notice' => '', 'url' => '', 'linktext' => ''); 3080 $array['linktext'] = get_language_string(getOption('dataprivacy_policy_customlinktext')); 3081 $array['notice'] = get_language_string(getOption('dataprivacy_policy_notice')); 3082 $custompage = trim(getOption('dataprivacy_policy_custompage')); 3083 $zenpage_page = ''; 3084 if (empty($array['notice'])) { 3085 $array['notice'] = gettext('By using this form you agree with the storage and handling of your data by this website.'); 3086 } 3087 if (extensionEnabled('zenpage') && ZP_PAGES_ENABLED) { 3088 $zenpage_page = getOption('dataprivacy_policy_zenpage'); 3089 if ($zenpage_page == 'none') { 3090 $zenpage_page = ''; 3091 } 3092 } 3093 if (!empty($custompage)) { 3094 $array['url'] = $custompage; 3095 } else if (!empty($zenpage_page)) { 3096 $obj = new ZenpagePage($zenpage_page); 3097 $array['url'] = $obj->getLink(); 3098 } 3099 if (empty($array['linktext'])) { 3100 $array['linktext'] = gettext('More info on our data privacy policy.'); 3101 } 3102 return $array; 3103} 3104 3105/** 3106 * Prints the data privacy policy page and the data usage confirmation text as defined on Options > Security 3107 * If there is no page defined it only prints the default text. 3108 * 3109 * @since Zenphoto 1.5 3110 */ 3111function printDataUsageNotice() { 3112 $data = getDataUsageNotice(); 3113 echo $data['notice']; 3114 if (!empty($data['url'])) { 3115 printLinkHTML($data['url'], ' ' . $data['linktext'], $data['linktext'], null, null); 3116 } 3117} 3118 3119/** 3120 * Returns an array with predefined info about general cookies set by the system and/or plugins 3121 * 3122 * @since ZenphotoCMS 1.5.8 3123 * 3124 * @param string $section Name of the section to get: 'authenticaion', 'search', 'admin', 'cookie', 'various' or null (default) for the full array 3125 * @return array 3126 */ 3127function getCookieInfoData($section = null) { 3128 $info = array( 3129 'authentication' => array( 3130 'sectiontitle' => gettext('Authentication'), 3131 'sectiondesc' => gettext('Cookies set if logging in as an admin or as one of the various guest user types.'), 3132 'cookies' => array( 3133 'zpcms_auth_user' => gettext('Stores the zenphoto user login credentials.'), 3134 'zpcms_auth_gallery' => gettext('Stores guest user gallery access credentias.'), 3135 'zpcms_auth_search' => gettext('Stores guest user search access credentials'), 3136 'zpcms_auth_image_itemid' => gettext('Stores guest user <em>image item</em> access credentials. <em>itemid</em> refers to the ID of the image.'), 3137 'zpcms_auth_album_itemid' => gettext('Stores guest user <em>album item</em> access credentials. <em>itemid</em> refers to the ID of the album.'), 3138 'zpcms_auth_category_itemid' => gettext('Stores guest user <em>category item</em> access credentials. <em>itemid</em> refers to the ID of the category.'), 3139 'zpcms_auth_page_itemid' => gettext('Stores guest user <em>page item</em> access credentials. <em>itemid</em> refers to the ID of the zenpage page.'), 3140 'zpcms_auth_download' => gettext('Stores guest user access used by the <em>downloadlist</em> plugin.') 3141 ), 3142 ), 3143 'search' => array( 3144 'sectiontitle' => gettext('Search context (frontend)'), 3145 'sectiondesc' => gettext('These cookies help keep the search result context while browsing results'), 3146 'cookies' => array( 3147 'zpcms_search_params' => gettext('Stores search parameters of the most recent search.'), 3148 'zpcms_search_lastalbum' => gettext('Stores the last album in search context.'), 3149 'zpcms_search_parent' => gettext('Stores the previous page within search context (either the main search results or an album result).') 3150 ), 3151 ), 3152 'admin' => array( 3153 'sectiontitle' => gettext('Administration'), 3154 'sectiondesc' => gettext('These are set on the backend to help editing.'), 3155 'cookies' => array( 3156 'zpcms_admin_gallery_nesting' => gettext('Stores the setting for the nested album list display on the backend.'), 3157 'zpcms_admin_subalbum_nesting' => gettext('Stores the setting for the nested subalbum list display on the backend.'), 3158 'zpcms_admin_imagestab_imagecount' => gettext('Stores the image count on the backend images pages.'), 3159 'zpcms_admin_uploadtype' => gettext('Stores the upload method on the backend.') 3160 ), 3161 ), 3162 'cookie' => array( 3163 'sectiontitle' => gettext('Cookie related'), 3164 'sectiondesc' => '', 3165 'cookies' => array( 3166 'zpcms_setup_testcookie' => gettext('Used by setup to test if cookies are operational on the installation. May store the Zenphoto version number of the last unsuccessful run.'), 3167 'zpcms_cookie_path' => gettext('Stores the path for cookies.') 3168 ), 3169 ), 3170 'various' => array( 3171 'sectiontitle' => gettext('Various'), 3172 'sectiondesc' => gettext('Various cookies set by plugins, themes or otherwise'), 3173 'cookies' => array( 3174 'zcms_ssl' => gettext('Stores the HTTPS/SSL setting.'), 3175 'zpcms_locale' => gettext('Stores the language selection set by the <em>dynamic_locale</em> plugin.'), 3176 'zpcms_mobiletheme' => gettext('Stores if the mobile theme is defined - used by the <em>mobileTheme</em> plugin.'), 3177 'zpcms_themeswitcher_theme' => gettext('Stores the current theme selected by the <em>themeSwitcher</em> plugin.'), 3178 'zpcms_comment' => gettext('Stores information from the comment form POST for re-populaton of the form in the <em>comment_form</em> plugin.') 3179 ) 3180 ) 3181 ); 3182 if (is_null($section) && array_key_exists($section, $info)) { 3183 return $info[$section]; 3184 } else { 3185 return $info; 3186 } 3187} 3188 3189/** 3190 * Returns a definition list with predefined info about general cookies set by the system and/or plugins as a string 3191 * 3192 * @since ZenphotoCMS 1.5.8 3193 * 3194 * @param string $section Name of the section to get: 'authenticaion', 'search', 'admin', 'cookie', 'various' or null (default) for the full array 3195 * @param string $sectionheadline Add h2 to h6 to print as the section headline, h2 default. 3196 * @return string 3197 */ 3198function getCookieInfoHTML($section = null, $sectionheadline = 'h2') { 3199 $cookies = getCookieInfoData($section); 3200 $html = ''; 3201 if ($cookies) { 3202 foreach ($cookies as $section) { 3203 if (!in_array($sectionheadline, array('h2', 'h3', 'h4', 'h5', 'h6'))) { 3204 $sectionheadline = 'h2'; 3205 } 3206 $html .= '<' . $sectionheadline . '>' . $section['sectiontitle'] . '</' . $sectionheadline . '>'; 3207 $html .= '<p>' . $section['sectiondesc'] . '</p>'; 3208 if ($section['cookies']) { 3209 $html .= '<dl>'; 3210 foreach ($section['cookies'] as $key => $val) { 3211 $html .= '<dt>' . $key . '</dt>'; 3212 $html .= '<dd>' . $val . '</dd>'; 3213 } 3214 $html .= '</dl>'; 3215 } 3216 } 3217 } 3218 return $html; 3219} 3220 3221/** 3222 * Prints a definition list with predefined info about general cookies set by the system and/or plugins 3223 * 3224 * @since ZenphotoCMS 1.5.8 3225 * 3226 * @param string $section Name of the section to get: 'authenticaion', 'search', 'admin', 'cookie', 'various' or null (default) for the full array 3227 * @param string $sectionheadline Add h2 to h6 to print as the section headline, h2 default. 3228 */ 3229function printCookieInfo($section = null, $sectionheadline = 'h2') { 3230 echo getCookieInfoHTML($section, $sectionheadline); 3231} 3232 3233/** 3234 * Registers the content macro(s) 3235 * 3236 * @param array $macros Passes through the array of already registered 3237 * @return array 3238 */ 3239function getCookieInfoMacro($macros) { 3240 $macros['COOKIEINFO'] = array( 3241 'class' => 'function', 3242 'params' => array('string*', 'string*'), 3243 'value' => 'getCookieInfoHTML', 3244 'owner' => 'core', 3245 'desc' => gettext('Set %1 to the section to get, set %2 to the h2-h6 for the headline element to use.') 3246 ); 3247 return $macros; 3248} 3249 3250/** 3251 * Standins for when no captcha is enabled 3252 */ 3253class _zp_captcha { 3254 3255 var $name = NULL; // "captcha" name if no captcha plugin loaded 3256 3257 function getCaptcha($prompt) { 3258 return array('input' => NULL, 'html' => '<p class="errorbox">' . gettext('No captcha handler is enabled.') . '</p>', 'hidden' => ''); 3259 } 3260 3261 function checkCaptcha($s1, $s2) { 3262 return false; 3263 } 3264 3265} 3266 3267/** 3268 * stand-in for when there is no HTML cache plugin enabled 3269 */ 3270class _zp_HTML_cache { 3271 3272 function disable() { 3273 3274 } 3275 3276 function startHTMLCache() { 3277 3278 } 3279 3280 function abortHTMLCache() { 3281 3282 } 3283 3284 function endHTMLCache() { 3285 3286 } 3287 3288 function clearHtmlCache() { 3289 3290 } 3291 3292} 3293 3294setexifvars(); 3295 3296/** 3297 * Prints the lang="" attribute for the main <html> element. 3298 * 3299 * @since ZenphotoCMS 1.5.7 3300 * 3301 * @param string $locale Default null so the current locale is used. Or a locale like "en_US" which will get the underscores replaced by hyphens to be valid 3302 */ 3303function printLangAttribute($locale = null) { 3304 echo ' lang="' . getLangAttributeLocale($locale) . '"'; 3305} 3306