Rev Author Line No. Line
228 kaklik 1 <?php
2  
3 /**
4 * Main class.
5 * @license http://opensource.org/licenses/gpl-license.php GNU General Public License
6 * @copyright (c)2003-2006 Tamlyn Rhodes
7 * @version $Id: singapore.class.php,v 1.75 2006/09/12 11:56:11 thepavian Exp $
8 */
9  
10 //define constants for regular expressions
11 define('SG_REGEXP_PROTOCOLURL', '(?:http://|https://|ftp://|mailto:)(?:[a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,4}(?::[0-9]+)?(?:/[^ \n\r\"\'<]+)?');
12 define('SG_REGEXP_WWWURL', 'www\.(?:[a-zA-Z0-9\-]+\.)*[a-zA-Z]{2,4}(?:/[^ \n\r\"\'<]+)?');
13 define('SG_REGEXP_EMAILURL', '(?:[\w][\w\.\-]+)+@(?:[\w\-]+\.)+[a-zA-Z]{2,4}');
14  
15 /**
16 * Provides functions for handling galleries and images
17 * @package singapore
18 * @author Tamlyn Rhodes <tam at zenology dot co dot uk>
19 */
20 class Singapore
21 {
22 /**
23 * current script version
24 * @var string
25 */
26 var $version = "0.10.1";
27  
28 /**
29 * instance of a {@link sgConfig} object representing the current
30 * script configuration
31 * @var sgConfig
32 */
33 var $config;
34  
35 /**
36 * instance of the currently selected IO handler object
37 * @var sgIO_csv
38 */
39 var $io;
40  
41 /**
42 * instance of a {@link Translator}
43 * @var Translator
44 */
45 var $translator;
46  
47 /**
48 * instance of a {@link sgGallery} representing the current gallery
49 * @var sgGallery
50 */
51 var $gallery;
52  
53 /**
54 * reference to the currently selected {@link sgImage} object in the
55 * $images array of {@link $gallery}
56 * @var sgImage
57 */
58 var $image;
59  
60 /**
61 * two character code of language currently in use
62 * @var string
63 */
64 var $language = null;
65  
66 /**
67 * name of template currently in use
68 * @var string
69 */
70 var $template = null;
71  
72 /**
73 * details of current user
74 * @var sgUser
75 */
76 var $user = null;
77  
78 /**
79 * name of action requested
80 * @var string
81 */
82 var $action = null;
83  
84 /**
85 * Constructor, does all init type stuff. This code is a total mess.
86 * @param string the path to the base singapore directory
87 */
88 function Singapore($basePath = "")
89 {
90 //import class definitions
91 //io handler class included once config is loaded
92 require_once $basePath."includes/translator.class.php";
93 require_once $basePath."includes/thumbnail.class.php";
94 require_once $basePath."includes/gallery.class.php";
95 require_once $basePath."includes/config.class.php";
96 require_once $basePath."includes/image.class.php";
97 require_once $basePath."includes/user.class.php";
98  
99 //start execution timer
100 $this->scriptStartTime = microtime();
101  
102 //remove slashes
103 if(get_magic_quotes_gpc())
104 $_REQUEST = array_map(array("Singapore","arraystripslashes"), $_REQUEST);
105  
106 //desanitize request
107 $_REQUEST = array_map("htmlentities", $_REQUEST);
108  
109 //load config from singapore root directory
110 $this->config =& sgConfig::getInstance();
111 $this->config->loadConfig($basePath."singapore.ini");
112 $this->config->loadConfig($basePath."secret.ini.php");
113  
114 //if instantiated remotely...
115 if(!empty($basePath)) {
116 //...try to guess base path and relative url
117 if(empty($this->config->base_path))
118 $this->config->base_path = $basePath;
119 if(empty($this->config->base_url))
120 $this->config->base_url = $basePath;
121 //...load local config if present
122 //may over-ride guessed values above
123 $this->config->loadConfig("singapore.local.ini");
124 }
125  
126 //set current gallery to root if not specified in url
127 $galleryId = isset($_REQUEST[$this->config->url_gallery]) ? $_REQUEST[$this->config->url_gallery] : ".";
128  
129 //load config from gallery ini file (gallery.ini) if present
130 $this->config->loadConfig($basePath.$this->config->pathto_galleries.$galleryId."/gallery.ini");
131  
132 //set current template from request vars or config
133 //first, preset template to default one
134 $this->template = $this->config->default_template;
135 //then check if requested template exists
136 if(!empty($_REQUEST[$this->config->url_template])) {
137 $templates = Singapore::getListing($this->config->base_path.$this->config->pathto_templates);
138 foreach($templates->dirs as $single) {
139 if($single == $_REQUEST[$this->config->url_template]) {
140 $this->template = $single;
141 break;
142 }
143 }
144 }
145  
146 $this->config->pathto_current_template = $this->config->pathto_templates.$this->template.'/';
147 //load config from template ini file (template.ini) if present
148 $this->config->loadConfig($basePath.$this->config->pathto_current_template."template.ini");
149  
150 //set runtime values
151 $this->config->pathto_logs = $this->config->pathto_data_dir."logs/";
152 $this->config->pathto_cache = $this->config->pathto_data_dir."cache/";
153 $this->config->pathto_admin_template = $this->config->pathto_templates.$this->config->admin_template_name."/";
154  
155 //set current language from request vars or config
156 if(!empty($_REQUEST[$this->config->url_lang]))
157 $this->language = $_REQUEST[$this->config->url_lang];
158 else {
159 $this->language = $this->config->default_language;
160 if($this->config->detect_language)
161 foreach($this->getBrowserLanguages() as $lang)
162 if($lang=="en" || file_exists($basePath.$this->config->pathto_locale."singapore.".$lang.".pmo")) {
163 $this->language = $lang;
164 break;
165 }
166 }
167  
168 //read the language file
169 $this->translator =& Translator::getInstance($this->language);
170 $this->translator->readLanguageFile($this->config->base_path.$this->config->pathto_locale."singapore.".$this->language.".pmo");
171  
172 //clear the UMASK
173 umask(0);
174  
175 //include IO handler class and create instance
176 require_once $basePath."includes/io_".$this->config->io_handler.".class.php";
177 $ioClassName = "sgIO_".$this->config->io_handler;
178 $this->io = new $ioClassName($this->config);
179  
180 //load gallery and image info
181 $this->selectGallery($galleryId);
182  
183 //set character set
184 if(!empty($this->translator->languageStrings[0]["charset"]))
185 $this->character_set = $this->translator->languageStrings[0]["charset"];
186 else
187 $this->character_set = $this->config->default_charset;
188  
189 //set action to perform
190 if(empty($_REQUEST["action"])) $this->action = "view";
191 else $this->action = $_REQUEST["action"];
192  
193  
194 }
195  
196 /**
197 * Load gallery and image info
198 * @param string the id of the gallery to load (optional)
199 */
200 function selectGallery($galleryId = "")
201 {
202 if(empty($galleryId)) $galleryId = isset($_REQUEST[$this->config->url_gallery]) ? $_REQUEST[$this->config->url_gallery] : ".";
203  
204 //try to validate gallery id
205 if(strlen($galleryId)>1 && $galleryId{1} != '/') $galleryId = './'.$galleryId;
206  
207 //detect back-references to avoid file-system walking
208 if(strpos($galleryId,"../")!==false) $galleryId = ".";
209  
210 //find all ancestors to current gallery
211 $this->ancestors = array();
212 $ancestorNames = explode("/", $galleryId);
213 $numberOfAncestors = count($ancestorNames);
214  
215 //construct fully qualified gallery ids
216 $ancestorIds[0] = ".";
217 for($i=1; $i<$numberOfAncestors; $i++)
218 $ancestorIds[$i] = $ancestorIds[$i-1]."/".$ancestorNames[$i];
219  
220 //fetch galleries passing previous gallery as parent pointer
221 for($i=0; $i<$numberOfAncestors; $i++)
222 if(!$this->ancestors[$i] =& $this->io->getGallery(
223 $ancestorIds[$i], $this->ancestors[$i-1],
224 //only fetch children of bottom level gallery
225 ($i==$numberOfAncestors-1) ? 1 : 0
226 )
227 )
228 break;
229  
230 //need to remove bogus parent of root gallery created by previous step
231 unset($this->ancestors[-1]);
232  
233 //set reference to current gallery
234 $this->gallery = &$this->ancestors[count($this->ancestors)-1];
235  
236 //check if gallery was successfully fetched
237 if($this->gallery == null) {
238 $this->gallery = new sgGallery($galleryId, $this->ancestors[0]);
239 $this->gallery->name = $this->translator->_g("Gallery not found '%s'",htmlspecialchars($galleryId));
240 }
241  
242 //sort galleries and images
243 $GLOBALS["sgSortOrder"] = $this->config->gallery_sort_order;
244 if($this->config->gallery_sort_order!="x") usort($this->gallery->galleries, array("Singapore","multiSort"));
245 $GLOBALS["sgSortOrder"] = $this->config->image_sort_order;
246 if($this->config->image_sort_order!="x") usort($this->gallery->images, array("Singapore","multiSort"));
247 unset($GLOBALS["sgSortOrder"]);
248  
249 //if startat is set then cast to int otherwise startat 0
250 $this->gallery->startat = isset($_REQUEST[$this->config->url_startat]) ? (int)$_REQUEST[$this->config->url_startat] : 0;
251 $this->startat = $this->gallery->startat; //depreciated
252  
253 //select the image (if any)
254 if(!empty($_REQUEST[$this->config->url_image]))
255 $this->selectImage($_REQUEST[$this->config->url_image]);
256  
257 //load hit data
258 if($this->config->track_views || $this->config->show_views)
259 $this->io->getHits($this->gallery);
260  
261 //update and save hit data
262 if($this->config->track_views) {
263 if($this->isImagePage()) {
264 $this->image->hits++;
265 $this->image->lasthit = time();
266 } elseif($this->gallery->startat == 0) {
267 $this->gallery->hits++;
268 $this->gallery->lasthit = time();
269 }
270 $this->io->putHits($this->gallery);
271 }
272 }
273  
274 /**
275 * Selects an image from the current gallery
276 * @param mixed either the filename of the image to select or the integer
277 * index of its position in the images array
278 * @return boolean true on success; false otherwise
279 */
280 function selectImage($image)
281 {
282 if(is_string($image)) {
283 foreach($this->gallery->images as $index => $img)
284 if($img->id == $image) {
285 $this->image =& $this->gallery->images[$index];
286 return true;
287 }
288 } elseif(is_int($image) && $image >= 0 && $image < count($this->gallery->images)) {
289 $this->image =& $this->gallery->images[$image];
290 return true;
291 }
292 $this->image =& new sgImage("", $this->gallery);
293 $this->image->name = $this->translator->_g("Image not found '%s'",htmlspecialchars($image));
294 return false;
295 }
296  
297  
298 /**
299 * Obfuscates the given email address by replacing "." with "dot" and "@" with "at"
300 * @param string email address to obfuscate
301 * @param boolean override the obfuscate_email config setting (optional)
302 * @return string obfuscated email address or HTML mailto link
303 */
304 function formatEmail($email, $forceObfuscate = false)
305 {
306 if($this->config->obfuscate_email || $forceObfuscate)
307 return strtr($email,array("@" => ' <b>'.$this->translator->_g("email|at").'</b> ', "." => ' <b>'.$this->translator->_g("email|dot").'</b> '));
308 else
309 return "<a href=\"mailto:".$email."\">".$email."</a>";
310 }
311  
312  
313 /**
314 * Returns image name for image pages and gallery name for gallery pages.
315 * If either of these is empty, returns gallery_name config option.
316 *
317 * @return string Title of current page
318 */
319 function pageTitle()
320 {
321 $crumbArray = $this->crumbLineArray();
322  
323 $ret = "";
324 for($i=count($crumbArray)-1;$i>0;$i--)
325 $ret .= $crumbArray[$i]->nameForce()." &lt; ";
326 $ret .= $crumbArray[$i]->nameForce();
327  
328 return $ret;
329 }
330  
331 /**
332 * @return bool true if this is an image page; false otherwise
333 */
334 function isImagePage()
335 {
336 return !empty($this->image);
337 }
338  
339 /**
340 * @return bool true if this is a non-album gallery page; false otherwise
341 */
342 function isGalleryPage()
343 {
344 return !empty($this->gallery) && $this->gallery->galleryCount()>0;;
345 }
346  
347 /**
348 * @return bool true if this is an album page; false otherwise
349 */
350 function isAlbumPage()
351 {
352 return !$this->isGalleryPage() && !$this->isImagePage() && !empty($this->gallery);
353 }
354  
355  
356 /**
357 * @return int the script execution time in seconds rounded to two decimal places
358 */
359 function scriptExecTime()
360 {
361 $scriptStartTime = $this->scriptStartTime;
362 $scriptEndTime = microtime();
363  
364 list($usec, $sec) = explode(" ",$scriptStartTime);
365 $scriptStartTime = (float)$usec + (float)$sec;
366 list($usec, $sec) = explode(" ",$scriptEndTime);
367 $scriptEndTime = (float)$usec + (float)$sec;
368  
369 $scriptExecTime = floor(($scriptEndTime - $scriptStartTime)*100)/100;
370 return $scriptExecTime;
371 }
372  
373 /**
374 * Displays the script execution time if configured to do so
375 * @returns string the script execution time
376 */
377 function scriptExecTimeText()
378 {
379 if($this->config->show_execution_time)
380 return $this->translator->_g("Page created in %s seconds",$this->scriptExecTime());
381 else
382 return "";
383 }
384  
385 function poweredByText()
386 {
387 return $this->translator->_g("Powered by").' <a href="http://www.sgal.org/">singapore</a>';
388 }
389  
390 function allRightsReserved()
391 {
392 return $this->translator->_g("All rights reserved.");
393 }
394  
395 function licenseText()
396 {
397 return $this->translator->_g("Images may not be reproduced in any form without the express written permission of the copyright holder.");
398 }
399  
400 function adminURL()
401 {
402 return '<a href="'.$this->config->base_url.'admin.php">';
403 }
404  
405 function adminLink()
406 {
407 return $this->adminURL().$this->translator->_g("Log in")."</a>";
408 }
409  
410  
411 /**
412 * Checks to see if the user is currently logged in to admin mode. Also resets
413 * the login timeout to the current time.
414 * @returns boolean true if the user is logged in; false otherwise
415 * @static
416 */
417 function isLoggedIn()
418 {
419 if(
420 isset($this->user) &&
421 $_SESSION["sgUser"]["ip"] == $_SERVER["REMOTE_ADDR"] &&
422 (time() - $_SESSION["sgUser"]["loginTime"] < 600)
423 ) {
424 //reset loginTime to current time
425 $_SESSION["sgUser"]["loginTime"] = time();
426 return true;
427 }
428 return false;
429 }
430  
431 function loadUser($username = null)
432 {
433 if($username == null)
434 if(isset($_SESSION["sgUser"]))
435 $username = $_SESSION["sgUser"]["username"];
436 else
437 return false;
438  
439 $users = $this->io->getUsers();
440 foreach($users as $user)
441 if($user->username == $username) {
442 $this->user = $user;
443 return $user;
444 }
445 return false;
446 }
447  
448 /**
449 * Creates an array of objects each representing an item in the crumb line.
450 * @return array the items of the crumb line
451 */
452 function crumbLineArray()
453 {
454 $crumb = $this->ancestors;
455  
456 if($this->isImagePage()) $crumb[] = $this->image;
457  
458 return $crumb;
459 }
460  
461 /**
462 * @return string the complete crumb line with links
463 */
464 function crumbLineText()
465 {
466 $crumbArray = $this->crumbLineArray();
467  
468 $ret = "";
469 for($i=0;$i<count($crumbArray)-1;$i++)
470 $ret .= $crumbArray[$i]->nameLink()." &gt;\n";
471 $ret .= $crumbArray[$i]->nameForce();
472  
473 return $ret;
474 }
475  
476 function crumbLine()
477 {
478 return $this->translator->_g("crumb line|You are here:")." ".$this->crumbLineText();
479 }
480  
481 /**
482 * Generates the HTML code for imagemap_navigation
483 * @return string imagemap HTML code
484 */
485 function imageMap()
486 {
487 if(!$this->config->imagemap_navigation) return "";
488  
489 $imageWidth = $this->image->width();
490 $imageHeight = $this->image->height();
491 $middleX = round($imageWidth/2);
492 $middleY = round($imageHeight/2);
493  
494 $ret = "<map name=\"sgNavMap\" id=\"sgNavMap\">\n";
495 if($this->image->hasNext()) $ret .= '<area href="'.$this->image->nextURL().'" alt="'.$this->image->nextText().'" title="'.$this->image->nextText().'" shape="poly" ';
496 else $ret .= '<area href="'.$this->image->parentURL().'" alt="'.$this->image->parentText().'" title="'.$this->image->parentText().'" shape="poly" ';
497 $ret .= "coords=\"$middleX,$middleY,$imageWidth,$imageHeight,$imageWidth,0,$middleX,$middleY\" />\n";
498 if($this->image->hasPrev()) $ret .= '<area href="'.$this->image->prevURL().'" alt="'.$this->image->prevText().'" title="'.$this->image->prevText().'" shape="poly" ';
499 else $ret .= '<area href="'.$this->image->parentURL().'" alt="'.$this->image->parentText().'" title="'.$this->image->parentText().'" shape="poly" ';
500 $ret .= "coords=\"$middleX,$middleY,0,0,0,$imageHeight,$middleX,$middleY\" />\n";
501 $ret .= '<area href="'.$this->image->parentURL().'" alt="'.$this->image->parentText().'" title="'.$this->image->parentText().'" shape="poly" ';
502 $ret .= "coords=\"$middleX,$middleY,0,0,$imageWidth,0,$middleX,$middleY\" />\n";
503 $ret .= '</map>';
504  
505 return $ret;
506 }
507  
508 /**
509 * Generates the HTML code for the language select box
510 * @return string select box HTML code
511 */
512 function languageFlipper()
513 {
514 if(!$this->config->language_flipper) return "";
515  
516 $languageCache = $this->config->base_path.$this->config->pathto_data_dir."languages.cache";
517 // Look for the language file
518 if(!file_exists($languageCache))
519 return "";
520  
521 // Open the file
522 $fp = @fopen($languageCache, "r");
523 if (!$fp) return "";
524  
525 // Read contents
526 $str = '';
527 while (!feof($fp)) $str .= fread($fp, 1024);
528 // Unserialize
529 $availableLanguages = @unserialize($str);
530  
531 $ret = '<div class="sgLanguageFlipper">';
532 $ret .= '<form method="get" action="'.$_SERVER["PHP_SELF"]."\">\n";
533 //carry over current get vars
534 foreach($_GET as $var => $val)
535 $ret .= '<input type="hidden" name="'.$var.'" value="'.htmlspecialchars($val)."\" />\n";
536 $ret .= '<select name="'.$this->config->url_lang."\">\n";
537 $ret .= ' <option value="'.$this->config->default_language.'">'.$this->translator->_g("Select language...")."</option>\n";
538 foreach($availableLanguages as $code => $name) {
539 $ret .= ' <option value="'.$code.'"';
540 if($code == $this->language && $this->language != $this->config->default_language)
541 $ret .= 'selected="true" ';
542 $ret .= '>'.htmlentities($name)."</option>\n";
543 }
544 $ret .= "</select>\n";
545 $ret .= '<input type="submit" class="button" value="'.$this->translator->_g("Go")."\" />\n";
546 $ret .= "</form></div>\n";
547 return $ret;
548 }
549  
550 /**
551 * Generates the HTML code for the template select box
552 * @return string select box HTML code
553 */
554 function templateFlipper()
555 {
556 if(!$this->config->template_flipper) return "";
557  
558 //get list of installed templates
559 $templates = Singapore::getListing($this->config->base_path.$this->config->pathto_templates, "dirs");
560  
561 $ret = '<div class="sgTemplateFlipper">';
562 $ret .= '<form method="get" action="'.$_SERVER["PHP_SELF"]."\">\n";
563 //carry over current get vars
564 foreach($_GET as $var => $val)
565 $ret .= '<input type="hidden" name="'.$var.'" value="'.htmlspecialchars($val)."\" />\n";
566 $ret .= '<select name="'.$this->config->url_template."\">\n";
567 $ret .= ' <option value="'.$this->config->default_template.'">'.$this->translator->_g("Select template...")."</option>\n";
568 foreach($templates->dirs as $name)
569 //do not list admin template(s)
570 if(strpos($name, "admin_")===false) {
571 $ret .= ' <option value="'.$name.'"';
572 if($name == $this->template && $this->template != $this->config->default_template)
573 $ret .= 'selected="true" ';
574 $ret .= '>'.$name."</option>\n";
575 }
576 $ret .= "</select>\n";
577 $ret .= '<input type="submit" class="button" value="'.$this->translator->_g("Go")."\" />\n";
578 $ret .= "</form></div>\n";
579 return $ret;
580 }
581  
582  
583 /**
584 * @param string $seperator optional string to seperate the Gallery Tab Links
585 * @return string
586 */
587 function galleryTab($seperator = " | ")
588 {
589 $showing = $this->galleryTabShowing();
590 return Singapore::conditional($this->galleryTabLinks(), $showing.$seperator."%s", $showing);
591 }
592  
593 /**
594 * @return string
595 */
596 function galleryTabShowing()
597 {
598 if($this->isAlbumPage()) {
599 $total = $this->gallery->imageCount();
600 $perPage = $this->config->thumb_number_album;
601 } else {
602 $total = $this->gallery->galleryCount();
603 $perPage = $this->config->thumb_number_gallery;
604 }
605  
606 if($this->gallery->startat+$perPage > $total)
607 $last = $total;
608 else
609 $last = $this->gallery->startat+$perPage;
610  
611 return $this->translator->_g("Showing %s-%s of %s",($this->gallery->startat+1),$last,$total);
612 }
613  
614 /**
615 * @return string
616 */
617 function galleryTabLinks()
618 {
619 $ret = "";
620 if($this->hasPrevPage())
621 $ret .= $this->prevPageLink()." ";
622  
623 //This is for compatibility with old templates
624 //it detects if compatibility mode is on using method_exists()
625 if(!$this->gallery->isRoot() && method_exists($this, 'galleryName'))
626 $ret .= $this->gallery->parentLink();
627  
628 if($this->hasNextPage())
629 $ret .= " ".$this->nextPageLink();
630  
631 return $ret;
632 }
633  
634 function navigationLinks() {
635 $ret = "<link rel=\"Top\" title=\"".$this->config->gallery_name."\" href=\"".$this->ancestors[0]->URL()."\" />\n";
636  
637 if($this->isImagePage()) {
638 $ret .= "<link rel=\"Up\" title=\"".$this->image->parent->name()."\" href=\"".$this->image->parent->URL()."\" />\n";
639 if ($this->image->hasPrev()) {
640 $first= $this->image->firstImage();
641 $prev = $this->image->prevImage();
642 $ret .= "<link rel=\"First\" title=\"".$first->name()."\" href=\"".$first->URL()."\" />\n";
643 $ret .= "<link rel=\"Prev\" title=\"".$prev->name()."\" href=\"".$prev->URL()."\" />\n";
644 }
645 if ($this->image->hasNext()) {
646 $next = $this->image->nextImage();
647 $last = $this->image->lastImage();
648 $ret .= "<link rel=\"Next\" title=\"".$next->name()."\" href=\"".$next->URL()."\" />\n";
649 $ret .= "<link rel=\"Last\" title=\"".$last->name()."\" href=\"".$last->URL()."\" />\n";
650 //prefetch next image
651 $ret .= "<link rel=\"Prefetch\" href=\"".$next->imageURL()."\" />\n";
652 }
653 } else {
654 if(!$this->gallery->isRoot())
655 $ret .= "<link rel=\"Up\" title=\"".$this->gallery->parent->name()."\" href=\"".$this->gallery->parent->URL()."\" />\n";
656 if($this->hasPrevPage()) {
657 $ret .= "<link rel=\"Prev\" title=\"".$this->translator->_g("gallery|Previous")."\" href=\"".$this->prevPageURL()."\" />\n";
658 $ret .= "<link rel=\"First\" title=\"".$this->translator->_g("gallery|First")."\" href=\"".$this->firstPageURL()."\" />\n";
659 }
660 if($this->hasNextPage()) {
661 $ret .= "<link rel=\"Next\" title=\"".$this->translator->_g("gallery|Next")."\" href=\"".$this->nextPageURL()."\" />\n";
662 $ret .= "<link rel=\"Last\" title=\"".$this->translator->_g("gallery|Last")."\" href=\"".$this->lastPageURL()."\" />\n";
663 }
664 }
665 return $ret;
666 }
667  
668  
669 /**
670 * @return int the number of 'pages' or 'screen-fulls'
671 */
672 function galleryPageCount() {
673 if($this->isAlbumPage())
674 return intval($this->gallery->imageCount()/$this->config->thumb_number_album)+1;
675 else
676 return intval($this->gallery->galleryCount()/$this->config->thumb_number_gallery)+1;
677 }
678  
679 /**
680 * @return int
681 */
682 function lastPageIndex() {
683 if($this->isAlbumPage())
684 return ($this->galleryPageCount()-1)*
685 ($this->isAlbumPage()?$this->config->thumb_number_album:$this->config->thumb_number_gallery);
686 }
687  
688 /**
689 * @return bool true if there is at least one more page
690 */
691 function hasNextPage() {
692 if($this->isAlbumPage())
693 return count($this->gallery->images)>$this->startat+$this->config->thumb_number_album;
694 elseif($this->isGalleryPage())
695 return count($this->gallery->galleries)>$this->startat+$this->config->thumb_number_gallery;
696 elseif($this->isImagePage())
697 return isset($this->gallery->images[$this->image->index+1]);
698 }
699  
700 /**
701 * @return bool true if there is at least one previous page
702 */
703 function hasPrevPage() {
704 if($this->isAlbumPage() || $this->isGalleryPage())
705 return $this->startat > 0;
706 elseif($this->isImagePage())
707 return isset($this->gallery->images[$this->image->index-1]);
708 }
709  
710 function firstPageURL() {
711 return $this->gallery->URL(0);
712 }
713  
714 function firstPageLink() {
715 return "<a href=\"".$this->firstPageURL()."\">".$this->translator->_g("First %s", $this->itemsPerPage())."</a>";
716 }
717  
718 /**
719 * @return string the URL of the previous page
720 */
721 function prevPageURL() {
722 return $this->gallery->URL($this->startat - $this->itemsPerPage());
723 }
724  
725 function prevPageLink() {
726 return "<a href=\"".$this->prevPageURL()."\">".$this->translator->_g("Previous %s", $this->itemsPerPage())."</a>";
727 }
728  
729 /**
730 * @return string the URL of the next page
731 */
732 function nextPageURL() {
733 return $this->gallery->URL($this->startat + $this->itemsPerPage());
734 }
735  
736 function nextPageLink() {
737 return "<a href=\"".$this->nextPageURL()."\">".$this->translator->_g("Next %s", $this->itemsPerPage())."</a>";
738 }
739  
740 function lastPageURL() {
741 $perpage = $this->isAlbumPage() ? $this->config->thumb_number_album : $this->config->thumb_number_gallery;
742 return $this->gallery->URL(floor($this->gallery->itemCount() / $perpage) * $perpage);
743 }
744  
745 function lastPageLink() {
746 return "<a href=\"".$this->lastPageURL()."\">".$this->translator->_g("Last %s", $this->itemsPerPage())."</a>";
747 }
748  
749 function itemsPerPage()
750 {
751 return $this->isAlbumPage() ? $this->config->thumb_number_album : $this->config->thumb_number_gallery;
752 }
753  
754 /**
755 * @return string link for adding a comment to image
756 */
757 function imageCommentLink()
758 {
759 return "<a href=\"".
760 $this->formatURL($this->gallery->idEncoded(), $this->image->id, null, "addcomment").
761 "\">".$this->translator->_g("Add a comment")."</a>";
762 }
763  
764 /**
765 * @return array array of sgImage objects
766 */
767 function &previewThumbnailsArray()
768 {
769 $ret = array();
770 $index = $this->image->index();
771 $start = ceil($index - $this->config->thumb_number_preview/2);
772  
773 for($i = $start; $i < $start + $this->config->thumb_number_preview; $i++)
774 if(isset($this->image->parent->images[$i]))
775 $ret[$i] =& $this->image->parent->images[$i];
776  
777 return $ret;
778 }
779  
780 function previewThumbnails()
781 {
782 $thumbs =& $this->previewThumbnailsArray();
783 $index = $this->image->index();
784 $ret = "";
785 foreach($thumbs as $key => $thumb) {
786 $thumbClass = "sgThumbnailPreview";
787 if($key==$index-1) $thumbClass .= " sgThumbnailPreviewPrev";
788 elseif($key==$index) $thumbClass .= " sgThumbnailPreviewCurrent";
789 elseif($key==$index+1) $thumbClass .= " sgThumbnailPreviewNext";
790 $ret .= $thumb->thumbnailLink($thumbClass, "preview")."\n";
791 }
792  
793 return $ret;
794 }
795  
796 //////////////////////////////
797 //////ex-sgUtils methods//////
798 //////////////////////////////
799  
800 /**
801 * Callback function for sorting things
802 * @static
803 */
804 function multiSort($a, $b) {
805 switch($GLOBALS["sgSortOrder"]) {
806 case "f" :
807 case "p" : return strcmp($a->id, $b->id); //path
808 case "F" :
809 case "P" : return strcmp($b->id, $a->id); //path (reverse)
810 case "n" : return strcmp($a->name, $b->name); //name
811 case "N" : return strcmp($b->name, $a->name); //name (reverse)
812 case "i" : return strcasecmp($a->name, $b->name); //case-insensitive name
813 case "I" : return strcasecmp($b->name, $a->name); //case-insensitive name (reverse)
814 case "a" : return strcmp($a->artist, $b->artist); //artist
815 case "A" : return strcmp($b->artist, $a->artist); //artist (reverse)
816 case "d" : return strcmp($a->date, $b->date); //date
817 case "D" : return strcmp($b->date, $a->date); //date (reverse)
818 case "l" : return strcmp($a->location, $b->location); //location
819 case "L" : return strcmp($b->location, $a->location); //location (reverse)
820 }
821 }
822  
823 /**
824 * Slightly pointless method
825 */
826 function conditional($conditional, $iftrue, $iffalse = null)
827 {
828 if($conditional) return sprintf($iftrue, $conditional);
829 elseif($iffalse != null) return sprintf($iffalse, $conditional);
830 else return "";
831 }
832  
833 /**
834 * Callback function for recursively stripping slashes
835 * @static
836 */
837 function arraystripslashes($toStrip)
838 {
839 if(is_array($toStrip))
840 return array_map(array("Singapore","arraystripslashes"), $toStrip);
841 else
842 return stripslashes($toStrip);
843 }
844  
845 function thumbnailPath($gallery, $image, $width, $height, $forceSize, $mode = 1)
846 {
847 $config =& sgConfig::getInstance();
848 switch($mode) {
849 case 0 :
850 return $config->pathto_data_dir."cache/".$width."x".$height.($forceSize?"f":"").strtr("-$gallery-$image",":/?\\","----");
851 case 1 :
852 return $config->pathto_galleries.$gallery."/_thumbs/".$width."x".$height.($forceSize?"f":"").strtr("-$image",":/?\\","----");
853 }
854 }
855  
856 /**
857 * @param string relative or absolute path to directory
858 * @param string regular expression of files to return (optional)
859 * @param bool true to get hidden directories too
860 * @returns stdClass|false a data object representing the directory and its contents
861 * @static
862 */
863 function getListing($wd, $mask = null, $getHidden = false)
864 {
865 $dir = new stdClass;
866 $dir->path = realpath($wd)."/";
867 $dir->files = array();
868 $dir->dirs = array();
869  
870 $dp = opendir($dir->path);
871 if(!$dp) return false;
872  
873 while(false !== ($entry = readdir($dp)))
874 if(is_dir($dir->path.$entry)) {
875 if(($entry{0} != '.' && $entry{0} != '_') || $getHidden)
876 $dir->dirs[] = $entry;
877 } else {
878 if($mask == null || preg_match("/\.($mask)$/i",$entry))
879 $dir->files[] = $entry;
880 }
881  
882 sort($dir->files);
883 sort($dir->dirs);
884 closedir($dp);
885 return $dir;
886 }
887  
888 /**
889 * Recursively deletes all directories and files in the specified directory.
890 * USE WITH EXTREME CAUTION!!
891 * @returns boolean true on success; false otherwise
892 * @static
893 */
894 function rmdir_all($wd)
895 {
896 if(!$dp = opendir($wd)) return false;
897 $success = true;
898 while(false !== ($entry = readdir($dp))) {
899 if($entry == "." || $entry == "..") continue;
900 if(is_dir("$wd/$entry")) $success &= Singapore::rmdir_all("$wd/$entry");
901 else $success &= unlink("$wd/$entry");
902 }
903 closedir($dp);
904 $success &= rmdir($wd);
905 return $success;
906 }
907  
908 /**
909 * Returns an array of language codes specified in the Accept-Language HHTP
910 * header field of the user's browser. q= components are ignored and removed.
911 * hyphens (-) are converted to underscores (_).
912 * @return array accepted language codes
913 * @static
914 */
915 function getBrowserLanguages()
916 {
917 $langs = array();
918 foreach(explode(",",$_SERVER["HTTP_ACCEPT_LANGUAGE"]) as $bit)
919 if($pos = strpos($bit,";"))
920 $langs[] = strtr(substr($bit,0,$pos),"-","_");
921 else
922 $langs[] = strtr($bit,"-","_");
923 return $langs;
924 }
925  
926 /**
927 * Wrapper for mkdir() implementing the safe-mode hack
928 */
929 function mkdir($path)
930 {
931 $config =& sgConfig::getInstance();
932 if($config->safe_mode_hack) {
933 $connection = ftp_connect($config->ftp_server);
934 // login to ftp server
935 $result = ftp_login($connection, $config->ftp_user, $config->ftp_pass);
936  
937 // check if connection was made
938 if ((!$connection) || (!$result))
939 return false;
940  
941 ftp_chdir($connection, $config->ftp_base_path); // go to destination dir
942 if(!ftp_mkdir($connection, $path)) // create directory
943 return false;
944  
945 ftp_site($connection, "CHMOD ".$config->directory_mode." ".$path);
946 ftp_close($connection); // close connection
947 return true;
948 } else
949 return mkdir($path, octdec($config->directory_mode));
950 }
951  
952 /**
953 * Tests if $child is within or is the same path as $parent.
954 *
955 * @param string path to parent directory
956 * @param string path to child directory or file
957 * @param bool set false to prevent canonicalisation of paths (optional)
958 * @return bool true if $child is contained within or is $parent
959 */
960 function isSubPath($parent, $child, $canonicalise = true)
961 {
962 $parentPath = $canonicalise ? realpath($parent) : $parent;
963 $childPath = $canonicalise ? realpath($child) : $child;
964 return $parentPath && $childPath && substr($childPath,0,strlen($parentPath)) == $parentPath;
965 }
966  
967  
968 function isInGroup($groups1,$groups2)
969 {
970 return (bool) array_intersect(explode(" ",$groups1),explode(" ",$groups2));
971 }
972  
973 }
974  
975  
976 ?>