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.5";
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 // has the user requested to hide this tag, and still someone,
771 // somehow tries to add it, don't allow this.
772 if(!isset($this->cfg->hide_tags) &&
773 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
776 if(!in_array($tag, $_SESSION['selected_tags']))
777 array_push($_SESSION['selected_tags'], $tag);
784 * remove tag to users session variable
786 * this function removes the specified tag from
787 * users current tag selection
791 public function delTag($tag)
793 if(isset($_SESSION['searchfor_tag']))
794 unset($_SESSION['searchfor_tag']);
796 if(isset($_SESSION['selected_tags'])) {
797 $key = array_search($tag, $_SESSION['selected_tags']);
798 unset($_SESSION['selected_tags'][$key]);
799 sort($_SESSION['selected_tags']);
807 * reset tag selection
809 * if there is any tag selection, it will be
812 public function resetTags()
814 if(isset($_SESSION['selected_tags']))
815 unset($_SESSION['selected_tags']);
820 * returns the value for the autocomplet tag-search
823 public function get_xml_tag_list()
825 if(!isset($_GET['search']) || !is_string($_GET['search']))
826 $_GET['search'] = '';
831 /* retrive tags from database */
834 $matched_tags = Array();
836 header("Content-Type: text/xml");
838 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
839 $string.= "<results>\n";
841 foreach($this->avail_tags as $tag)
843 if(!empty($_GET['search']) &&
844 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
845 count($matched_tags) < $length) {
847 $count = $this->get_num_photos($tag);
850 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
853 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
859 /* if we have collected enough items, break out */
860 if(count($matched_tags) >= $length)
864 $string.= "</results>\n";
868 } // get_xml_tag_list()
874 * if a specific photo was requested (external link)
875 * unset the session variable now
877 public function resetPhotoView()
879 if(isset($_SESSION['current_photo']))
880 unset($_SESSION['current_photo']);
882 } // resetPhotoView();
887 * if any tag search has taken place, reset it now
889 public function resetTagSearch()
891 if(isset($_SESSION['searchfor_tag']))
892 unset($_SESSION['searchfor_tag']);
894 } // resetTagSearch()
899 * if any name search has taken place, reset it now
901 public function resetNameSearch()
903 if(isset($_SESSION['searchfor_name']))
904 unset($_SESSION['searchfor_name']);
906 } // resetNameSearch()
911 * if any date search has taken place, reset
914 public function resetDateSearch()
916 if(isset($_SESSION['from_date']))
917 unset($_SESSION['from_date']);
918 if(isset($_SESSION['to_date']))
919 unset($_SESSION['to_date']);
921 } // resetDateSearch();
924 * return all photo according selection
926 * this function returns all photos based on
927 * the tag-selection, tag- or date-search.
928 * the tag-search also has to take care of AND
929 * and OR conjunctions
932 public function getPhotoSelection()
934 $matched_photos = Array();
935 $additional_where_cond = "";
937 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
938 $from_date = $_SESSION['from_date'];
939 $to_date = $_SESSION['to_date'];
940 $additional_where_cond.= "
941 p.time>='". $from_date ."'
943 p.time<='". $to_date ."'
947 if(isset($_SESSION['searchfor_name'])) {
948 if($this->dbver < 9) {
949 $additional_where_cond.= "
951 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
953 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
958 $additional_where_cond.= "
960 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
962 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
968 if(isset($_SESSION['sort_order'])) {
969 $order_str = $this->get_sort_order();
972 /* return a search result */
973 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
975 SELECT DISTINCT pt1.photo_id
977 INNER JOIN photo_tags pt2
978 ON pt1.photo_id=pt2.photo_id
985 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
987 if(isset($additional_where_cond) && !empty($additional_where_cond))
988 $query_str.= "AND ". $additional_where_cond ." ";
990 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
991 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
994 if(isset($order_str))
995 $query_str.= $order_str;
997 $result = $this->db->db_query($query_str);
998 while($row = $this->db->db_fetch_object($result)) {
999 array_push($matched_photos, $row['photo_id']);
1001 return $matched_photos;
1004 /* return according the selected tags */
1005 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1007 foreach($_SESSION['selected_tags'] as $tag)
1008 $selected.= $tag .",";
1009 $selected = substr($selected, 0, strlen($selected)-1);
1011 /* photo has to match at least on of the selected tags */
1012 if($_SESSION['tag_condition'] == 'or') {
1014 SELECT DISTINCT pt1.photo_id
1016 INNER JOIN photo_tags pt2
1017 ON pt1.photo_id=pt2.photo_id
1021 ON pt1.photo_id=p.id
1022 WHERE pt1.tag_id IN (". $selected .")
1024 if(isset($additional_where_cond) && !empty($additional_where_cond))
1025 $query_str.= "AND ". $additional_where_cond ." ";
1027 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1028 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1031 if(isset($order_str))
1032 $query_str.= $order_str;
1034 /* photo has to match all selected tags */
1035 elseif($_SESSION['tag_condition'] == 'and') {
1037 if(count($_SESSION['selected_tags']) >= 32) {
1038 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1039 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1043 /* Join together a table looking like
1045 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1047 so the query can quickly return all images matching the
1048 selected tags in an AND condition
1053 SELECT DISTINCT pt1.photo_id
1057 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1064 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1066 INNER JOIN photo_tags pt". ($i+2) ."
1067 ON pt1.photo_id=pt". ($i+2) .".photo_id
1072 ON pt1.photo_id=p.id
1074 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1075 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1077 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1080 if(isset($additional_where_cond) && !empty($additional_where_cond))
1081 $query_str.= "AND ". $additional_where_cond;
1083 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1084 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1087 if(isset($order_str))
1088 $query_str.= $order_str;
1092 $result = $this->db->db_query($query_str);
1093 while($row = $this->db->db_fetch_object($result)) {
1094 array_push($matched_photos, $row['photo_id']);
1096 return $matched_photos;
1099 /* return all available photos */
1101 SELECT DISTINCT p.id
1103 LEFT JOIN photo_tags pt
1109 if(isset($additional_where_cond) && !empty($additional_where_cond))
1110 $query_str.= "WHERE ". $additional_where_cond ." ";
1112 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1113 if(isset($additional_where_cond) && !empty($additional_where_cond))
1114 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1116 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1119 if(isset($order_str))
1120 $query_str.= $order_str;
1122 $result = $this->db->db_query($query_str);
1123 while($row = $this->db->db_fetch_object($result)) {
1124 array_push($matched_photos, $row['id']);
1126 return $matched_photos;
1128 } // getPhotoSelection()
1131 * control HTML ouput for photo index
1133 * this function provides all the necessary information
1134 * for the photo index template.
1136 public function showPhotoIndex()
1138 $photos = $this->getPhotoSelection();
1140 $count = count($photos);
1142 /* if all thumbnails should be shown on one page */
1143 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1147 /* thumbnails should be splitted up in several pages */
1148 elseif($this->cfg->thumbs_per_page > 0) {
1150 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1154 $begin_with = $_SESSION['begin_with'];
1157 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1161 $images[$thumbs] = Array();
1162 $img_height[$thumbs] = Array();
1163 $img_width[$thumbs] = Array();
1164 $img_id[$thumbs] = Array();
1165 $img_name[$thumbs] = Array();
1166 $img_fullname[$thumbs] = Array();
1167 $img_title = Array();
1169 for($i = $begin_with; $i < $end_with; $i++) {
1171 if(isset($photos[$i])) {
1173 $images[$thumbs] = $photos[$i];
1174 $img_id[$thumbs] = $i;
1175 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1176 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1177 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1179 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1181 if(file_exists($thumb_path)) {
1182 $info = getimagesize($thumb_path);
1183 $img_width[$thumbs] = $info[0];
1184 $img_height[$thumbs] = $info[1];
1190 // +1 for for smarty's selection iteration
1193 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1194 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1196 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1197 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1198 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1201 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1202 $this->tmpl->assign('tag_result', 1);
1205 /* do we have to display the page selector ? */
1206 if($this->cfg->thumbs_per_page != 0) {
1210 /* calculate the page switchers */
1211 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1212 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1214 if($begin_with != 0)
1215 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1216 if($end_with < $count)
1217 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1219 $photo_per_page = $this->cfg->thumbs_per_page;
1220 $last_page = ceil($count / $photo_per_page);
1222 /* get the current selected page */
1223 if($begin_with == 0) {
1227 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1234 for($i = 1; $i <= $last_page; $i++) {
1236 if($current_page == $i)
1237 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1238 elseif($current_page-1 == $i || $current_page+1 == $i)
1239 $style = "style=\"font-size: 105%;\"";
1240 elseif(($current_page-5 >= $i) && ($i != 1) ||
1241 ($current_page+5 <= $i) && ($i != $last_page))
1242 $style = "style=\"font-size: 75%;\"";
1246 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1249 $select.= ">". $i ."</a> ";
1251 // until 9 pages we show the selector from 1-9
1252 if($last_page <= 9) {
1253 $page_select.= $select;
1256 if($i == 1 /* first page */ ||
1257 $i == $last_page /* last page */ ||
1258 $i == $current_page /* current page */ ||
1259 $i == ceil($last_page * 0.25) /* first quater */ ||
1260 $i == ceil($last_page * 0.5) /* half */ ||
1261 $i == ceil($last_page * 0.75) /* third quater */ ||
1262 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1263 (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 */ ||
1264 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1265 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1267 $page_select.= $select;
1275 $page_select.= "......... ";
1280 /* only show the page selector if we have more then one page */
1282 $this->tmpl->assign('page_selector', $page_select);
1286 $current_tags = $this->getCurrentTags();
1287 $extern_link = "index.php?mode=showpi";
1288 $rss_link = "index.php?mode=rss";
1289 if($current_tags != "") {
1290 $extern_link.= "&tags=". $current_tags;
1291 $rss_link.= "&tags=". $current_tags;
1293 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1294 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1295 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1298 $export_link = "index.php?mode=export";
1299 $slideshow_link = "index.php?mode=slideshow";
1301 $this->tmpl->assign('extern_link', $extern_link);
1302 $this->tmpl->assign('slideshow_link', $slideshow_link);
1303 $this->tmpl->assign('export_link', $export_link);
1304 $this->tmpl->assign('rss_link', $rss_link);
1305 $this->tmpl->assign('count', $count);
1306 $this->tmpl->assign('width', $this->cfg->thumb_width);
1307 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1308 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1309 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1310 $this->tmpl->assign('images', $images);
1311 $this->tmpl->assign('img_width', $img_width);
1312 $this->tmpl->assign('img_height', $img_height);
1313 $this->tmpl->assign('img_id', $img_id);
1314 $this->tmpl->assign('img_name', $img_name);
1315 $this->tmpl->assign('img_fullname', $img_fullname);
1316 $this->tmpl->assign('img_title', $img_title);
1317 $this->tmpl->assign('thumbs', $thumbs);
1319 $this->tmpl->show("photo_index.tpl");
1321 /* if we are returning to photo index from an photo-view,
1322 scroll the window to the last shown photo-thumbnail.
1323 after this, unset the last_photo session variable.
1325 if(isset($_SESSION['last_photo'])) {
1326 print "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1327 unset($_SESSION['last_photo']);
1330 } // showPhotoIndex()
1333 * show credit template
1335 public function showCredits()
1337 $this->tmpl->assign('version', $this->cfg->version);
1338 $this->tmpl->assign('product', $this->cfg->product);
1339 $this->tmpl->assign('db_version', $this->dbver);
1340 $this->tmpl->show("credits.tpl");
1345 * create thumbnails for the requested width
1347 * this function creates image thumbnails of $orig_image
1348 * stored as $thumb_image. It will check if the image is
1349 * in a supported format, if necessary rotate the image
1350 * (based on EXIF orientation meta headers) and re-sizing.
1351 * @param string $orig_image
1352 * @param string $thumb_image
1353 * @param integer $width
1356 public function create_thumbnail($orig_image, $thumb_image, $width)
1358 if(!file_exists($orig_image)) {
1362 $mime = $this->get_mime_info($orig_image);
1364 /* check if original photo is a support image type */
1365 if(!$this->checkifImageSupported($mime))
1372 $meta = $this->get_meta_informations($orig_image);
1378 switch($meta['Orientation']) {
1379 case 1: /* top, left */
1380 /* nothing to do */ break;
1381 case 2: /* top, right */
1382 $rotate = 0; $flip_hori = true; break;
1383 case 3: /* bottom, left */
1384 $rotate = 180; break;
1385 case 4: /* bottom, right */
1386 $flip_vert = true; break;
1387 case 5: /* left side, top */
1388 $rotate = 90; $flip_vert = true; break;
1389 case 6: /* right side, top */
1390 $rotate = 90; break;
1391 case 7: /* left side, bottom */
1392 $rotate = 270; $flip_vert = true; break;
1393 case 8: /* right side, bottom */
1394 $rotate = 270; break;
1397 $src_img = @imagecreatefromjpeg($orig_image);
1403 $src_img = @imagecreatefrompng($orig_image);
1407 case 'image/x-portable-pixmap':
1409 $src_img = new Imagick($orig_image);
1410 $handler = "imagick";
1415 if(!isset($src_img) || empty($src_img)) {
1416 print "Can't load image from ". $orig_image ."\n";
1424 /* grabs the height and width */
1425 $cur_width = imagesx($src_img);
1426 $cur_height = imagesy($src_img);
1428 // If requested width is more then the actual image width,
1429 // do not generate a thumbnail, instead safe the original
1430 // as thumbnail but with lower quality. But if the image
1431 // is to heigh too, then we still have to resize it.
1432 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1433 $result = imagejpeg($src_img, $thumb_image, 75);
1434 imagedestroy($src_img);
1441 $cur_width = $src_img->getImageWidth();
1442 $cur_height = $src_img->getImageHeight();
1444 // If requested width is more then the actual image width,
1445 // do not generate a thumbnail, instead safe the original
1446 // as thumbnail but with lower quality. But if the image
1447 // is to heigh too, then we still have to resize it.
1448 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1449 $src_img->setCompressionQuality(75);
1450 $src_img->setImageFormat('jpeg');
1451 $src_img->writeImage($thumb_image);
1453 $src_img->destroy();
1460 // If the image will be rotate because EXIF orientation said so
1461 // 'virtually rotate' the image for further calculations
1462 if($rotate == 90 || $rotate == 270) {
1464 $cur_width = $cur_height;
1468 /* calculates aspect ratio */
1469 $aspect_ratio = $cur_height / $cur_width;
1472 if($aspect_ratio < 1) {
1474 $new_h = abs($new_w * $aspect_ratio);
1476 /* 'virtually' rotate the image and calculate it's ratio */
1477 $tmp_w = $cur_height;
1478 $tmp_h = $cur_width;
1479 /* now get the ratio from the 'rotated' image */
1480 $tmp_ratio = $tmp_h/$tmp_w;
1481 /* now calculate the new dimensions */
1483 $tmp_h = abs($tmp_w * $tmp_ratio);
1485 // now that we know, how high they photo should be, if it
1486 // gets rotated, use this high to scale the image
1488 $new_w = abs($new_h / $aspect_ratio);
1490 // If the image will be rotate because EXIF orientation said so
1491 // now 'virtually rotate' back the image for the image manipulation
1492 if($rotate == 90 || $rotate == 270) {
1503 /* creates new image of that size */
1504 $dst_img = imagecreatetruecolor($new_w, $new_h);
1506 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1508 /* copies resized portion of original image into new image */
1509 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1511 /* needs the image to be flipped horizontal? */
1513 $this->_debug("(FLIP)");
1514 $dst_img = $this->flipImage($dst_img, 'hori');
1516 /* needs the image to be flipped vertical? */
1518 $this->_debug("(FLIP)");
1519 $dst_img = $this->flipImage($dst_img, 'vert');
1523 $this->_debug("(ROTATE)");
1524 $dst_img = $this->rotateImage($dst_img, $rotate);
1527 /* write down new generated file */
1528 $result = imagejpeg($dst_img, $thumb_image, 75);
1530 /* free your mind */
1531 imagedestroy($dst_img);
1532 imagedestroy($src_img);
1534 if($result === false) {
1535 print "Can't write thumbnail ". $thumb_image ."\n";
1545 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1547 /* needs the image to be flipped horizontal? */
1549 $this->_debug("(FLIP)");
1550 $src_img->rotateImage(new ImagickPixel(), 90);
1551 $src_img->flipImage();
1552 $src_img->rotateImage(new ImagickPixel(), -90);
1554 /* needs the image to be flipped vertical? */
1556 $this->_debug("(FLIP)");
1557 $src_img->flipImage();
1561 $this->_debug("(ROTATE)");
1562 $src_img->rotateImage(new ImagickPixel(), $rotate);
1565 $src_img->setCompressionQuality(75);
1566 $src_img->setImageFormat('jpeg');
1568 if(!$src_img->writeImage($thumb_image)) {
1569 print "Can't write thumbnail ". $thumb_image ."\n";
1574 $src_img->destroy();
1581 } // create_thumbnail()
1584 * return all exif meta data from the file
1585 * @param string $file
1588 public function get_meta_informations($file)
1590 return exif_read_data($file);
1592 } // get_meta_informations()
1595 * create phpfspot own sqlite database
1597 * this function creates phpfspots own sqlite database
1598 * if it does not exist yet. this own is used to store
1599 * some necessary informations (md5 sum's, ...).
1601 public function check_config_table()
1603 // if the config table doesn't exist yet, create it
1604 if(!$this->cfg_db->db_check_table_exists("images")) {
1605 $this->cfg_db->db_exec("
1606 CREATE TABLE images (
1607 img_idx int primary key,
1613 } // check_config_table
1616 * Generates a thumbnail from photo idx
1618 * This function will generate JPEG thumbnails from provided F-Spot photo
1621 * 1. Check if all thumbnail generations (width) are already in place and
1623 * 2. Check if the md5sum of the original file has changed
1624 * 3. Generate the thumbnails if needed
1625 * @param integer $idx
1626 * @param integer $force
1627 * @param boolean $overwrite
1629 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1633 $resolutions = Array(
1634 $this->cfg->thumb_width,
1635 $this->cfg->photo_width,
1636 $this->cfg->mini_width,
1639 /* get details from F-Spot's database */
1640 $details = $this->get_photo_details($idx);
1642 /* calculate file MD5 sum */
1643 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1645 if(!file_exists($full_path)) {
1646 $this->_error("File ". $full_path ." does not exist\n");
1650 if(!is_readable($full_path)) {
1651 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1655 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1657 /* If Nikon NEF format, we need to treat it another way */
1658 if(isset($this->cfg->dcraw_bin) &&
1659 file_exists($this->cfg->dcraw_bin) &&
1660 is_executable($this->cfg->dcraw_bin) &&
1661 preg_match('/\.nef$/i', $details['uri'])) {
1663 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1665 /* if PPM file does not exist, let dcraw convert it from NEF */
1666 if(!file_exists($ppm_path)) {
1667 system($this->cfg->dcraw_bin ." -a ". $full_path);
1670 /* for now we handle the PPM instead of the NEF */
1671 $full_path = $ppm_path;
1675 $file_md5 = md5_file($full_path);
1678 foreach($resolutions as $resolution) {
1680 $generate_it = false;
1682 $thumb_sub_path = substr($file_md5, 0, 2);
1683 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1685 /* if thumbnail-subdirectory does not exist yet, create it */
1686 if(!file_exists(dirname($thumb_path))) {
1687 mkdir(dirname($thumb_path), 0755);
1690 /* if the thumbnail file doesn't exist, create it */
1691 if(!file_exists($thumb_path)) {
1692 $generate_it = true;
1694 /* if the file hasn't changed there is no need to regen the thumb */
1695 elseif($file_md5 != $this->getMD5($idx) || $force) {
1696 $generate_it = true;
1699 if($generate_it || $overwrite) {
1701 $this->_debug(" ". $resolution ."px");
1702 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1710 $this->_debug(" already exist");
1713 /* set the new/changed MD5 sum for the current photo */
1715 $this->setMD5($idx, $file_md5);
1718 $this->_debug("\n");
1723 * returns stored md5 sum for a specific photo
1725 * this function queries the phpfspot database for a
1726 * stored MD5 checksum of the specified photo
1727 * @param integer $idx
1728 * @return string|null
1730 public function getMD5($idx)
1732 $result = $this->cfg_db->db_query("
1735 WHERE img_idx='". $idx ."'
1741 $img = $this->cfg_db->db_fetch_object($result);
1742 return $img['img_md5'];
1747 * set MD5 sum for the specific photo
1748 * @param integer $idx
1749 * @param string $md5
1751 private function setMD5($idx, $md5)
1753 $result = $this->cfg_db->db_exec("
1754 REPLACE INTO images (img_idx, img_md5)
1755 VALUES ('". $idx ."', '". $md5 ."')
1761 * store current tag condition
1763 * this function stores the current tag condition
1764 * (AND or OR) in the users session variables
1765 * @param string $mode
1768 public function setTagCondition($mode)
1770 $_SESSION['tag_condition'] = $mode;
1774 } // setTagCondition()
1777 * invoke tag & date search
1779 * this function will return all matching tags and store
1780 * them in the session variable selected_tags. furthermore
1781 * it also handles the date search.
1782 * getPhotoSelection() will then only return the matching
1786 public function startSearch()
1788 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1789 $from = $_POST['from'];
1791 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
1795 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
1796 $searchfor_tag = $_POST['for_tag'];
1797 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
1800 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
1801 $searchfor_name = $_POST['for_name'];
1802 $_SESSION['searchfor_name'] = $_POST['for_name'];
1807 if(isset($from) && !empty($from))
1808 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1810 unset($_SESSION['from_date']);
1812 if(isset($to) && !empty($to))
1813 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1815 unset($_SESSION['to_date']);
1817 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
1818 /* new search, reset the current selected tags */
1819 $_SESSION['selected_tags'] = Array();
1820 foreach($this->avail_tags as $tag) {
1821 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
1822 array_push($_SESSION['selected_tags'], $tag);
1831 * updates sort order in session variable
1833 * this function is invoked by RPC and will sort the requested
1834 * sort order in the session variable.
1835 * @param string $sort_order
1838 public function updateSortOrder($order)
1840 if(isset($this->sort_orders[$order])) {
1841 $_SESSION['sort_order'] = $order;
1845 return "unkown error";
1847 } // updateSortOrder()
1852 * this function rotates the image according the
1854 * @param string $img
1855 * @param integer $degress
1858 private function rotateImage($img, $degrees)
1860 if(function_exists("imagerotate")) {
1861 $img = imagerotate($img, $degrees, 0);
1863 function imagerotate($src_img, $angle)
1865 $src_x = imagesx($src_img);
1866 $src_y = imagesy($src_img);
1867 if ($angle == 180) {
1871 elseif ($src_x <= $src_y) {
1875 elseif ($src_x >= $src_y) {
1880 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1881 imagealphablending($rotate, false);
1886 for ($y = 0; $y < ($src_y); $y++) {
1887 for ($x = 0; $x < ($src_x); $x++) {
1888 $color = imagecolorat($src_img, $x, $y);
1889 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1895 for ($y = 0; $y < ($src_y); $y++) {
1896 for ($x = 0; $x < ($src_x); $x++) {
1897 $color = imagecolorat($src_img, $x, $y);
1898 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1904 for ($y = 0; $y < ($src_y); $y++) {
1905 for ($x = 0; $x < ($src_x); $x++) {
1906 $color = imagecolorat($src_img, $x, $y);
1907 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1921 $img = imagerotate($img, $degrees);
1930 * returns flipped image
1932 * this function will return an either horizontal or
1933 * vertical flipped truecolor image.
1934 * @param string $image
1935 * @param string $mode
1938 private function flipImage($image, $mode)
1940 $w = imagesx($image);
1941 $h = imagesy($image);
1942 $flipped = imagecreatetruecolor($w, $h);
1946 for ($y = 0; $y < $h; $y++) {
1947 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1951 for ($x = 0; $x < $w; $x++) {
1952 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1962 * return all assigned tags for the specified photo
1963 * @param integer $idx
1966 private function get_photo_tags($idx)
1968 $result = $this->db->db_query("
1971 INNER JOIN photo_tags pt
1973 WHERE pt.photo_id='". $idx ."'
1978 while($row = $this->db->db_fetch_object($result)) {
1979 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
1981 $tags[$row['id']] = $row['name'];
1986 } // get_photo_tags()
1989 * create on-the-fly images with text within
1990 * @param string $txt
1991 * @param string $color
1992 * @param integer $space
1993 * @param integer $font
1996 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1998 if (strlen($color) != 6)
2001 $int = hexdec($color);
2002 $h = imagefontheight($font);
2003 $fw = imagefontwidth($font);
2004 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2005 $lines = count($txt);
2006 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2007 $bg = imagecolorallocate($im, 255, 255, 255);
2008 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2011 foreach ($txt as $text) {
2012 $x = (($w - ($fw * strlen($text))) / 2);
2013 imagestring($im, $font, $x, $y, $text, $color);
2014 $y += ($h + $space);
2017 Header("Content-type: image/png");
2020 } // showTextImage()
2023 * check if all requirements are met
2026 private function check_requirements()
2028 if(!function_exists("imagecreatefromjpeg")) {
2029 print "PHP GD library extension is missing<br />\n";
2033 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2034 print "PHP SQLite3 library extension is missing<br />\n";
2038 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2039 ini_set('track_errors', 1);
2040 @include_once 'HTML/AJAX/Server.php';
2041 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2042 print "PEAR HTML_AJAX package is missing<br />\n";
2045 @include_once 'Calendar/Calendar.php';
2046 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2047 print "PEAR Calendar package is missing<br />\n";
2050 @include_once 'Console/Getopt.php';
2051 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2052 print "PEAR Console_Getopt package is missing<br />\n";
2055 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2056 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2057 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2060 ini_restore('track_errors');
2067 } // check_requirements()
2069 private function _debug($text)
2071 if($this->fromcmd) {
2078 * check if specified MIME type is supported
2079 * @param string $mime
2082 public function checkifImageSupported($mime)
2084 $supported_types = Array(
2087 "image/x-portable-pixmap",
2091 if(in_array($mime, $supported_types))
2096 } // checkifImageSupported()
2100 * @param string $text
2102 public function _error($text)
2104 switch($this->cfg->logging) {
2107 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2108 print $text ."<br />\n";
2114 error_log($text, 3, $his->cfg->log_file);
2118 $this->runtime_error = true;
2123 * output calendard input fields
2124 * @param string $mode
2127 private function get_calendar($mode)
2129 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2130 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2131 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2133 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2134 if(!isset($_SESSION[$mode .'_date']))
2135 $output.= " disabled=\"disabled\"";
2137 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2138 if(!isset($_SESSION[$mode .'_date']))
2139 $output.= " disabled=\"disabled\"";
2141 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2142 if(!isset($_SESSION[$mode .'_date']))
2143 $output.= " disabled=\"disabled\"";
2151 * output calendar matrix
2152 * @param integer $year
2153 * @param integer $month
2154 * @param integer $day
2156 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2158 if (!isset($year)) $year = date('Y');
2159 if (!isset($month)) $month = date('m');
2160 if (!isset($day)) $day = date('d');
2165 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2166 require_once CALENDAR_ROOT.'Day.php';
2169 $month = new Calendar_Month_Weekdays($year,$month);
2172 $prevStamp = $month->prevMonth(true);
2173 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2174 $nextStamp = $month->nextMonth(true);
2175 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2177 $selectedDays = array (
2178 new Calendar_Day($year,$month,$day),
2179 new Calendar_Day($year,12,25),
2182 // Build the days in the month
2183 $month->build($selectedDays);
2185 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2186 $this->tmpl->assign('prev_month', $prev);
2187 $this->tmpl->assign('next_month', $next);
2189 while ( $day = $month->fetch() ) {
2191 if(!isset($matrix[$rows]))
2192 $matrix[$rows] = Array();
2196 $dayStamp = $day->thisDay(true);
2197 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2199 // isFirst() to find start of week
2200 if ( $day->isFirst() )
2203 if ( $day->isSelected() ) {
2204 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2205 } else if ( $day->isEmpty() ) {
2206 $string.= "<td> </td>\n";
2208 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2211 // isLast() to find end of week
2212 if ( $day->isLast() )
2213 $string.= "</tr>\n";
2215 $matrix[$rows][$cols] = $string;
2225 $this->tmpl->assign('matrix', $matrix);
2226 $this->tmpl->assign('rows', $rows);
2227 $this->tmpl->show("calendar.tpl");
2229 } // get_calendar_matrix()
2232 * output export page
2233 * @param string $mode
2235 public function getExport($mode)
2237 $pictures = $this->getPhotoSelection();
2238 $current_tags = $this->getCurrentTags();
2240 foreach($pictures as $picture) {
2242 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2243 if($current_tags != "") {
2244 $orig_url.= "&tags=". $current_tags;
2246 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2247 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2250 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2255 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2256 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2260 // "[%pictureurl% %thumbnailurl%]"
2261 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2264 case 'MoinMoinList':
2265 // " * [%pictureurl% %thumbnailurl%]"
2266 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2277 public function getRSSFeed()
2279 Header("Content-type: text/xml; charset=utf-8");
2280 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2283 xmlns:media="http://search.yahoo.com/mrss/"
2284 xmlns:dc="http://purl.org/dc/elements/1.1/"
2287 <title>phpfspot</title>
2288 <description>phpfspot RSS feed</description>
2289 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2290 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2291 <generator>phpfspot</generator>
2294 $pictures = $this->getPhotoSelection();
2295 $current_tags = $this->getCurrentTags();
2297 foreach($pictures as $picture) {
2299 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2300 if($current_tags != "") {
2301 $orig_url.= "&tags=". $current_tags;
2303 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2304 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2307 $details = $this->get_photo_details($picture);
2309 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2310 $thumb_html = htmlspecialchars("
2311 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2313 ". $details['description']);
2315 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2317 /* get EXIF information if JPEG */
2318 if($details['mime'] == "image/jpeg") {
2319 $meta = $this->get_meta_informations($orig_path);
2322 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2326 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2327 <link><?php print htmlspecialchars($orig_url); ?></link>
2328 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2329 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2331 <?php print $thumb_html; ?>
2333 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2348 * return all selected tags as one string
2351 private function getCurrentTags()
2354 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2355 foreach($_SESSION['selected_tags'] as $tag)
2356 $current_tags.= $tag .",";
2357 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2359 return $current_tags;
2361 } // getCurrentTags()
2364 * return the current photo
2366 public function getCurrentPhoto()
2368 if(isset($_SESSION['current_photo'])) {
2369 print $_SESSION['current_photo'];
2371 } // getCurrentPhoto()
2374 * tells the client browser what to do
2376 * this function is getting called via AJAX by the
2377 * client browsers. it will tell them what they have
2378 * to do next. This is necessary for directly jumping
2379 * into photo index or single photo view when the are
2380 * requested with specific URLs
2383 public function whatToDo()
2385 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2386 return "show_photo";
2388 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2389 return "showpi_tags";
2391 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2395 return "nothing special";
2400 * return the current process-user
2403 private function getuid()
2405 if($uid = posix_getuid()) {
2406 if($user = posix_getpwuid($uid)) {
2407 return $user['name'];
2416 * returns a select-dropdown box to select photo index sort parameters
2417 * @param array $params
2418 * @param smarty $smarty
2421 public function smarty_sort_select_list($params, &$smarty)
2425 foreach($this->sort_orders as $key => $value) {
2426 $output.= "<option value=\"". $key ."\"";
2427 if($key == $_SESSION['sort_order']) {
2428 $output.= " selected=\"selected\"";
2430 $output.= ">". $value ."</option>";
2435 } // smarty_sort_select_list()
2438 * returns the currently selected sort order
2441 private function get_sort_order()
2443 switch($_SESSION['sort_order']) {
2445 return " ORDER BY p.time ASC";
2448 return " ORDER BY p.time DESC";
2451 if($this->dbver < 9) {
2452 return " ORDER BY p.name ASC";
2455 return " ORDER BY basename(p.uri) ASC";
2459 if($this->dbver < 9) {
2460 return " ORDER BY p.name DESC";
2463 return " ORDER BY basename(p.uri) DESC";
2467 return " ORDER BY t.name ASC ,p.time ASC";
2470 return " ORDER BY t.name DESC ,p.time ASC";
2474 } // get_sort_order()
2477 * return the next to be shown slide show image
2479 * this function returns the URL of the next image
2480 * in the slideshow sequence.
2483 public function getNextSlideShowImage()
2485 $all_photos = $this->getPhotoSelection();
2487 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2488 $_SESSION['slideshow_img'] = 0;
2490 $_SESSION['slideshow_img']++;
2492 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2494 } // getNextSlideShowImage()
2497 * return the previous to be shown slide show image
2499 * this function returns the URL of the previous image
2500 * in the slideshow sequence.
2503 public function getPrevSlideShowImage()
2505 $all_photos = $this->getPhotoSelection();
2507 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2508 $_SESSION['slideshow_img'] = 0;
2510 $_SESSION['slideshow_img']--;
2512 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2514 } // getPrevSlideShowImage()
2516 public function resetSlideShow()
2518 if(isset($_SESSION['slideshow_img']))
2519 unset($_SESSION['slideshow_img']);
2521 } // resetSlideShow()
2526 * this function will get all photos from the fspot
2527 * database and randomly return ONE entry
2529 * saddly there is yet no sqlite3 function which returns
2530 * the bulk result in array, so we have to fill up our
2534 public function get_random_photo()
2543 /* if show_tags is set, only return details for photos which
2544 are specified to be shown
2546 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2548 INNER JOIN photo_tags pt
2553 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2556 $result = $this->db->db_query($query_str);
2558 while($row = $this->db->db_fetch_object($result)) {
2559 array_push($all, $row['id']);
2562 return $all[array_rand($all)];
2564 } // get_random_photo()
2567 * validates provided date
2569 * this function validates if the provided date
2570 * contains a valid date and will return true
2572 * @param string $date_str
2575 public function isValidDate($date_str)
2577 $timestamp = strtotime($date_str);
2579 if(is_numeric($timestamp))
2587 * timestamp to string conversion
2588 * @param integer $timestamp
2591 private function ts2str($timestamp)
2593 return strftime("%Y-%m-%d", $timestamp);
2597 * extract tag-names from $_GET['tags']
2598 * @param string $tags_str
2601 private function extractTags($tags_str)
2603 $not_validated = split(',', $tags_str);
2604 $validated = array();
2606 foreach($not_validated as $tag) {
2607 if(is_numeric($tag))
2608 array_push($validated, $tag);
2616 * returns the full path to a thumbnail
2617 * @param integer $width
2618 * @param integer $photo
2621 public function get_thumb_path($width, $photo)
2623 $md5 = $this->getMD5($photo);
2624 $sub_path = substr($md5, 0, 2);
2625 return $this->cfg->thumb_path
2633 } // get_thumb_path()
2636 * returns server's virtual host name
2639 private function get_server_name()
2641 return $_SERVER['SERVER_NAME'];
2642 } // get_server_name()
2645 * returns type of webprotocol which is currently used
2648 private function get_web_protocol()
2650 if(!isset($_SERVER['HTTPS']))
2654 } // get_web_protocol()
2657 * return url to this phpfspot installation
2660 private function get_phpfspot_url()
2662 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2663 } // get_phpfspot_url()
2666 * returns the number of photos which are tagged with $tag_id
2667 * @param integer $tag_id
2670 public function get_num_photos($tag_id)
2672 if($result = $this->db->db_fetchSingleRow("
2673 SELECT count(*) as number
2676 tag_id LIKE '". $tag_id ."'")) {
2678 return $result['number'];
2684 } // get_num_photos()
2687 * check file exists and is readable
2689 * returns true, if everything is ok, otherwise false
2690 * if $silent is not set, this function will output and
2692 * @param string $file
2693 * @param boolean $silent
2696 private function check_readable($file, $silent = null)
2698 if(!file_exists($file)) {
2700 print "File \"". $file ."\" does not exist.\n";
2704 if(!is_readable($file)) {
2706 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2712 } // check_readable()
2715 * check if all needed indices are present
2717 * this function checks, if some needed indices are already
2718 * present, or if not, create them on the fly. they are
2719 * necessary to speed up some queries like that one look for
2720 * all tags, when show_tags is specified in the configuration.
2722 private function checkDbIndices()
2724 $result = $this->db->db_exec("
2725 CREATE INDEX IF NOT EXISTS
2732 } // checkDbIndices()
2735 * retrive F-Spot database version
2737 * this function will return the F-Spot database version number
2738 * It is stored within the sqlite3 database in the table meta
2739 * @return string|null
2741 public function getFspotDBVersion()
2743 if($result = $this->db->db_fetchSingleRow("
2744 SELECT data as version
2747 name LIKE 'F-Spot Database Version'
2749 return $result['version'];
2753 } // getFspotDBVersion()
2756 * parse the provided URI and will returned the requested chunk
2757 * @param string $uri
2758 * @param string $mode
2761 public function parse_uri($uri, $mode)
2763 if(($components = parse_url($uri)) !== false) {
2767 return basename($components['path']);
2770 return dirname($components['path']);
2773 return $components['path'];
2783 * validate config options
2785 * this function checks if all necessary configuration options are
2786 * specified and set.
2789 private function check_config_options()
2791 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
2792 $this->_error("Please set \$page_title in phpfspot_cfg");
2794 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
2795 $this->_error("Please set \$base_path in phpfspot_cfg");
2797 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
2798 $this->_error("Please set \$web_path in phpfspot_cfg");
2800 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
2801 $this->_error("Please set \$thumb_path in phpfspot_cfg");
2803 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
2804 $this->_error("Please set \$smarty_path in phpfspot_cfg");
2806 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
2807 $this->_error("Please set \$fspot_db in phpfspot_cfg");
2809 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
2810 $this->_error("Please set \$db_access in phpfspot_cfg");
2812 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
2813 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
2815 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
2816 $this->_error("Please set \$thumb_width in phpfspot_cfg");
2818 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
2819 $this->_error("Please set \$thumb_height in phpfspot_cfg");
2821 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
2822 $this->_error("Please set \$photo_width in phpfspot_cfg");
2824 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
2825 $this->_error("Please set \$mini_width in phpfspot_cfg");
2827 if(!isset($this->cfg->thumbs_per_page))
2828 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
2830 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
2831 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
2833 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
2834 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
2836 if(!isset($this->cfg->hide_tags))
2837 $this->_error("Please set \$hide_tags in phpfspot_cfg");
2839 if(!isset($this->cfg->theme_name))
2840 $this->_error("Please set \$theme_name in phpfspot_cfg");
2842 if(!isset($this->cfg->logging))
2843 $this->_error("Please set \$logging in phpfspot_cfg");
2845 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
2847 if(!isset($this->cfg->log_file))
2848 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
2850 if(!is_writeable($this->cfg->log_file))
2851 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
2855 /* check for pending slash on web_path */
2856 if(!preg_match("/\/$/", $this->cfg->web_path))
2857 $this->cfg->web_path.= "/";
2859 return $this->runtime_error;
2861 } // check_config_options()
2864 * cleanup phpfspot own database
2866 * When photos are getting delete from F-Spot, there will remain
2867 * remain some residues in phpfspot own database. This function
2868 * will try to wipe them out.
2870 public function cleanup_phpfspot_db()
2872 $to_delete = Array();
2874 $result = $this->cfg_db->db_query("
2877 ORDER BY img_idx ASC
2880 while($row = $this->cfg_db->db_fetch_object($result)) {
2881 if(!$this->db->db_fetchSingleRow("
2884 WHERE id='". $row['img_idx'] ."'")) {
2886 array_push($to_delete, $row['img_idx'], ',');
2890 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
2892 $this->cfg_db->db_exec("
2894 WHERE img_idx IN (". implode($to_delete) .")
2897 } // cleanup_phpfspot_db()
2900 * return first image of the page, the $current photo
2903 * this function is used to find out the first photo of the
2904 * current page, in which the $current photo lies. this is
2905 * used to display the correct photo, when calling showPhotoIndex()
2907 * @param integer $current
2908 * @param integer $max
2911 private function getCurrentPage($current, $max)
2913 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
2914 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
2915 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
2921 } // getCurrentPage()
2926 * this function tries to find out the correct mime-type
2927 * for the provided file.
2928 * @param string $file
2931 public function get_mime_info($file)
2933 $details = getimagesize($orig_image);
2935 /* if getimagesize() returns empty, try at least to find out the
2938 if(empty($details) && function_exists('mime_content_type')) {
2940 // mime_content_type is marked as deprecated in the documentation,
2941 // but is it really necessary to force users to install a PECL
2943 $details['mime'] = mime_content_type($file);
2946 return $details['mime'];
2948 } // get_mime_info()
2951 * return tag-name by tag-idx
2953 * this function returns the tag-name for the requested
2954 * tag specified by tag-idx.
2955 * @param integer $idx
2958 public function get_tag_name($idx)
2960 if($result = $this->db->db_fetchSingleRow("
2964 id LIKE '". $idx ."'")) {
2966 return $result['name'];