3 /***************************************************************************
5 * phpfspot, presents your F-Spot photo collection in Web browsers.
7 * Copyright (c) by Andreas Unterkircher
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 ***************************************************************************/
25 require_once "phpfspot_cfg.php";
26 require_once "phpfspot_db.php";
31 * this class contains the most functions which will to the major
39 * phpfspot configuration
47 * SQLite database handle to f-spot database
55 * SQLite database handle to phpfspot database
63 * Smarty template engine
64 * @link http://smarty.php.net smarty.php.net
65 * @see PHPFSPOT_TMPL()
79 * list of available, not-selected, tags
86 * true if runtime error occued
90 private $runtime_error = false;
93 * F-Spot database version
100 * class constructor ($cfg, $db, $cfg_db, $tmpl, $db_ver)
102 * this function will be called on class construct
103 * and will check requirements, loads configuration,
104 * open databases and start the user session
106 public function __construct()
109 * register PHPFSPOT class global
111 * @global PHPFSPOT $GLOBALS['phpfspot']
114 $GLOBALS['phpfspot'] =& $this;
116 $this->cfg = new PHPFSPOT_CFG;
118 /* verify config settings */
119 if($this->check_config_options()) {
123 /* set application name and version information */
124 $this->cfg->product = "phpfspot";
125 $this->cfg->version = "1.4";
127 $this->sort_orders= array(
128 'date_asc' => 'Date ↑',
129 'date_desc' => 'Date ↓',
130 'name_asc' => 'Name ↑',
131 'name_desc' => 'Name ↓',
132 'tags_asc' => 'Tags ↑',
133 'tags_desc' => 'Tags ↓',
136 /* Check necessary requirements */
137 if(!$this->check_requirements()) {
141 /******* Opening F-Spot's sqlite database *********/
143 /* Check if database file is writeable */
144 if(!is_writeable($this->cfg->fspot_db)) {
145 print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
149 /* open the database */
150 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
152 /* change sqlite temp directory, if requested */
153 if(isset($this->cfg->sqlite_temp_dir)) {
156 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
160 $this->dbver = $this->getFspotDBVersion();
162 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
163 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
167 if(!is_writeable($this->cfg->thumb_path)) {
168 print $this->cfg->thumb_path .": directory is not writeable for user ". $this->getuid() ."\n";
172 /******* Opening phpfspot's sqlite database *********/
174 /* Check if directory where the database file is stored is writeable */
175 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
176 print dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() ."\n";
180 /* Check if database file is writeable */
181 if(!is_writeable($this->cfg->phpfspot_db)) {
182 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
186 /* open the database */
187 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
189 /* change sqlite temp directory, if requested */
190 if(isset($this->cfg->sqlite_temp_dir)) {
191 $this->cfg_db->db_exec("
193 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
197 /* Check if some tables need to be created */
198 $this->check_config_table();
200 /* overload Smarty class with our own template handler */
201 require_once "phpfspot_tmpl.php";
202 $this->tmpl = new PHPFSPOT_TMPL();
204 /* check if all necessary indices exist */
205 $this->checkDbIndices();
207 /* if session is not yet started, do it now */
208 if(session_id() == "")
211 if(!isset($_SESSION['tag_condition']))
212 $_SESSION['tag_condition'] = 'or';
214 if(!isset($_SESSION['sort_order']))
215 $_SESSION['sort_order'] = 'date_desc';
217 if(!isset($_SESSION['searchfor_tag']))
218 $_SESSION['searchfor_tag'] = '';
220 // if begin_with is still set but thumbs_per_page is now 0, unset it
221 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
222 unset($_SESSION['begin_with']);
226 public function __destruct()
232 * show - generate html output
234 * this function can be called after the constructor has
235 * prepared everyhing. it will load the index.tpl smarty
236 * template. if necessary it will registere pre-selects
237 * (photo index, photo, tag search, date search) into
240 public function show()
242 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
243 $this->tmpl->assign('page_title', $this->cfg->page_title);
244 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
245 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
247 if(isset($_GET['mode'])) {
249 $_SESSION['start_action'] = $_GET['mode'];
251 switch($_GET['mode']) {
253 if(isset($_GET['tags'])) {
254 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
256 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
257 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
259 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
260 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
264 if(isset($_GET['tags'])) {
265 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
266 $_SESSION['start_action'] = 'showp';
268 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
269 $_SESSION['current_photo'] = $_GET['id'];
270 $_SESSION['start_action'] = 'showp';
272 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
273 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
275 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
276 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
280 $this->tmpl->show("export.tpl");
284 $this->tmpl->show("slideshow.tpl");
288 if(isset($_GET['tags'])) {
289 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
291 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
292 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
294 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
295 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
303 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
304 $this->tmpl->assign('date_search_enabled', true);
306 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
307 $this->tmpl->assign('from_date', $this->get_calendar('from'));
308 $this->tmpl->assign('to_date', $this->get_calendar('to'));
309 $this->tmpl->assign('content_page', 'welcome.tpl');
310 $this->tmpl->show("index.tpl");
315 * get_tags - grab all tags of f-spot's database
317 * this function will get all available tags from
318 * the f-spot database and store them within two
319 * arrays within this class for later usage. in
320 * fact, if the user requests (hide_tags) it will
321 * opt-out some of them.
323 * this function is getting called once by show()
325 private function get_tags()
327 $this->avail_tags = Array();
330 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
333 DISTINCT t1.id as id, t1.name as name
336 INNER JOIN photo_tags
337 pt2 ON pt1.photo_id=pt2.photo_id
343 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
345 t1.sort_priority ASC";
347 $result = $this->db->db_query($query_str);
351 $result = $this->db->db_query("
354 ORDER BY sort_priority ASC
358 while($row = $this->db->db_fetch_object($result)) {
360 $tag_id = $row['id'];
361 $tag_name = $row['name'];
363 /* if the user has specified to ignore this tag in phpfspot's
364 configuration, ignore it here so it does not get added to
367 if(in_array($row['name'], $this->cfg->hide_tags))
370 /* if you include the following if-clause and the user has specified
371 to only show certain tags which are specified in phpfspot's
372 configuration, ignore all others so they will not be added to the
374 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
375 !in_array($row['name'], $this->cfg->show_tags))
379 $this->tags[$tag_id] = $tag_name;
380 $this->avail_tags[$count] = $tag_id;
388 * extract all photo details
390 * retrieve all available details from f-spot's
391 * database and return them as object
392 * @param integer $idx
393 * @return object|null
395 public function get_photo_details($idx)
397 if($this->dbver < 9) {
399 SELECT p.id, p.name, p.time, p.directory_path, p.description
405 SELECT p.id, p.uri, p.time, p.description
410 /* if show_tags is set, only return details for photos which
411 are specified to be shown
413 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
415 INNER JOIN photo_tags pt
419 WHERE p.id='". $idx ."'
420 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
424 WHERE p.id='". $idx ."'
428 if($result = $this->db->db_query($query_str)) {
430 $row = $this->db->db_fetch_object($result);
432 if($this->dbver < 9) {
433 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
442 } // get_photo_details
445 * returns aligned photo names
447 * this function returns aligned (length) names for
448 * an specific photo. If the length of the name exceeds
449 * $limit the name will be shrinked (...)
450 * @param integer $idx
451 * @param integer $limit
452 * @return string|null
454 public function getPhotoName($idx, $limit = 0)
456 if($details = $this->get_photo_details($idx)) {
457 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
458 $name = $this->shrink_text($long_name, $limit);
468 * shrink text according provided limit
470 * If the length of the name exceeds $limit the
471 * text will be shortend and some content in between
472 * will be replaced with "..."
474 * @param integer $limit
477 private function shrink_text($text, $limit)
479 if($limit != 0 && strlen($text) > $limit) {
480 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
488 * translate f-spoth photo path
490 * as the full-qualified path recorded in the f-spot database
491 * is usally not the same as on the webserver, this function
492 * will replace the path with that one specified in the cfg
493 * @param string $path
496 public function translate_path($path)
498 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
503 * control HTML ouput for a single photo
505 * this function provides all the necessary information
506 * for the single photo template.
507 * @param integer photo
509 public function showPhoto($photo)
511 /* get all photos from the current photo selection */
512 $all_photos = $this->getPhotoSelection();
513 $count = count($all_photos);
515 for($i = 0; $i < $count; $i++) {
517 // $get_next will be set, when the photo which has to
518 // be displayed has been found - this means that the
519 // next available is in fact the NEXT image (for the
521 if(isset($get_next)) {
522 $next_img = $all_photos[$i];
526 /* the next photo is our NEXT photo */
527 if($all_photos[$i] == $photo) {
531 $previous_img = $all_photos[$i];
534 if($photo == $all_photos[$i]) {
539 $details = $this->get_photo_details($photo);
546 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
547 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
549 if(!file_exists($orig_path)) {
550 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
554 if(!is_readable($orig_path)) {
555 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
559 /* If the thumbnail doesn't exist yet, try to create it */
560 if(!file_exists($thumb_path)) {
561 $this->gen_thumb($photo, true);
562 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
565 /* get mime-type, height and width from the original photo */
566 $info = getimagesize($orig_path);
568 /* get EXIF information if JPEG */
569 if($info['mime'] == "image/jpeg") {
570 $meta = $this->get_meta_informations($orig_path);
573 /* If EXIF data are available, use them */
574 if(isset($meta['ExifImageWidth'])) {
575 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
577 $meta_res = $info[0] ."x". $info[1];
580 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
581 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
582 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
584 $extern_link = "index.php?mode=showp&id=". $photo;
585 $current_tags = $this->getCurrentTags();
586 if($current_tags != "") {
587 $extern_link.= "&tags=". $current_tags;
589 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
590 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
593 $this->tmpl->assign('extern_link', $extern_link);
595 if(!file_exists($thumb_path)) {
596 $this->_error("Can't open file ". $thumb_path ."\n");
600 $info_thumb = getimagesize($thumb_path);
602 $this->tmpl->assign('description', $details['description']);
603 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
605 $this->tmpl->assign('width', $info_thumb[0]);
606 $this->tmpl->assign('height', $info_thumb[1]);
607 $this->tmpl->assign('ExifMadeOn', $meta_date);
608 $this->tmpl->assign('ExifMadeWith', $meta_make);
609 $this->tmpl->assign('ExifOrigResolution', $meta_res);
610 $this->tmpl->assign('ExifFileSize', $meta_size);
612 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
613 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
614 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
616 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
617 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
618 $this->tmpl->assign('current_img', $photo);
621 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
622 $this->tmpl->assign('prev_img', $previous_img);
626 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
627 $this->tmpl->assign('next_img', $next_img);
629 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
630 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
631 $this->tmpl->assign('photo_number', $i);
632 $this->tmpl->assign('photo_count', count($all_photos));
634 $this->tmpl->show("single_photo.tpl");
639 * all available tags and tag cloud
641 * this function outputs all available tags (time ordered)
642 * and in addition output them as tag cloud (tags which have
643 * many photos will appears more then others)
645 public function getAvailableTags()
647 /* retrive tags from database */
652 $result = $this->db->db_query("
653 SELECT tag_id as id, count(tag_id) as quantity
663 while($row = $this->db->db_fetch_object($result)) {
664 $tags[$row['id']] = $row['quantity'];
667 // change these font sizes if you will
668 $max_size = 125; // max font size in %
669 $min_size = 75; // min font size in %
672 $max_sat = hexdec('cc');
673 $min_sat = hexdec('44');
675 // get the largest and smallest array values
676 $max_qty = max(array_values($tags));
677 $min_qty = min(array_values($tags));
679 // find the range of values
680 $spread = $max_qty - $min_qty;
681 if (0 == $spread) { // we don't want to divide by zero
685 // determine the font-size increment
686 // this is the increase per tag quantity (times used)
687 $step = ($max_size - $min_size)/($spread);
688 $step_sat = ($max_sat - $min_sat)/($spread);
690 // loop through our tag array
691 foreach ($tags as $key => $value) {
693 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
696 // calculate CSS font-size
697 // find the $value in excess of $min_qty
698 // multiply by the font-size increment ($size)
699 // and add the $min_size set above
700 $size = $min_size + (($value - $min_qty) * $step);
701 // uncomment if you want sizes in whole %:
704 $color = $min_sat + ($value - $min_qty) * $step_sat;
710 if(isset($this->tags[$key])) {
711 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
716 $output = substr($output, 0, strlen($output)-2);
719 } // getAvailableTags()
722 * output all selected tags
724 * this function output all tags which have been selected
725 * by the user. the selected tags are stored in the
726 * session-variable $_SESSION['selected_tags']
729 public function getSelectedTags()
731 /* retrive tags from database */
736 foreach($this->avail_tags as $tag)
738 // return all selected tags
739 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
740 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
745 $output = substr($output, 0, strlen($output)-2);
749 return "no tags selected";
752 } // getSelectedTags()
755 * add tag to users session variable
757 * this function will add the specified to users current
758 * tag selection. if a date search has been made before
759 * it will be now cleared
762 public function addTag($tag)
764 if(!isset($_SESSION['selected_tags']))
765 $_SESSION['selected_tags'] = Array();
767 if(isset($_SESSION['searchfor_tag']))
768 unset($_SESSION['searchfor_tag']);
770 if(!in_array($tag, $_SESSION['selected_tags']))
771 array_push($_SESSION['selected_tags'], $tag);
779 * remove tag to users session variable
781 * this function removes the specified tag from
782 * users current tag selection
786 public function delTag($tag)
788 if(isset($_SESSION['searchfor_tag']))
789 unset($_SESSION['searchfor_tag']);
791 if(isset($_SESSION['selected_tags'])) {
792 $key = array_search($tag, $_SESSION['selected_tags']);
793 unset($_SESSION['selected_tags'][$key]);
794 sort($_SESSION['selected_tags']);
802 * reset tag selection
804 * if there is any tag selection, it will be
807 public function resetTags()
809 if(isset($_SESSION['selected_tags']))
810 unset($_SESSION['selected_tags']);
815 * returns the value for the autocomplet tag-search
818 public function get_xml_tag_list()
820 if(!isset($_GET['search']) || !is_string($_GET['search']))
821 $_GET['search'] = '';
826 /* retrive tags from database */
829 $matched_tags = Array();
831 header("Content-Type: text/xml");
833 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
834 $string.= "<results>\n";
836 foreach($this->avail_tags as $tag)
838 if(!empty($_GET['search']) &&
839 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
840 count($matched_tags) < $length) {
842 $count = $this->get_num_photos($tag);
845 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
848 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
854 /* if we have collected enough items, break out */
855 if(count($matched_tags) >= $length)
859 $string.= "</results>\n";
863 } // get_xml_tag_list()
869 * if a specific photo was requested (external link)
870 * unset the session variable now
872 public function resetPhotoView()
874 if(isset($_SESSION['current_photo']))
875 unset($_SESSION['current_photo']);
877 } // resetPhotoView();
882 * if any tag search has taken place, reset it now
884 public function resetTagSearch()
886 if(isset($_SESSION['searchfor_tag']))
887 unset($_SESSION['searchfor_tag']);
889 } // resetTagSearch()
894 * if any name search has taken place, reset it now
896 public function resetNameSearch()
898 if(isset($_SESSION['searchfor_name']))
899 unset($_SESSION['searchfor_name']);
901 } // resetNameSearch()
906 * if any date search has taken place, reset
909 public function resetDateSearch()
911 if(isset($_SESSION['from_date']))
912 unset($_SESSION['from_date']);
913 if(isset($_SESSION['to_date']))
914 unset($_SESSION['to_date']);
916 } // resetDateSearch();
919 * return all photo according selection
921 * this function returns all photos based on
922 * the tag-selection, tag- or date-search.
923 * the tag-search also has to take care of AND
924 * and OR conjunctions
927 public function getPhotoSelection()
929 $matched_photos = Array();
930 $additional_where_cond = "";
932 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
933 $from_date = $_SESSION['from_date'];
934 $to_date = $_SESSION['to_date'];
935 $additional_where_cond.= "
936 p.time>='". $from_date ."'
938 p.time<='". $to_date ."'
942 if(isset($_SESSION['searchfor_name'])) {
943 if($this->dbver < 9) {
944 $additional_where_cond.= "
946 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
948 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
953 $additional_where_cond.= "
955 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
957 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
963 if(isset($_SESSION['sort_order'])) {
964 $order_str = $this->get_sort_order();
967 /* return a search result */
968 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
970 SELECT DISTINCT pt1.photo_id
972 INNER JOIN photo_tags pt2
973 ON pt1.photo_id=pt2.photo_id
980 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
982 if(isset($additional_where_cond) && !empty($additional_where_cond))
983 $query_str.= "AND ". $additional_where_cond ." ";
985 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
986 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
989 if(isset($order_str))
990 $query_str.= $order_str;
992 $result = $this->db->db_query($query_str);
993 while($row = $this->db->db_fetch_object($result)) {
994 array_push($matched_photos, $row['photo_id']);
996 return $matched_photos;
999 /* return according the selected tags */
1000 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1002 foreach($_SESSION['selected_tags'] as $tag)
1003 $selected.= $tag .",";
1004 $selected = substr($selected, 0, strlen($selected)-1);
1006 /* photo has to match at least on of the selected tags */
1007 if($_SESSION['tag_condition'] == 'or') {
1009 SELECT DISTINCT pt1.photo_id
1011 INNER JOIN photo_tags pt2
1012 ON pt1.photo_id=pt2.photo_id
1016 ON pt1.photo_id=p.id
1017 WHERE pt1.tag_id IN (". $selected .")
1019 if(isset($additional_where_cond) && !empty($additional_where_cond))
1020 $query_str.= "AND ". $additional_where_cond ." ";
1022 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1023 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1026 if(isset($order_str))
1027 $query_str.= $order_str;
1029 /* photo has to match all selected tags */
1030 elseif($_SESSION['tag_condition'] == 'and') {
1032 if(count($_SESSION['selected_tags']) >= 32) {
1033 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1034 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1038 /* Join together a table looking like
1040 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1042 so the query can quickly return all images matching the
1043 selected tags in an AND condition
1048 SELECT DISTINCT pt1.photo_id
1052 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1059 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1061 INNER JOIN photo_tags pt". ($i+2) ."
1062 ON pt1.photo_id=pt". ($i+2) .".photo_id
1067 ON pt1.photo_id=p.id
1069 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1070 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1072 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1075 if(isset($additional_where_cond) && !empty($additional_where_cond))
1076 $query_str.= "AND ". $additional_where_cond;
1078 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1079 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1082 if(isset($order_str))
1083 $query_str.= $order_str;
1087 $result = $this->db->db_query($query_str);
1088 while($row = $this->db->db_fetch_object($result)) {
1089 array_push($matched_photos, $row['photo_id']);
1091 return $matched_photos;
1094 /* return all available photos */
1096 SELECT DISTINCT p.id
1098 LEFT JOIN photo_tags pt
1104 if(isset($additional_where_cond) && !empty($additional_where_cond))
1105 $query_str.= "WHERE ". $additional_where_cond ." ";
1107 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1108 if(isset($additional_where_cond) && !empty($additional_where_cond))
1109 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1111 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1114 if(isset($order_str))
1115 $query_str.= $order_str;
1117 $result = $this->db->db_query($query_str);
1118 while($row = $this->db->db_fetch_object($result)) {
1119 array_push($matched_photos, $row['id']);
1121 return $matched_photos;
1123 } // getPhotoSelection()
1126 * control HTML ouput for photo index
1128 * this function provides all the necessary information
1129 * for the photo index template.
1131 public function showPhotoIndex()
1133 $photos = $this->getPhotoSelection();
1135 $count = count($photos);
1137 /* if all thumbnails should be shown on one page */
1138 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1142 /* thumbnails should be splitted up in several pages */
1143 elseif($this->cfg->thumbs_per_page > 0) {
1145 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1149 $begin_with = $_SESSION['begin_with'];
1152 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1156 $images[$thumbs] = Array();
1157 $img_height[$thumbs] = Array();
1158 $img_width[$thumbs] = Array();
1159 $img_id[$thumbs] = Array();
1160 $img_name[$thumbs] = Array();
1161 $img_fullname[$thumbs] = Array();
1162 $img_title = Array();
1164 for($i = $begin_with; $i < $end_with; $i++) {
1166 if(isset($photos[$i])) {
1168 $images[$thumbs] = $photos[$i];
1169 $img_id[$thumbs] = $i;
1170 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1171 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1172 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1174 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1176 if(file_exists($thumb_path)) {
1177 $info = getimagesize($thumb_path);
1178 $img_width[$thumbs] = $info[0];
1179 $img_height[$thumbs] = $info[1];
1185 // +1 for for smarty's selection iteration
1188 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1189 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1191 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1192 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1193 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1196 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1197 $this->tmpl->assign('tag_result', 1);
1200 /* do we have to display the page selector ? */
1201 if($this->cfg->thumbs_per_page != 0) {
1205 /* calculate the page switchers */
1206 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1207 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1209 if($begin_with != 0)
1210 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1211 if($end_with < $count)
1212 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1214 $photo_per_page = $this->cfg->thumbs_per_page;
1215 $last_page = ceil($count / $photo_per_page);
1217 /* get the current selected page */
1218 if($begin_with == 0) {
1222 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1229 for($i = 1; $i <= $last_page; $i++) {
1231 if($current_page == $i)
1232 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1233 elseif($current_page-1 == $i || $current_page+1 == $i)
1234 $style = "style=\"font-size: 105%;\"";
1235 elseif(($current_page-5 >= $i) && ($i != 1) ||
1236 ($current_page+5 <= $i) && ($i != $last_page))
1237 $style = "style=\"font-size: 75%;\"";
1241 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1244 $select.= ">". $i ."</a> ";
1246 // until 9 pages we show the selector from 1-9
1247 if($last_page <= 9) {
1248 $page_select.= $select;
1251 if($i == 1 /* first page */ ||
1252 $i == $last_page /* last page */ ||
1253 $i == $current_page /* current page */ ||
1254 $i == ceil($last_page * 0.25) /* first quater */ ||
1255 $i == ceil($last_page * 0.5) /* half */ ||
1256 $i == ceil($last_page * 0.75) /* third quater */ ||
1257 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1258 (in_array($i, array($last_page, $last_page-1, $last_page-2, $last_page-3, $last_page-4, $last_page-5)) && $current_page >= $last_page-4) /* the last 6 */ ||
1259 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1260 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1262 $page_select.= $select;
1270 $page_select.= "......... ";
1275 /* only show the page selector if we have more then one page */
1277 $this->tmpl->assign('page_selector', $page_select);
1281 $current_tags = $this->getCurrentTags();
1282 $extern_link = "index.php?mode=showpi";
1283 $rss_link = "index.php?mode=rss";
1284 if($current_tags != "") {
1285 $extern_link.= "&tags=". $current_tags;
1286 $rss_link.= "&tags=". $current_tags;
1288 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1289 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1290 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1293 $export_link = "index.php?mode=export";
1294 $slideshow_link = "index.php?mode=slideshow";
1296 $this->tmpl->assign('extern_link', $extern_link);
1297 $this->tmpl->assign('slideshow_link', $slideshow_link);
1298 $this->tmpl->assign('export_link', $export_link);
1299 $this->tmpl->assign('rss_link', $rss_link);
1300 $this->tmpl->assign('count', $count);
1301 $this->tmpl->assign('width', $this->cfg->thumb_width);
1302 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1303 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1304 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1305 $this->tmpl->assign('images', $images);
1306 $this->tmpl->assign('img_width', $img_width);
1307 $this->tmpl->assign('img_height', $img_height);
1308 $this->tmpl->assign('img_id', $img_id);
1309 $this->tmpl->assign('img_name', $img_name);
1310 $this->tmpl->assign('img_fullname', $img_fullname);
1311 $this->tmpl->assign('img_title', $img_title);
1312 $this->tmpl->assign('thumbs', $thumbs);
1314 $this->tmpl->show("photo_index.tpl");
1316 /* if we are returning to photo index from an photo-view,
1317 scroll the window to the last shown photo-thumbnail.
1318 after this, unset the last_photo session variable.
1320 if(isset($_SESSION['last_photo'])) {
1321 print "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1322 unset($_SESSION['last_photo']);
1325 } // showPhotoIndex()
1328 * show credit template
1330 public function showCredits()
1332 $this->tmpl->assign('version', $this->cfg->version);
1333 $this->tmpl->assign('product', $this->cfg->product);
1334 $this->tmpl->assign('db_version', $this->dbver);
1335 $this->tmpl->show("credits.tpl");
1340 * create thumbnails for the requested width
1342 * this function creates image thumbnails of $orig_image
1343 * stored as $thumb_image. It will check if the image is
1344 * in a supported format, if necessary rotate the image
1345 * (based on EXIF orientation meta headers) and re-sizing.
1346 * @param string $orig_image
1347 * @param string $thumb_image
1348 * @param integer $width
1351 public function create_thumbnail($orig_image, $thumb_image, $width)
1353 if(!file_exists($orig_image)) {
1357 $mime = $this->get_mime_info($orig_image);
1359 /* check if original photo is a support image type */
1360 if(!$this->checkifImageSupported($mime))
1367 $meta = $this->get_meta_informations($orig_image);
1373 switch($meta['Orientation']) {
1374 case 1: /* top, left */
1375 /* nothing to do */ break;
1376 case 2: /* top, right */
1377 $rotate = 0; $flip_hori = true; break;
1378 case 3: /* bottom, left */
1379 $rotate = 180; break;
1380 case 4: /* bottom, right */
1381 $flip_vert = true; break;
1382 case 5: /* left side, top */
1383 $rotate = 90; $flip_vert = true; break;
1384 case 6: /* right side, top */
1385 $rotate = 90; break;
1386 case 7: /* left side, bottom */
1387 $rotate = 270; $flip_vert = true; break;
1388 case 8: /* right side, bottom */
1389 $rotate = 270; break;
1392 $src_img = @imagecreatefromjpeg($orig_image);
1398 $src_img = @imagecreatefrompng($orig_image);
1402 case 'image/x-portable-pixmap':
1404 $src_img = new Imagick($orig_image);
1405 $handler = "imagick";
1410 if(!isset($src_img) || empty($src_img)) {
1411 print "Can't load image from ". $orig_image ."\n";
1419 /* grabs the height and width */
1420 $cur_width = imagesx($src_img);
1421 $cur_height = imagesy($src_img);
1423 // If requested width is more then the actual image width,
1424 // do not generate a thumbnail, instead safe the original
1425 // as thumbnail but with lower quality. But if the image
1426 // is to heigh too, then we still have to resize it.
1427 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1428 $result = imagejpeg($src_img, $thumb_image, 75);
1429 imagedestroy($src_img);
1436 $cur_width = $src_img->getImageWidth();
1437 $cur_height = $src_img->getImageHeight();
1439 // If requested width is more then the actual image width,
1440 // do not generate a thumbnail, instead safe the original
1441 // as thumbnail but with lower quality. But if the image
1442 // is to heigh too, then we still have to resize it.
1443 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1444 $src_img->setCompressionQuality(75);
1445 $src_img->setImageFormat('jpeg');
1446 $src_img->writeImage($thumb_image);
1448 $src_img->destroy();
1455 // If the image will be rotate because EXIF orientation said so
1456 // 'virtually rotate' the image for further calculations
1457 if($rotate == 90 || $rotate == 270) {
1459 $cur_width = $cur_height;
1463 /* calculates aspect ratio */
1464 $aspect_ratio = $cur_height / $cur_width;
1467 if($aspect_ratio < 1) {
1469 $new_h = abs($new_w * $aspect_ratio);
1471 /* 'virtually' rotate the image and calculate it's ratio */
1472 $tmp_w = $cur_height;
1473 $tmp_h = $cur_width;
1474 /* now get the ratio from the 'rotated' image */
1475 $tmp_ratio = $tmp_h/$tmp_w;
1476 /* now calculate the new dimensions */
1478 $tmp_h = abs($tmp_w * $tmp_ratio);
1480 // now that we know, how high they photo should be, if it
1481 // gets rotated, use this high to scale the image
1483 $new_w = abs($new_h / $aspect_ratio);
1485 // If the image will be rotate because EXIF orientation said so
1486 // now 'virtually rotate' back the image for the image manipulation
1487 if($rotate == 90 || $rotate == 270) {
1498 /* creates new image of that size */
1499 $dst_img = imagecreatetruecolor($new_w, $new_h);
1501 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1503 /* copies resized portion of original image into new image */
1504 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1506 /* needs the image to be flipped horizontal? */
1508 $this->_debug("(FLIP)");
1509 $dst_img = $this->flipImage($dst_img, 'hori');
1511 /* needs the image to be flipped vertical? */
1513 $this->_debug("(FLIP)");
1514 $dst_img = $this->flipImage($dst_img, 'vert');
1518 $this->_debug("(ROTATE)");
1519 $dst_img = $this->rotateImage($dst_img, $rotate);
1522 /* write down new generated file */
1523 $result = imagejpeg($dst_img, $thumb_image, 75);
1525 /* free your mind */
1526 imagedestroy($dst_img);
1527 imagedestroy($src_img);
1529 if($result === false) {
1530 print "Can't write thumbnail ". $thumb_image ."\n";
1540 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1542 /* needs the image to be flipped horizontal? */
1544 $this->_debug("(FLIP)");
1545 $src_img->rotateImage(new ImagickPixel(), 90);
1546 $src_img->flipImage();
1547 $src_img->rotateImage(new ImagickPixel(), -90);
1549 /* needs the image to be flipped vertical? */
1551 $this->_debug("(FLIP)");
1552 $src_img->flipImage();
1556 $this->_debug("(ROTATE)");
1557 $src_img->rotateImage(new ImagickPixel(), $rotate);
1560 $src_img->setCompressionQuality(75);
1561 $src_img->setImageFormat('jpeg');
1563 if(!$src_img->writeImage($thumb_image)) {
1564 print "Can't write thumbnail ". $thumb_image ."\n";
1569 $src_img->destroy();
1576 } // create_thumbnail()
1579 * return all exif meta data from the file
1580 * @param string $file
1583 public function get_meta_informations($file)
1585 return exif_read_data($file);
1587 } // get_meta_informations()
1590 * create phpfspot own sqlite database
1592 * this function creates phpfspots own sqlite database
1593 * if it does not exist yet. this own is used to store
1594 * some necessary informations (md5 sum's, ...).
1596 public function check_config_table()
1598 // if the config table doesn't exist yet, create it
1599 if(!$this->cfg_db->db_check_table_exists("images")) {
1600 $this->cfg_db->db_exec("
1601 CREATE TABLE images (
1602 img_idx int primary key,
1608 } // check_config_table
1611 * Generates a thumbnail from photo idx
1613 * This function will generate JPEG thumbnails from provided F-Spot photo
1616 * 1. Check if all thumbnail generations (width) are already in place and
1618 * 2. Check if the md5sum of the original file has changed
1619 * 3. Generate the thumbnails if needed
1620 * @param integer $idx
1621 * @param integer $force
1622 * @param boolean $overwrite
1624 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1628 $resolutions = Array(
1629 $this->cfg->thumb_width,
1630 $this->cfg->photo_width,
1631 $this->cfg->mini_width,
1634 /* get details from F-Spot's database */
1635 $details = $this->get_photo_details($idx);
1637 /* calculate file MD5 sum */
1638 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1640 if(!file_exists($full_path)) {
1641 $this->_error("File ". $full_path ." does not exist\n");
1645 if(!is_readable($full_path)) {
1646 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1650 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1652 /* If Nikon NEF format, we need to treat it another way */
1653 if(isset($this->cfg->dcraw_bin) &&
1654 file_exists($this->cfg->dcraw_bin) &&
1655 is_executable($this->cfg->dcraw_bin) &&
1656 preg_match('/\.nef$/i', $details['uri'])) {
1658 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1660 /* if PPM file does not exist, let dcraw convert it from NEF */
1661 if(!file_exists($ppm_path)) {
1662 system($this->cfg->dcraw_bin ." -a ". $full_path);
1665 /* for now we handle the PPM instead of the NEF */
1666 $full_path = $ppm_path;
1670 $file_md5 = md5_file($full_path);
1673 foreach($resolutions as $resolution) {
1675 $generate_it = false;
1677 $thumb_sub_path = substr($file_md5, 0, 2);
1678 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1680 /* if thumbnail-subdirectory does not exist yet, create it */
1681 if(!file_exists(dirname($thumb_path))) {
1682 mkdir(dirname($thumb_path), 0755);
1685 /* if the thumbnail file doesn't exist, create it */
1686 if(!file_exists($thumb_path)) {
1687 $generate_it = true;
1689 /* if the file hasn't changed there is no need to regen the thumb */
1690 elseif($file_md5 != $this->getMD5($idx) || $force) {
1691 $generate_it = true;
1694 if($generate_it || $overwrite) {
1696 $this->_debug(" ". $resolution ."px");
1697 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1705 $this->_debug(" already exist");
1708 /* set the new/changed MD5 sum for the current photo */
1710 $this->setMD5($idx, $file_md5);
1713 $this->_debug("\n");
1718 * returns stored md5 sum for a specific photo
1720 * this function queries the phpfspot database for a
1721 * stored MD5 checksum of the specified photo
1722 * @param integer $idx
1723 * @return string|null
1725 public function getMD5($idx)
1727 $result = $this->cfg_db->db_query("
1730 WHERE img_idx='". $idx ."'
1736 $img = $this->cfg_db->db_fetch_object($result);
1737 return $img['img_md5'];
1742 * set MD5 sum for the specific photo
1743 * @param integer $idx
1744 * @param string $md5
1746 private function setMD5($idx, $md5)
1748 $result = $this->cfg_db->db_exec("
1749 REPLACE INTO images (img_idx, img_md5)
1750 VALUES ('". $idx ."', '". $md5 ."')
1756 * store current tag condition
1758 * this function stores the current tag condition
1759 * (AND or OR) in the users session variables
1760 * @param string $mode
1763 public function setTagCondition($mode)
1765 $_SESSION['tag_condition'] = $mode;
1769 } // setTagCondition()
1772 * invoke tag & date search
1774 * this function will return all matching tags and store
1775 * them in the session variable selected_tags. furthermore
1776 * it also handles the date search.
1777 * getPhotoSelection() will then only return the matching
1781 public function startSearch()
1783 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1784 $from = $_POST['from'];
1786 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
1790 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
1791 $searchfor_tag = $_POST['for_tag'];
1792 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
1795 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
1796 $searchfor_name = $_POST['for_name'];
1797 $_SESSION['searchfor_name'] = $_POST['for_name'];
1802 if(isset($from) && !empty($from))
1803 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1805 unset($_SESSION['from_date']);
1807 if(isset($to) && !empty($to))
1808 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1810 unset($_SESSION['to_date']);
1812 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
1813 /* new search, reset the current selected tags */
1814 $_SESSION['selected_tags'] = Array();
1815 foreach($this->avail_tags as $tag) {
1816 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
1817 array_push($_SESSION['selected_tags'], $tag);
1826 * updates sort order in session variable
1828 * this function is invoked by RPC and will sort the requested
1829 * sort order in the session variable.
1830 * @param string $sort_order
1833 public function updateSortOrder($order)
1835 if(isset($this->sort_orders[$order])) {
1836 $_SESSION['sort_order'] = $order;
1840 return "unkown error";
1842 } // updateSortOrder()
1847 * this function rotates the image according the
1849 * @param string $img
1850 * @param integer $degress
1853 private function rotateImage($img, $degrees)
1855 if(function_exists("imagerotate")) {
1856 $img = imagerotate($img, $degrees, 0);
1858 function imagerotate($src_img, $angle)
1860 $src_x = imagesx($src_img);
1861 $src_y = imagesy($src_img);
1862 if ($angle == 180) {
1866 elseif ($src_x <= $src_y) {
1870 elseif ($src_x >= $src_y) {
1875 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1876 imagealphablending($rotate, false);
1881 for ($y = 0; $y < ($src_y); $y++) {
1882 for ($x = 0; $x < ($src_x); $x++) {
1883 $color = imagecolorat($src_img, $x, $y);
1884 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1890 for ($y = 0; $y < ($src_y); $y++) {
1891 for ($x = 0; $x < ($src_x); $x++) {
1892 $color = imagecolorat($src_img, $x, $y);
1893 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1899 for ($y = 0; $y < ($src_y); $y++) {
1900 for ($x = 0; $x < ($src_x); $x++) {
1901 $color = imagecolorat($src_img, $x, $y);
1902 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1916 $img = imagerotate($img, $degrees);
1925 * returns flipped image
1927 * this function will return an either horizontal or
1928 * vertical flipped truecolor image.
1929 * @param string $image
1930 * @param string $mode
1933 private function flipImage($image, $mode)
1935 $w = imagesx($image);
1936 $h = imagesy($image);
1937 $flipped = imagecreatetruecolor($w, $h);
1941 for ($y = 0; $y < $h; $y++) {
1942 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1946 for ($x = 0; $x < $w; $x++) {
1947 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1957 * return all assigned tags for the specified photo
1958 * @param integer $idx
1961 private function get_photo_tags($idx)
1963 $result = $this->db->db_query("
1966 INNER JOIN photo_tags pt
1968 WHERE pt.photo_id='". $idx ."'
1973 while($row = $this->db->db_fetch_object($result)) {
1974 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
1976 $tags[$row['id']] = $row['name'];
1981 } // get_photo_tags()
1984 * create on-the-fly images with text within
1985 * @param string $txt
1986 * @param string $color
1987 * @param integer $space
1988 * @param integer $font
1991 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1993 if (strlen($color) != 6)
1996 $int = hexdec($color);
1997 $h = imagefontheight($font);
1998 $fw = imagefontwidth($font);
1999 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2000 $lines = count($txt);
2001 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2002 $bg = imagecolorallocate($im, 255, 255, 255);
2003 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2006 foreach ($txt as $text) {
2007 $x = (($w - ($fw * strlen($text))) / 2);
2008 imagestring($im, $font, $x, $y, $text, $color);
2009 $y += ($h + $space);
2012 Header("Content-type: image/png");
2015 } // showTextImage()
2018 * check if all requirements are met
2021 private function check_requirements()
2023 if(!function_exists("imagecreatefromjpeg")) {
2024 print "PHP GD library extension is missing<br />\n";
2028 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2029 print "PHP SQLite3 library extension is missing<br />\n";
2033 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2034 ini_set('track_errors', 1);
2035 @include_once 'HTML/AJAX/Server.php';
2036 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2037 print "PEAR HTML_AJAX package is missing<br />\n";
2040 @include_once 'Calendar/Calendar.php';
2041 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2042 print "PEAR Calendar package is missing<br />\n";
2045 @include_once 'Console/Getopt.php';
2046 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2047 print "PEAR Console_Getopt package is missing<br />\n";
2050 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2051 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2052 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2055 ini_restore('track_errors');
2062 } // check_requirements()
2064 private function _debug($text)
2066 if($this->fromcmd) {
2073 * check if specified MIME type is supported
2074 * @param string $mime
2077 public function checkifImageSupported($mime)
2079 $supported_types = Array(
2082 "image/x-portable-pixmap",
2086 if(in_array($mime, $supported_types))
2091 } // checkifImageSupported()
2095 * @param string $text
2097 public function _error($text)
2099 switch($this->cfg->logging) {
2102 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2103 print $text ."<br />\n";
2109 error_log($text, 3, $his->cfg->log_file);
2113 $this->runtime_error = true;
2118 * output calendard input fields
2119 * @param string $mode
2122 private function get_calendar($mode)
2124 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2125 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2126 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2128 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2129 if(!isset($_SESSION[$mode .'_date']))
2130 $output.= " disabled=\"disabled\"";
2132 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2133 if(!isset($_SESSION[$mode .'_date']))
2134 $output.= " disabled=\"disabled\"";
2136 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2137 if(!isset($_SESSION[$mode .'_date']))
2138 $output.= " disabled=\"disabled\"";
2146 * output calendar matrix
2147 * @param integer $year
2148 * @param integer $month
2149 * @param integer $day
2151 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2153 if (!isset($year)) $year = date('Y');
2154 if (!isset($month)) $month = date('m');
2155 if (!isset($day)) $day = date('d');
2160 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2161 require_once CALENDAR_ROOT.'Day.php';
2164 $month = new Calendar_Month_Weekdays($year,$month);
2167 $prevStamp = $month->prevMonth(true);
2168 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2169 $nextStamp = $month->nextMonth(true);
2170 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2172 $selectedDays = array (
2173 new Calendar_Day($year,$month,$day),
2174 new Calendar_Day($year,12,25),
2177 // Build the days in the month
2178 $month->build($selectedDays);
2180 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2181 $this->tmpl->assign('prev_month', $prev);
2182 $this->tmpl->assign('next_month', $next);
2184 while ( $day = $month->fetch() ) {
2186 if(!isset($matrix[$rows]))
2187 $matrix[$rows] = Array();
2191 $dayStamp = $day->thisDay(true);
2192 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2194 // isFirst() to find start of week
2195 if ( $day->isFirst() )
2198 if ( $day->isSelected() ) {
2199 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2200 } else if ( $day->isEmpty() ) {
2201 $string.= "<td> </td>\n";
2203 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2206 // isLast() to find end of week
2207 if ( $day->isLast() )
2208 $string.= "</tr>\n";
2210 $matrix[$rows][$cols] = $string;
2220 $this->tmpl->assign('matrix', $matrix);
2221 $this->tmpl->assign('rows', $rows);
2222 $this->tmpl->show("calendar.tpl");
2224 } // get_calendar_matrix()
2227 * output export page
2228 * @param string $mode
2230 public function getExport($mode)
2232 $pictures = $this->getPhotoSelection();
2233 $current_tags = $this->getCurrentTags();
2235 foreach($pictures as $picture) {
2237 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2238 if($current_tags != "") {
2239 $orig_url.= "&tags=". $current_tags;
2241 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2242 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2245 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2250 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2251 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2255 // "[%pictureurl% %thumbnailurl%]"
2256 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2259 case 'MoinMoinList':
2260 // " * [%pictureurl% %thumbnailurl%]"
2261 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2272 public function getRSSFeed()
2274 Header("Content-type: text/xml; charset=utf-8");
2275 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2278 xmlns:media="http://search.yahoo.com/mrss/"
2279 xmlns:dc="http://purl.org/dc/elements/1.1/"
2282 <title>phpfspot</title>
2283 <description>phpfspot RSS feed</description>
2284 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2285 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2286 <generator>phpfspot</generator>
2289 $pictures = $this->getPhotoSelection();
2290 $current_tags = $this->getCurrentTags();
2292 foreach($pictures as $picture) {
2294 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2295 if($current_tags != "") {
2296 $orig_url.= "&tags=". $current_tags;
2298 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2299 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2302 $details = $this->get_photo_details($picture);
2304 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2305 $thumb_html = htmlspecialchars("
2306 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2308 ". $details['description']);
2310 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2312 /* get EXIF information if JPEG */
2313 if($details['mime'] == "image/jpeg") {
2314 $meta = $this->get_meta_informations($orig_path);
2317 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2321 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2322 <link><?php print htmlspecialchars($orig_url); ?></link>
2323 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2324 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2326 <?php print $thumb_html; ?>
2328 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2343 * return all selected tags as one string
2346 private function getCurrentTags()
2349 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2350 foreach($_SESSION['selected_tags'] as $tag)
2351 $current_tags.= $tag .",";
2352 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2354 return $current_tags;
2356 } // getCurrentTags()
2359 * return the current photo
2361 public function getCurrentPhoto()
2363 if(isset($_SESSION['current_photo'])) {
2364 print $_SESSION['current_photo'];
2366 } // getCurrentPhoto()
2369 * tells the client browser what to do
2371 * this function is getting called via AJAX by the
2372 * client browsers. it will tell them what they have
2373 * to do next. This is necessary for directly jumping
2374 * into photo index or single photo view when the are
2375 * requested with specific URLs
2378 public function whatToDo()
2380 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2381 return "show_photo";
2383 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2384 return "showpi_tags";
2386 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2390 return "nothing special";
2395 * return the current process-user
2398 private function getuid()
2400 if($uid = posix_getuid()) {
2401 if($user = posix_getpwuid($uid)) {
2402 return $user['name'];
2411 * returns a select-dropdown box to select photo index sort parameters
2412 * @param array $params
2413 * @param smarty $smarty
2416 public function smarty_sort_select_list($params, &$smarty)
2420 foreach($this->sort_orders as $key => $value) {
2421 $output.= "<option value=\"". $key ."\"";
2422 if($key == $_SESSION['sort_order']) {
2423 $output.= " selected=\"selected\"";
2425 $output.= ">". $value ."</option>";
2430 } // smarty_sort_select_list()
2433 * returns the currently selected sort order
2436 private function get_sort_order()
2438 switch($_SESSION['sort_order']) {
2440 return " ORDER BY p.time ASC";
2443 return " ORDER BY p.time DESC";
2446 if($this->dbver < 9) {
2447 return " ORDER BY p.name ASC";
2450 return " ORDER BY basename(p.uri) ASC";
2454 if($this->dbver < 9) {
2455 return " ORDER BY p.name DESC";
2458 return " ORDER BY basename(p.uri) DESC";
2462 return " ORDER BY t.name ASC ,p.time ASC";
2465 return " ORDER BY t.name DESC ,p.time ASC";
2469 } // get_sort_order()
2472 * return the next to be shown slide show image
2474 * this function returns the URL of the next image
2475 * in the slideshow sequence.
2478 public function getNextSlideShowImage()
2480 $all_photos = $this->getPhotoSelection();
2482 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2483 $_SESSION['slideshow_img'] = 0;
2485 $_SESSION['slideshow_img']++;
2487 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2489 } // getNextSlideShowImage()
2492 * return the previous to be shown slide show image
2494 * this function returns the URL of the previous image
2495 * in the slideshow sequence.
2498 public function getPrevSlideShowImage()
2500 $all_photos = $this->getPhotoSelection();
2502 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2503 $_SESSION['slideshow_img'] = 0;
2505 $_SESSION['slideshow_img']--;
2507 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2509 } // getPrevSlideShowImage()
2511 public function resetSlideShow()
2513 if(isset($_SESSION['slideshow_img']))
2514 unset($_SESSION['slideshow_img']);
2516 } // resetSlideShow()
2521 * this function will get all photos from the fspot
2522 * database and randomly return ONE entry
2524 * saddly there is yet no sqlite3 function which returns
2525 * the bulk result in array, so we have to fill up our
2529 public function get_random_photo()
2533 $result = $this->db->db_query("
2538 while($row = $this->db->db_fetch_object($result)) {
2539 array_push($all, $row['id']);
2542 return $all[array_rand($all)];
2544 } // get_random_photo()
2547 * validates provided date
2549 * this function validates if the provided date
2550 * contains a valid date and will return true
2552 * @param string $date_str
2555 public function isValidDate($date_str)
2557 $timestamp = strtotime($date_str);
2559 if(is_numeric($timestamp))
2567 * timestamp to string conversion
2568 * @param integer $timestamp
2571 private function ts2str($timestamp)
2573 return strftime("%Y-%m-%d", $timestamp);
2577 * extract tag-names from $_GET['tags']
2578 * @param string $tags_str
2581 private function extractTags($tags_str)
2583 $not_validated = split(',', $tags_str);
2584 $validated = array();
2586 foreach($not_validated as $tag) {
2587 if(is_numeric($tag))
2588 array_push($validated, $tag);
2596 * returns the full path to a thumbnail
2597 * @param integer $width
2598 * @param integer $photo
2601 public function get_thumb_path($width, $photo)
2603 $md5 = $this->getMD5($photo);
2604 $sub_path = substr($md5, 0, 2);
2605 return $this->cfg->thumb_path
2613 } // get_thumb_path()
2616 * returns server's virtual host name
2619 private function get_server_name()
2621 return $_SERVER['SERVER_NAME'];
2622 } // get_server_name()
2625 * returns type of webprotocol which is currently used
2628 private function get_web_protocol()
2630 if(!isset($_SERVER['HTTPS']))
2634 } // get_web_protocol()
2637 * return url to this phpfspot installation
2640 private function get_phpfspot_url()
2642 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2643 } // get_phpfspot_url()
2646 * returns the number of photos which are tagged with $tag_id
2647 * @param integer $tag_id
2650 public function get_num_photos($tag_id)
2652 if($result = $this->db->db_fetchSingleRow("
2653 SELECT count(*) as number
2656 tag_id LIKE '". $tag_id ."'")) {
2658 return $result['number'];
2664 } // get_num_photos()
2667 * check file exists and is readable
2669 * returns true, if everything is ok, otherwise false
2670 * if $silent is not set, this function will output and
2672 * @param string $file
2673 * @param boolean $silent
2676 private function check_readable($file, $silent = null)
2678 if(!file_exists($file)) {
2680 print "File \"". $file ."\" does not exist.\n";
2684 if(!is_readable($file)) {
2686 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2692 } // check_readable()
2695 * check if all needed indices are present
2697 * this function checks, if some needed indices are already
2698 * present, or if not, create them on the fly. they are
2699 * necessary to speed up some queries like that one look for
2700 * all tags, when show_tags is specified in the configuration.
2702 private function checkDbIndices()
2704 $result = $this->db->db_exec("
2705 CREATE INDEX IF NOT EXISTS
2712 } // checkDbIndices()
2715 * retrive F-Spot database version
2717 * this function will return the F-Spot database version number
2718 * It is stored within the sqlite3 database in the table meta
2719 * @return string|null
2721 public function getFspotDBVersion()
2723 if($result = $this->db->db_fetchSingleRow("
2724 SELECT data as version
2727 name LIKE 'F-Spot Database Version'
2729 return $result['version'];
2733 } // getFspotDBVersion()
2736 * parse the provided URI and will returned the requested chunk
2737 * @param string $uri
2738 * @param string $mode
2741 public function parse_uri($uri, $mode)
2743 if(($components = parse_url($uri)) !== false) {
2747 return basename($components['path']);
2750 return dirname($components['path']);
2753 return $components['path'];
2763 * validate config options
2765 * this function checks if all necessary configuration options are
2766 * specified and set.
2769 private function check_config_options()
2771 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
2772 $this->_error("Please set \$page_title in phpfspot_cfg");
2774 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
2775 $this->_error("Please set \$base_path in phpfspot_cfg");
2777 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
2778 $this->_error("Please set \$web_path in phpfspot_cfg");
2780 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
2781 $this->_error("Please set \$thumb_path in phpfspot_cfg");
2783 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
2784 $this->_error("Please set \$smarty_path in phpfspot_cfg");
2786 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
2787 $this->_error("Please set \$fspot_db in phpfspot_cfg");
2789 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
2790 $this->_error("Please set \$db_access in phpfspot_cfg");
2792 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
2793 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
2795 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
2796 $this->_error("Please set \$thumb_width in phpfspot_cfg");
2798 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
2799 $this->_error("Please set \$thumb_height in phpfspot_cfg");
2801 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
2802 $this->_error("Please set \$photo_width in phpfspot_cfg");
2804 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
2805 $this->_error("Please set \$mini_width in phpfspot_cfg");
2807 if(!isset($this->cfg->thumbs_per_page))
2808 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
2810 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
2811 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
2813 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
2814 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
2816 if(!isset($this->cfg->hide_tags))
2817 $this->_error("Please set \$hide_tags in phpfspot_cfg");
2819 if(!isset($this->cfg->theme_name))
2820 $this->_error("Please set \$theme_name in phpfspot_cfg");
2822 if(!isset($this->cfg->logging))
2823 $this->_error("Please set \$logging in phpfspot_cfg");
2825 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
2827 if(!isset($this->cfg->log_file))
2828 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
2830 if(!is_writeable($this->cfg->log_file))
2831 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
2835 /* check for pending slash on web_path */
2836 if(!preg_match("/\/$/", $this->cfg->web_path))
2837 $this->cfg->web_path.= "/";
2839 return $this->runtime_error;
2841 } // check_config_options()
2844 * cleanup phpfspot own database
2846 * When photos are getting delete from F-Spot, there will remain
2847 * remain some residues in phpfspot own database. This function
2848 * will try to wipe them out.
2850 public function cleanup_phpfspot_db()
2852 $to_delete = Array();
2854 $result = $this->cfg_db->db_query("
2857 ORDER BY img_idx ASC
2860 while($row = $this->cfg_db->db_fetch_object($result)) {
2861 if(!$this->db->db_fetchSingleRow("
2864 WHERE id='". $row['img_idx'] ."'")) {
2866 array_push($to_delete, $row['img_idx'], ',');
2870 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
2872 $this->cfg_db->db_exec("
2874 WHERE img_idx IN (". implode($to_delete) .")
2877 } // cleanup_phpfspot_db()
2880 * return first image of the page, the $current photo
2883 * this function is used to find out the first photo of the
2884 * current page, in which the $current photo lies. this is
2885 * used to display the correct photo, when calling showPhotoIndex()
2887 * @param integer $current
2888 * @param integer $max
2891 private function getCurrentPage($current, $max)
2893 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
2894 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
2895 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
2901 } // getCurrentPage()
2906 * this function tries to find out the correct mime-type
2907 * for the provided file.
2908 * @param string $file
2911 public function get_mime_info($file)
2913 $details = getimagesize($orig_image);
2915 /* if getimagesize() returns empty, try at least to find out the
2918 if(empty($details) && function_exists('mime_content_type')) {
2920 // mime_content_type is marked as deprecated in the documentation,
2921 // but is it really necessary to force users to install a PECL
2923 $details['mime'] = mime_content_type($file);
2926 return $details['mime'];
2928 } // get_mime_info()