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);
248 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url) {
249 $content = $this->parse_user_friendly_url($_SERVER['REQUEST_URI']);
252 if(isset($_GET['mode'])) {
254 $_SESSION['start_action'] = $_GET['mode'];
256 switch($_GET['mode']) {
258 if(isset($_GET['tags'])) {
259 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
261 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
262 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
264 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
265 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
269 if(isset($_GET['tags'])) {
270 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
271 $_SESSION['start_action'] = 'showp';
273 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
274 $_SESSION['current_photo'] = $_GET['id'];
275 $_SESSION['start_action'] = 'showp';
277 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
278 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
280 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
281 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
285 $this->tmpl->show("export.tpl");
289 $this->tmpl->show("slideshow.tpl");
293 if(isset($_GET['tags'])) {
294 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
296 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
297 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
299 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
300 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
308 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
309 $this->tmpl->assign('date_search_enabled', true);
311 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
312 $this->tmpl->assign('from_date', $this->get_calendar('from'));
313 $this->tmpl->assign('to_date', $this->get_calendar('to'));
316 $this->tmpl->assign('content_page', $this->tmpl->fetch('welcome.tpl'));
318 $this->tmpl->assign('content_page', $content);
320 $this->tmpl->show("index.tpl");
325 * get_tags - grab all tags of f-spot's database
327 * this function will get all available tags from
328 * the f-spot database and store them within two
329 * arrays within this class for later usage. in
330 * fact, if the user requests (hide_tags) it will
331 * opt-out some of them.
333 * this function is getting called once by show()
335 private function get_tags()
337 $this->avail_tags = Array();
340 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
343 DISTINCT t1.id as id, t1.name as name
346 INNER JOIN photo_tags
347 pt2 ON pt1.photo_id=pt2.photo_id
353 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
355 t1.sort_priority ASC";
357 $result = $this->db->db_query($query_str);
361 $result = $this->db->db_query("
364 ORDER BY sort_priority ASC
368 while($row = $this->db->db_fetch_object($result)) {
370 $tag_id = $row['id'];
371 $tag_name = $row['name'];
373 /* if the user has specified to ignore this tag in phpfspot's
374 configuration, ignore it here so it does not get added to
377 if(in_array($row['name'], $this->cfg->hide_tags))
380 /* if you include the following if-clause and the user has specified
381 to only show certain tags which are specified in phpfspot's
382 configuration, ignore all others so they will not be added to the
384 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
385 !in_array($row['name'], $this->cfg->show_tags))
389 $this->tags[$tag_id] = $tag_name;
390 $this->avail_tags[$count] = $tag_id;
398 * extract all photo details
400 * retrieve all available details from f-spot's
401 * database and return them as object
402 * @param integer $idx
403 * @return object|null
405 public function get_photo_details($idx)
407 if($this->dbver < 9) {
409 SELECT p.id, p.name, p.time, p.directory_path, p.description
415 SELECT p.id, p.uri, p.time, p.description
420 /* if show_tags is set, only return details for photos which
421 are specified to be shown
423 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
425 INNER JOIN photo_tags pt
429 WHERE p.id='". $idx ."'
430 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
434 WHERE p.id='". $idx ."'
438 if($result = $this->db->db_query($query_str)) {
440 $row = $this->db->db_fetch_object($result);
442 if($this->dbver < 9) {
443 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
452 } // get_photo_details
455 * returns aligned photo names
457 * this function returns aligned (length) names for
458 * an specific photo. If the length of the name exceeds
459 * $limit the name will be shrinked (...)
460 * @param integer $idx
461 * @param integer $limit
462 * @return string|null
464 public function getPhotoName($idx, $limit = 0)
466 if($details = $this->get_photo_details($idx)) {
467 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
468 $name = $this->shrink_text($long_name, $limit);
478 * shrink text according provided limit
480 * If the length of the name exceeds $limit the
481 * text will be shortend and some content in between
482 * will be replaced with "..."
484 * @param integer $limit
487 private function shrink_text($text, $limit)
489 if($limit != 0 && strlen($text) > $limit) {
490 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
498 * translate f-spoth photo path
500 * as the full-qualified path recorded in the f-spot database
501 * is usally not the same as on the webserver, this function
502 * will replace the path with that one specified in the cfg
503 * @param string $path
506 public function translate_path($path)
508 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
513 * control HTML ouput for a single photo
515 * this function provides all the necessary information
516 * for the single photo template.
517 * @param integer photo
519 public function showPhoto($photo)
521 /* get all photos from the current photo selection */
522 $all_photos = $this->getPhotoSelection();
523 $count = count($all_photos);
525 for($i = 0; $i < $count; $i++) {
527 // $get_next will be set, when the photo which has to
528 // be displayed has been found - this means that the
529 // next available is in fact the NEXT image (for the
531 if(isset($get_next)) {
532 $next_img = $all_photos[$i];
536 /* the next photo is our NEXT photo */
537 if($all_photos[$i] == $photo) {
541 $previous_img = $all_photos[$i];
544 if($photo == $all_photos[$i]) {
549 $details = $this->get_photo_details($photo);
556 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
557 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
559 if(!file_exists($orig_path)) {
560 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
564 if(!is_readable($orig_path)) {
565 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
569 /* If the thumbnail doesn't exist yet, try to create it */
570 if(!file_exists($thumb_path)) {
571 $this->gen_thumb($photo, true);
572 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
575 /* get mime-type, height and width from the original photo */
576 $info = getimagesize($orig_path);
578 /* get EXIF information if JPEG */
579 if($info['mime'] == "image/jpeg") {
580 $meta = $this->get_meta_informations($orig_path);
583 /* If EXIF data are available, use them */
584 if(isset($meta['ExifImageWidth'])) {
585 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
587 $meta_res = $info[0] ."x". $info[1];
590 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
591 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
592 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
594 $extern_link = "index.php?mode=showp&id=". $photo;
595 $current_tags = $this->getCurrentTags();
596 if($current_tags != "") {
597 $extern_link.= "&tags=". $current_tags;
599 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
600 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
603 $this->tmpl->assign('extern_link', $extern_link);
605 if(!file_exists($thumb_path)) {
606 $this->_error("Can't open file ". $thumb_path ."\n");
610 $info_thumb = getimagesize($thumb_path);
612 $this->tmpl->assign('description', $details['description']);
613 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
615 $this->tmpl->assign('width', $info_thumb[0]);
616 $this->tmpl->assign('height', $info_thumb[1]);
617 $this->tmpl->assign('ExifMadeOn', $meta_date);
618 $this->tmpl->assign('ExifMadeWith', $meta_make);
619 $this->tmpl->assign('ExifOrigResolution', $meta_res);
620 $this->tmpl->assign('ExifFileSize', $meta_size);
622 if(!isset($this->cfg->user_friendly_url) || !$this->cfg->user_friendly_url)
623 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
625 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
627 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
628 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
630 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
631 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
632 $this->tmpl->assign('current_img', $photo);
635 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
636 $this->tmpl->assign('prev_img', $previous_img);
640 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
641 $this->tmpl->assign('next_img', $next_img);
643 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
644 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
645 $this->tmpl->assign('photo_number', $i);
646 $this->tmpl->assign('photo_count', count($all_photos));
648 return $this->tmpl->fetch("single_photo.tpl");
653 * all available tags and tag cloud
655 * this function outputs all available tags (time ordered)
656 * and in addition output them as tag cloud (tags which have
657 * many photos will appears more then others)
659 public function getAvailableTags()
661 /* retrive tags from database */
666 $result = $this->db->db_query("
667 SELECT tag_id as id, count(tag_id) as quantity
677 while($row = $this->db->db_fetch_object($result)) {
678 $tags[$row['id']] = $row['quantity'];
681 // change these font sizes if you will
682 $max_size = 125; // max font size in %
683 $min_size = 75; // min font size in %
686 $max_sat = hexdec('cc');
687 $min_sat = hexdec('44');
689 // get the largest and smallest array values
690 $max_qty = max(array_values($tags));
691 $min_qty = min(array_values($tags));
693 // find the range of values
694 $spread = $max_qty - $min_qty;
695 if (0 == $spread) { // we don't want to divide by zero
699 // determine the font-size increment
700 // this is the increase per tag quantity (times used)
701 $step = ($max_size - $min_size)/($spread);
702 $step_sat = ($max_sat - $min_sat)/($spread);
704 // loop through our tag array
705 foreach ($tags as $key => $value) {
707 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
710 // calculate CSS font-size
711 // find the $value in excess of $min_qty
712 // multiply by the font-size increment ($size)
713 // and add the $min_size set above
714 $size = $min_size + (($value - $min_qty) * $step);
715 // uncomment if you want sizes in whole %:
718 $color = $min_sat + ($value - $min_qty) * $step_sat;
724 if(isset($this->tags[$key])) {
725 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
730 $output = substr($output, 0, strlen($output)-2);
733 } // getAvailableTags()
736 * output all selected tags
738 * this function output all tags which have been selected
739 * by the user. the selected tags are stored in the
740 * session-variable $_SESSION['selected_tags']
743 public function getSelectedTags($type = 'link')
745 /* retrive tags from database */
750 foreach($this->avail_tags as $tag)
752 // return all selected tags
753 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
758 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
762 <div style=\"display: table-cell;\">
763 <div style=\"display: table-row; text-align: center;\">
764 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
765 <img src=\"phpfspot_img.php?tagidx=". $tag ."\" />
768 <div style=\"display: table-row; text-align: center;\">
769 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
770 <img src=\"resources/underbar.png\" />
781 $output = substr($output, 0, strlen($output)-2);
785 return "no tags selected";
788 } // getSelectedTags()
791 * add tag to users session variable
793 * this function will add the specified to users current
794 * tag selection. if a date search has been made before
795 * it will be now cleared
798 public function addTag($tag)
800 if(!isset($_SESSION['selected_tags']))
801 $_SESSION['selected_tags'] = Array();
803 if(isset($_SESSION['searchfor_tag']))
804 unset($_SESSION['searchfor_tag']);
806 // has the user requested to hide this tag, and still someone,
807 // somehow tries to add it, don't allow this.
808 if(!isset($this->cfg->hide_tags) &&
809 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
812 if(!in_array($tag, $_SESSION['selected_tags']))
813 array_push($_SESSION['selected_tags'], $tag);
820 * remove tag to users session variable
822 * this function removes the specified tag from
823 * users current tag selection
827 public function delTag($tag)
829 if(isset($_SESSION['searchfor_tag']))
830 unset($_SESSION['searchfor_tag']);
832 if(isset($_SESSION['selected_tags'])) {
833 $key = array_search($tag, $_SESSION['selected_tags']);
834 unset($_SESSION['selected_tags'][$key]);
835 sort($_SESSION['selected_tags']);
843 * reset tag selection
845 * if there is any tag selection, it will be
848 public function resetTags()
850 if(isset($_SESSION['selected_tags']))
851 unset($_SESSION['selected_tags']);
856 * returns the value for the autocomplet tag-search
859 public function get_xml_tag_list()
861 if(!isset($_GET['search']) || !is_string($_GET['search']))
862 $_GET['search'] = '';
867 /* retrive tags from database */
870 $matched_tags = Array();
872 header("Content-Type: text/xml");
874 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
875 $string.= "<results>\n";
877 foreach($this->avail_tags as $tag)
879 if(!empty($_GET['search']) &&
880 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
881 count($matched_tags) < $length) {
883 $count = $this->get_num_photos($tag);
886 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
889 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
895 /* if we have collected enough items, break out */
896 if(count($matched_tags) >= $length)
900 $string.= "</results>\n";
904 } // get_xml_tag_list()
910 * if a specific photo was requested (external link)
911 * unset the session variable now
913 public function resetPhotoView()
915 if(isset($_SESSION['current_photo']))
916 unset($_SESSION['current_photo']);
918 } // resetPhotoView();
923 * if any tag search has taken place, reset it now
925 public function resetTagSearch()
927 if(isset($_SESSION['searchfor_tag']))
928 unset($_SESSION['searchfor_tag']);
930 } // resetTagSearch()
935 * if any name search has taken place, reset it now
937 public function resetNameSearch()
939 if(isset($_SESSION['searchfor_name']))
940 unset($_SESSION['searchfor_name']);
942 } // resetNameSearch()
947 * if any date search has taken place, reset
950 public function resetDateSearch()
952 if(isset($_SESSION['from_date']))
953 unset($_SESSION['from_date']);
954 if(isset($_SESSION['to_date']))
955 unset($_SESSION['to_date']);
957 } // resetDateSearch();
960 * return all photo according selection
962 * this function returns all photos based on
963 * the tag-selection, tag- or date-search.
964 * the tag-search also has to take care of AND
965 * and OR conjunctions
968 public function getPhotoSelection()
970 $matched_photos = Array();
971 $additional_where_cond = "";
973 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
974 $from_date = $_SESSION['from_date'];
975 $to_date = $_SESSION['to_date'];
976 $additional_where_cond.= "
977 p.time>='". $from_date ."'
979 p.time<='". $to_date ."'
983 if(isset($_SESSION['searchfor_name'])) {
984 if($this->dbver < 9) {
985 $additional_where_cond.= "
987 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
989 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
994 $additional_where_cond.= "
996 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
998 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1004 if(isset($_SESSION['sort_order'])) {
1005 $order_str = $this->get_sort_order();
1008 /* return a search result */
1009 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1011 SELECT DISTINCT pt1.photo_id
1013 INNER JOIN photo_tags pt2
1014 ON pt1.photo_id=pt2.photo_id
1018 ON pt1.photo_id=p.id
1021 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1023 if(isset($additional_where_cond) && !empty($additional_where_cond))
1024 $query_str.= "AND ". $additional_where_cond ." ";
1026 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1027 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1030 if(isset($order_str))
1031 $query_str.= $order_str;
1033 $result = $this->db->db_query($query_str);
1034 while($row = $this->db->db_fetch_object($result)) {
1035 array_push($matched_photos, $row['photo_id']);
1037 return $matched_photos;
1040 /* return according the selected tags */
1041 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1043 foreach($_SESSION['selected_tags'] as $tag)
1044 $selected.= $tag .",";
1045 $selected = substr($selected, 0, strlen($selected)-1);
1047 /* photo has to match at least on of the selected tags */
1048 if($_SESSION['tag_condition'] == 'or') {
1050 SELECT DISTINCT pt1.photo_id
1052 INNER JOIN photo_tags pt2
1053 ON pt1.photo_id=pt2.photo_id
1057 ON pt1.photo_id=p.id
1058 WHERE pt1.tag_id IN (". $selected .")
1060 if(isset($additional_where_cond) && !empty($additional_where_cond))
1061 $query_str.= "AND ". $additional_where_cond ." ";
1063 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1064 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1067 if(isset($order_str))
1068 $query_str.= $order_str;
1070 /* photo has to match all selected tags */
1071 elseif($_SESSION['tag_condition'] == 'and') {
1073 if(count($_SESSION['selected_tags']) >= 32) {
1074 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1075 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1079 /* Join together a table looking like
1081 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1083 so the query can quickly return all images matching the
1084 selected tags in an AND condition
1089 SELECT DISTINCT pt1.photo_id
1093 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1100 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1102 INNER JOIN photo_tags pt". ($i+2) ."
1103 ON pt1.photo_id=pt". ($i+2) .".photo_id
1108 ON pt1.photo_id=p.id
1110 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1111 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1113 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1116 if(isset($additional_where_cond) && !empty($additional_where_cond))
1117 $query_str.= "AND ". $additional_where_cond;
1119 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1120 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1123 if(isset($order_str))
1124 $query_str.= $order_str;
1128 $result = $this->db->db_query($query_str);
1129 while($row = $this->db->db_fetch_object($result)) {
1130 array_push($matched_photos, $row['photo_id']);
1132 return $matched_photos;
1135 /* return all available photos */
1137 SELECT DISTINCT p.id
1139 LEFT JOIN photo_tags pt
1145 if(isset($additional_where_cond) && !empty($additional_where_cond))
1146 $query_str.= "WHERE ". $additional_where_cond ." ";
1148 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1149 if(isset($additional_where_cond) && !empty($additional_where_cond))
1150 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1152 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1155 if(isset($order_str))
1156 $query_str.= $order_str;
1158 $result = $this->db->db_query($query_str);
1159 while($row = $this->db->db_fetch_object($result)) {
1160 array_push($matched_photos, $row['id']);
1162 return $matched_photos;
1164 } // getPhotoSelection()
1167 * control HTML ouput for photo index
1169 * this function provides all the necessary information
1170 * for the photo index template.
1172 public function showPhotoIndex()
1174 $photos = $this->getPhotoSelection();
1176 $count = count($photos);
1178 /* if all thumbnails should be shown on one page */
1179 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1183 /* thumbnails should be splitted up in several pages */
1184 elseif($this->cfg->thumbs_per_page > 0) {
1186 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1190 $begin_with = $_SESSION['begin_with'];
1193 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1197 $images[$thumbs] = Array();
1198 $img_height[$thumbs] = Array();
1199 $img_width[$thumbs] = Array();
1200 $img_id[$thumbs] = Array();
1201 $img_name[$thumbs] = Array();
1202 $img_fullname[$thumbs] = Array();
1203 $img_title = Array();
1205 for($i = $begin_with; $i < $end_with; $i++) {
1207 if(isset($photos[$i])) {
1209 $images[$thumbs] = $photos[$i];
1210 $img_id[$thumbs] = $i;
1211 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1212 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1213 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1215 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1217 if(file_exists($thumb_path)) {
1218 $info = getimagesize($thumb_path);
1219 $img_width[$thumbs] = $info[0];
1220 $img_height[$thumbs] = $info[1];
1226 // +1 for for smarty's selection iteration
1229 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1230 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1232 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1233 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1234 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1237 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1238 $this->tmpl->assign('tag_result', 1);
1241 /* do we have to display the page selector ? */
1242 if($this->cfg->thumbs_per_page != 0) {
1246 /* calculate the page switchers */
1247 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1248 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1250 if($begin_with != 0)
1251 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1252 if($end_with < $count)
1253 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1255 $photo_per_page = $this->cfg->thumbs_per_page;
1256 $last_page = ceil($count / $photo_per_page);
1258 /* get the current selected page */
1259 if($begin_with == 0) {
1263 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1270 for($i = 1; $i <= $last_page; $i++) {
1272 if($current_page == $i)
1273 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1274 elseif($current_page-1 == $i || $current_page+1 == $i)
1275 $style = "style=\"font-size: 105%;\"";
1276 elseif(($current_page-5 >= $i) && ($i != 1) ||
1277 ($current_page+5 <= $i) && ($i != $last_page))
1278 $style = "style=\"font-size: 75%;\"";
1282 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1285 $select.= ">". $i ."</a> ";
1287 // until 9 pages we show the selector from 1-9
1288 if($last_page <= 9) {
1289 $page_select.= $select;
1292 if($i == 1 /* first page */ ||
1293 $i == $last_page /* last page */ ||
1294 $i == $current_page /* current page */ ||
1295 $i == ceil($last_page * 0.25) /* first quater */ ||
1296 $i == ceil($last_page * 0.5) /* half */ ||
1297 $i == ceil($last_page * 0.75) /* third quater */ ||
1298 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1299 (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 */ ||
1300 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1301 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1303 $page_select.= $select;
1311 $page_select.= "......... ";
1316 /* only show the page selector if we have more then one page */
1318 $this->tmpl->assign('page_selector', $page_select);
1322 $current_tags = $this->getCurrentTags();
1323 $extern_link = "index.php?mode=showpi";
1324 $rss_link = "index.php?mode=rss";
1325 if($current_tags != "") {
1326 $extern_link.= "&tags=". $current_tags;
1327 $rss_link.= "&tags=". $current_tags;
1329 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1330 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1331 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1334 $export_link = "index.php?mode=export";
1335 $slideshow_link = "index.php?mode=slideshow";
1337 $this->tmpl->assign('extern_link', $extern_link);
1338 $this->tmpl->assign('slideshow_link', $slideshow_link);
1339 $this->tmpl->assign('export_link', $export_link);
1340 $this->tmpl->assign('rss_link', $rss_link);
1341 $this->tmpl->assign('count', $count);
1342 $this->tmpl->assign('width', $this->cfg->thumb_width);
1343 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1344 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1345 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1346 $this->tmpl->assign('images', $images);
1347 $this->tmpl->assign('img_width', $img_width);
1348 $this->tmpl->assign('img_height', $img_height);
1349 $this->tmpl->assign('img_id', $img_id);
1350 $this->tmpl->assign('img_name', $img_name);
1351 $this->tmpl->assign('img_fullname', $img_fullname);
1352 $this->tmpl->assign('img_title', $img_title);
1353 $this->tmpl->assign('thumbs', $thumbs);
1354 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1356 $this->tmpl->show("photo_index.tpl");
1358 /* if we are returning to photo index from an photo-view,
1359 scroll the window to the last shown photo-thumbnail.
1360 after this, unset the last_photo session variable.
1362 if(isset($_SESSION['last_photo'])) {
1363 print "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1364 unset($_SESSION['last_photo']);
1367 } // showPhotoIndex()
1370 * show credit template
1372 public function showCredits()
1374 $this->tmpl->assign('version', $this->cfg->version);
1375 $this->tmpl->assign('product', $this->cfg->product);
1376 $this->tmpl->assign('db_version', $this->dbver);
1377 $this->tmpl->show("credits.tpl");
1382 * create thumbnails for the requested width
1384 * this function creates image thumbnails of $orig_image
1385 * stored as $thumb_image. It will check if the image is
1386 * in a supported format, if necessary rotate the image
1387 * (based on EXIF orientation meta headers) and re-sizing.
1388 * @param string $orig_image
1389 * @param string $thumb_image
1390 * @param integer $width
1393 public function create_thumbnail($orig_image, $thumb_image, $width)
1395 if(!file_exists($orig_image)) {
1399 $mime = $this->get_mime_info($orig_image);
1401 /* check if original photo is a support image type */
1402 if(!$this->checkifImageSupported($mime))
1409 $meta = $this->get_meta_informations($orig_image);
1415 switch($meta['Orientation']) {
1416 case 1: /* top, left */
1417 /* nothing to do */ break;
1418 case 2: /* top, right */
1419 $rotate = 0; $flip_hori = true; break;
1420 case 3: /* bottom, left */
1421 $rotate = 180; break;
1422 case 4: /* bottom, right */
1423 $flip_vert = true; break;
1424 case 5: /* left side, top */
1425 $rotate = 90; $flip_vert = true; break;
1426 case 6: /* right side, top */
1427 $rotate = 90; break;
1428 case 7: /* left side, bottom */
1429 $rotate = 270; $flip_vert = true; break;
1430 case 8: /* right side, bottom */
1431 $rotate = 270; break;
1434 $src_img = @imagecreatefromjpeg($orig_image);
1440 $src_img = @imagecreatefrompng($orig_image);
1444 case 'image/x-portable-pixmap':
1446 $src_img = new Imagick($orig_image);
1447 $handler = "imagick";
1452 if(!isset($src_img) || empty($src_img)) {
1453 print "Can't load image from ". $orig_image ."\n";
1461 /* grabs the height and width */
1462 $cur_width = imagesx($src_img);
1463 $cur_height = imagesy($src_img);
1465 // If requested width is more then the actual image width,
1466 // do not generate a thumbnail, instead safe the original
1467 // as thumbnail but with lower quality. But if the image
1468 // is to heigh too, then we still have to resize it.
1469 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1470 $result = imagejpeg($src_img, $thumb_image, 75);
1471 imagedestroy($src_img);
1478 $cur_width = $src_img->getImageWidth();
1479 $cur_height = $src_img->getImageHeight();
1481 // If requested width is more then the actual image width,
1482 // do not generate a thumbnail, instead safe the original
1483 // as thumbnail but with lower quality. But if the image
1484 // is to heigh too, then we still have to resize it.
1485 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1486 $src_img->setCompressionQuality(75);
1487 $src_img->setImageFormat('jpeg');
1488 $src_img->writeImage($thumb_image);
1490 $src_img->destroy();
1497 // If the image will be rotate because EXIF orientation said so
1498 // 'virtually rotate' the image for further calculations
1499 if($rotate == 90 || $rotate == 270) {
1501 $cur_width = $cur_height;
1505 /* calculates aspect ratio */
1506 $aspect_ratio = $cur_height / $cur_width;
1509 if($aspect_ratio < 1) {
1511 $new_h = abs($new_w * $aspect_ratio);
1513 /* 'virtually' rotate the image and calculate it's ratio */
1514 $tmp_w = $cur_height;
1515 $tmp_h = $cur_width;
1516 /* now get the ratio from the 'rotated' image */
1517 $tmp_ratio = $tmp_h/$tmp_w;
1518 /* now calculate the new dimensions */
1520 $tmp_h = abs($tmp_w * $tmp_ratio);
1522 // now that we know, how high they photo should be, if it
1523 // gets rotated, use this high to scale the image
1525 $new_w = abs($new_h / $aspect_ratio);
1527 // If the image will be rotate because EXIF orientation said so
1528 // now 'virtually rotate' back the image for the image manipulation
1529 if($rotate == 90 || $rotate == 270) {
1540 /* creates new image of that size */
1541 $dst_img = imagecreatetruecolor($new_w, $new_h);
1543 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1545 /* copies resized portion of original image into new image */
1546 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1548 /* needs the image to be flipped horizontal? */
1550 $this->_debug("(FLIP)");
1551 $dst_img = $this->flipImage($dst_img, 'hori');
1553 /* needs the image to be flipped vertical? */
1555 $this->_debug("(FLIP)");
1556 $dst_img = $this->flipImage($dst_img, 'vert');
1560 $this->_debug("(ROTATE)");
1561 $dst_img = $this->rotateImage($dst_img, $rotate);
1564 /* write down new generated file */
1565 $result = imagejpeg($dst_img, $thumb_image, 75);
1567 /* free your mind */
1568 imagedestroy($dst_img);
1569 imagedestroy($src_img);
1571 if($result === false) {
1572 print "Can't write thumbnail ". $thumb_image ."\n";
1582 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1584 /* needs the image to be flipped horizontal? */
1586 $this->_debug("(FLIP)");
1587 $src_img->rotateImage(new ImagickPixel(), 90);
1588 $src_img->flipImage();
1589 $src_img->rotateImage(new ImagickPixel(), -90);
1591 /* needs the image to be flipped vertical? */
1593 $this->_debug("(FLIP)");
1594 $src_img->flipImage();
1598 $this->_debug("(ROTATE)");
1599 $src_img->rotateImage(new ImagickPixel(), $rotate);
1602 $src_img->setCompressionQuality(75);
1603 $src_img->setImageFormat('jpeg');
1605 if(!$src_img->writeImage($thumb_image)) {
1606 print "Can't write thumbnail ". $thumb_image ."\n";
1611 $src_img->destroy();
1618 } // create_thumbnail()
1621 * return all exif meta data from the file
1622 * @param string $file
1625 public function get_meta_informations($file)
1627 return exif_read_data($file);
1629 } // get_meta_informations()
1632 * create phpfspot own sqlite database
1634 * this function creates phpfspots own sqlite database
1635 * if it does not exist yet. this own is used to store
1636 * some necessary informations (md5 sum's, ...).
1638 public function check_config_table()
1640 // if the config table doesn't exist yet, create it
1641 if(!$this->cfg_db->db_check_table_exists("images")) {
1642 $this->cfg_db->db_exec("
1643 CREATE TABLE images (
1644 img_idx int primary key,
1650 } // check_config_table
1653 * Generates a thumbnail from photo idx
1655 * This function will generate JPEG thumbnails from provided F-Spot photo
1658 * 1. Check if all thumbnail generations (width) are already in place and
1660 * 2. Check if the md5sum of the original file has changed
1661 * 3. Generate the thumbnails if needed
1662 * @param integer $idx
1663 * @param integer $force
1664 * @param boolean $overwrite
1666 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1670 $resolutions = Array(
1671 $this->cfg->thumb_width,
1672 $this->cfg->photo_width,
1673 $this->cfg->mini_width,
1677 /* get details from F-Spot's database */
1678 $details = $this->get_photo_details($idx);
1680 /* calculate file MD5 sum */
1681 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1683 if(!file_exists($full_path)) {
1684 $this->_error("File ". $full_path ." does not exist\n");
1688 if(!is_readable($full_path)) {
1689 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1693 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1695 /* If Nikon NEF format, we need to treat it another way */
1696 if(isset($this->cfg->dcraw_bin) &&
1697 file_exists($this->cfg->dcraw_bin) &&
1698 is_executable($this->cfg->dcraw_bin) &&
1699 preg_match('/\.nef$/i', $details['uri'])) {
1701 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1703 /* if PPM file does not exist, let dcraw convert it from NEF */
1704 if(!file_exists($ppm_path)) {
1705 system($this->cfg->dcraw_bin ." -a ". $full_path);
1708 /* for now we handle the PPM instead of the NEF */
1709 $full_path = $ppm_path;
1713 $file_md5 = md5_file($full_path);
1716 foreach($resolutions as $resolution) {
1718 $generate_it = false;
1720 $thumb_sub_path = substr($file_md5, 0, 2);
1721 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1723 /* if thumbnail-subdirectory does not exist yet, create it */
1724 if(!file_exists(dirname($thumb_path))) {
1725 mkdir(dirname($thumb_path), 0755);
1728 /* if the thumbnail file doesn't exist, create it */
1729 if(!file_exists($thumb_path)) {
1730 $generate_it = true;
1732 /* if the file hasn't changed there is no need to regen the thumb */
1733 elseif($file_md5 != $this->getMD5($idx) || $force) {
1734 $generate_it = true;
1737 if($generate_it || $overwrite) {
1739 $this->_debug(" ". $resolution ."px");
1740 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1748 $this->_debug(" already exist");
1751 /* set the new/changed MD5 sum for the current photo */
1753 $this->setMD5($idx, $file_md5);
1756 $this->_debug("\n");
1761 * returns stored md5 sum for a specific photo
1763 * this function queries the phpfspot database for a
1764 * stored MD5 checksum of the specified photo
1765 * @param integer $idx
1766 * @return string|null
1768 public function getMD5($idx)
1770 $result = $this->cfg_db->db_query("
1773 WHERE img_idx='". $idx ."'
1779 $img = $this->cfg_db->db_fetch_object($result);
1780 return $img['img_md5'];
1785 * set MD5 sum for the specific photo
1786 * @param integer $idx
1787 * @param string $md5
1789 private function setMD5($idx, $md5)
1791 $result = $this->cfg_db->db_exec("
1792 REPLACE INTO images (img_idx, img_md5)
1793 VALUES ('". $idx ."', '". $md5 ."')
1799 * store current tag condition
1801 * this function stores the current tag condition
1802 * (AND or OR) in the users session variables
1803 * @param string $mode
1806 public function setTagCondition($mode)
1808 $_SESSION['tag_condition'] = $mode;
1812 } // setTagCondition()
1815 * invoke tag & date search
1817 * this function will return all matching tags and store
1818 * them in the session variable selected_tags. furthermore
1819 * it also handles the date search.
1820 * getPhotoSelection() will then only return the matching
1824 public function startSearch()
1826 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1827 $from = $_POST['from'];
1829 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
1833 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
1834 $searchfor_tag = $_POST['for_tag'];
1835 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
1838 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
1839 $searchfor_name = $_POST['for_name'];
1840 $_SESSION['searchfor_name'] = $_POST['for_name'];
1845 if(isset($from) && !empty($from))
1846 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1848 unset($_SESSION['from_date']);
1850 if(isset($to) && !empty($to))
1851 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1853 unset($_SESSION['to_date']);
1855 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
1856 /* new search, reset the current selected tags */
1857 $_SESSION['selected_tags'] = Array();
1858 foreach($this->avail_tags as $tag) {
1859 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
1860 array_push($_SESSION['selected_tags'], $tag);
1869 * updates sort order in session variable
1871 * this function is invoked by RPC and will sort the requested
1872 * sort order in the session variable.
1873 * @param string $sort_order
1876 public function updateSortOrder($order)
1878 if(isset($this->sort_orders[$order])) {
1879 $_SESSION['sort_order'] = $order;
1883 return "unkown error";
1885 } // updateSortOrder()
1890 * this function rotates the image according the
1892 * @param string $img
1893 * @param integer $degress
1896 private function rotateImage($img, $degrees)
1898 if(function_exists("imagerotate")) {
1899 $img = imagerotate($img, $degrees, 0);
1901 function imagerotate($src_img, $angle)
1903 $src_x = imagesx($src_img);
1904 $src_y = imagesy($src_img);
1905 if ($angle == 180) {
1909 elseif ($src_x <= $src_y) {
1913 elseif ($src_x >= $src_y) {
1918 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1919 imagealphablending($rotate, false);
1924 for ($y = 0; $y < ($src_y); $y++) {
1925 for ($x = 0; $x < ($src_x); $x++) {
1926 $color = imagecolorat($src_img, $x, $y);
1927 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1933 for ($y = 0; $y < ($src_y); $y++) {
1934 for ($x = 0; $x < ($src_x); $x++) {
1935 $color = imagecolorat($src_img, $x, $y);
1936 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1942 for ($y = 0; $y < ($src_y); $y++) {
1943 for ($x = 0; $x < ($src_x); $x++) {
1944 $color = imagecolorat($src_img, $x, $y);
1945 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1959 $img = imagerotate($img, $degrees);
1968 * returns flipped image
1970 * this function will return an either horizontal or
1971 * vertical flipped truecolor image.
1972 * @param string $image
1973 * @param string $mode
1976 private function flipImage($image, $mode)
1978 $w = imagesx($image);
1979 $h = imagesy($image);
1980 $flipped = imagecreatetruecolor($w, $h);
1984 for ($y = 0; $y < $h; $y++) {
1985 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1989 for ($x = 0; $x < $w; $x++) {
1990 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2000 * return all assigned tags for the specified photo
2001 * @param integer $idx
2004 private function get_photo_tags($idx)
2006 $result = $this->db->db_query("
2009 INNER JOIN photo_tags pt
2011 WHERE pt.photo_id='". $idx ."'
2016 while($row = $this->db->db_fetch_object($result)) {
2017 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2019 $tags[$row['id']] = $row['name'];
2024 } // get_photo_tags()
2027 * create on-the-fly images with text within
2028 * @param string $txt
2029 * @param string $color
2030 * @param integer $space
2031 * @param integer $font
2034 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2036 if (strlen($color) != 6)
2039 $int = hexdec($color);
2040 $h = imagefontheight($font);
2041 $fw = imagefontwidth($font);
2042 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2043 $lines = count($txt);
2044 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2045 $bg = imagecolorallocate($im, 255, 255, 255);
2046 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2049 foreach ($txt as $text) {
2050 $x = (($w - ($fw * strlen($text))) / 2);
2051 imagestring($im, $font, $x, $y, $text, $color);
2052 $y += ($h + $space);
2055 Header("Content-type: image/png");
2058 } // showTextImage()
2061 * check if all requirements are met
2064 private function check_requirements()
2066 if(!function_exists("imagecreatefromjpeg")) {
2067 print "PHP GD library extension is missing<br />\n";
2071 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2072 print "PHP SQLite3 library extension is missing<br />\n";
2076 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2077 ini_set('track_errors', 1);
2078 @include_once 'HTML/AJAX/Server.php';
2079 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2080 print "PEAR HTML_AJAX package is missing<br />\n";
2083 @include_once 'Calendar/Calendar.php';
2084 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2085 print "PEAR Calendar package is missing<br />\n";
2088 @include_once 'Console/Getopt.php';
2089 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2090 print "PEAR Console_Getopt package is missing<br />\n";
2093 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2094 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2095 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2098 ini_restore('track_errors');
2105 } // check_requirements()
2107 private function _debug($text)
2109 if($this->fromcmd) {
2116 * check if specified MIME type is supported
2117 * @param string $mime
2120 public function checkifImageSupported($mime)
2122 $supported_types = Array(
2125 "image/x-portable-pixmap",
2129 if(in_array($mime, $supported_types))
2134 } // checkifImageSupported()
2138 * @param string $text
2140 public function _error($text)
2142 switch($this->cfg->logging) {
2145 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2146 print $text ."<br />\n";
2152 error_log($text, 3, $his->cfg->log_file);
2156 $this->runtime_error = true;
2161 * output calendard input fields
2162 * @param string $mode
2165 private function get_calendar($mode)
2167 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2168 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2169 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2171 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2172 if(!isset($_SESSION[$mode .'_date']))
2173 $output.= " disabled=\"disabled\"";
2175 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2176 if(!isset($_SESSION[$mode .'_date']))
2177 $output.= " disabled=\"disabled\"";
2179 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2180 if(!isset($_SESSION[$mode .'_date']))
2181 $output.= " disabled=\"disabled\"";
2189 * output calendar matrix
2190 * @param integer $year
2191 * @param integer $month
2192 * @param integer $day
2194 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2196 if (!isset($year)) $year = date('Y');
2197 if (!isset($month)) $month = date('m');
2198 if (!isset($day)) $day = date('d');
2203 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2204 require_once CALENDAR_ROOT.'Day.php';
2207 $month = new Calendar_Month_Weekdays($year,$month);
2210 $prevStamp = $month->prevMonth(true);
2211 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2212 $nextStamp = $month->nextMonth(true);
2213 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2215 $selectedDays = array (
2216 new Calendar_Day($year,$month,$day),
2217 new Calendar_Day($year,12,25),
2220 // Build the days in the month
2221 $month->build($selectedDays);
2223 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2224 $this->tmpl->assign('prev_month', $prev);
2225 $this->tmpl->assign('next_month', $next);
2227 while ( $day = $month->fetch() ) {
2229 if(!isset($matrix[$rows]))
2230 $matrix[$rows] = Array();
2234 $dayStamp = $day->thisDay(true);
2235 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2237 // isFirst() to find start of week
2238 if ( $day->isFirst() )
2241 if ( $day->isSelected() ) {
2242 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2243 } else if ( $day->isEmpty() ) {
2244 $string.= "<td> </td>\n";
2246 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2249 // isLast() to find end of week
2250 if ( $day->isLast() )
2251 $string.= "</tr>\n";
2253 $matrix[$rows][$cols] = $string;
2263 $this->tmpl->assign('matrix', $matrix);
2264 $this->tmpl->assign('rows', $rows);
2265 $this->tmpl->show("calendar.tpl");
2267 } // get_calendar_matrix()
2270 * output export page
2271 * @param string $mode
2273 public function getExport($mode)
2275 $pictures = $this->getPhotoSelection();
2276 $current_tags = $this->getCurrentTags();
2278 foreach($pictures as $picture) {
2280 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2281 if($current_tags != "") {
2282 $orig_url.= "&tags=". $current_tags;
2284 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2285 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2288 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2293 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2294 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2298 // "[%pictureurl% %thumbnailurl%]"
2299 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2302 case 'MoinMoinList':
2303 // " * [%pictureurl% %thumbnailurl%]"
2304 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2315 public function getRSSFeed()
2317 Header("Content-type: text/xml; charset=utf-8");
2318 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2321 xmlns:media="http://search.yahoo.com/mrss/"
2322 xmlns:dc="http://purl.org/dc/elements/1.1/"
2325 <title>phpfspot</title>
2326 <description>phpfspot RSS feed</description>
2327 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2328 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2329 <generator>phpfspot</generator>
2332 $pictures = $this->getPhotoSelection();
2333 $current_tags = $this->getCurrentTags();
2335 foreach($pictures as $picture) {
2337 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2338 if($current_tags != "") {
2339 $orig_url.= "&tags=". $current_tags;
2341 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2342 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2345 $details = $this->get_photo_details($picture);
2347 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2348 $thumb_html = htmlspecialchars("
2349 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2351 ". $details['description']);
2353 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2355 /* get EXIF information if JPEG */
2356 if($details['mime'] == "image/jpeg") {
2357 $meta = $this->get_meta_informations($orig_path);
2360 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2364 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2365 <link><?php print htmlspecialchars($orig_url); ?></link>
2366 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2367 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2369 <?php print $thumb_html; ?>
2371 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2386 * return all selected tags as one string
2389 private function getCurrentTags()
2392 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2393 foreach($_SESSION['selected_tags'] as $tag)
2394 $current_tags.= $tag .",";
2395 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2397 return $current_tags;
2399 } // getCurrentTags()
2402 * return the current photo
2404 public function getCurrentPhoto()
2406 if(isset($_SESSION['current_photo'])) {
2407 print $_SESSION['current_photo'];
2409 } // getCurrentPhoto()
2412 * tells the client browser what to do
2414 * this function is getting called via AJAX by the
2415 * client browsers. it will tell them what they have
2416 * to do next. This is necessary for directly jumping
2417 * into photo index or single photo view when the are
2418 * requested with specific URLs
2421 public function whatToDo()
2423 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2424 return "show_photo";
2426 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2427 return "showpi_tags";
2429 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2433 return "nothing special";
2438 * return the current process-user
2441 private function getuid()
2443 if($uid = posix_getuid()) {
2444 if($user = posix_getpwuid($uid)) {
2445 return $user['name'];
2454 * returns a select-dropdown box to select photo index sort parameters
2455 * @param array $params
2456 * @param smarty $smarty
2459 public function smarty_sort_select_list($params, &$smarty)
2463 foreach($this->sort_orders as $key => $value) {
2464 $output.= "<option value=\"". $key ."\"";
2465 if($key == $_SESSION['sort_order']) {
2466 $output.= " selected=\"selected\"";
2468 $output.= ">". $value ."</option>";
2473 } // smarty_sort_select_list()
2476 * returns the currently selected sort order
2479 private function get_sort_order()
2481 switch($_SESSION['sort_order']) {
2483 return " ORDER BY p.time ASC";
2486 return " ORDER BY p.time DESC";
2489 if($this->dbver < 9) {
2490 return " ORDER BY p.name ASC";
2493 return " ORDER BY basename(p.uri) ASC";
2497 if($this->dbver < 9) {
2498 return " ORDER BY p.name DESC";
2501 return " ORDER BY basename(p.uri) DESC";
2505 return " ORDER BY t.name ASC ,p.time ASC";
2508 return " ORDER BY t.name DESC ,p.time ASC";
2512 } // get_sort_order()
2515 * return the next to be shown slide show image
2517 * this function returns the URL of the next image
2518 * in the slideshow sequence.
2521 public function getNextSlideShowImage()
2523 $all_photos = $this->getPhotoSelection();
2525 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2526 $_SESSION['slideshow_img'] = 0;
2528 $_SESSION['slideshow_img']++;
2530 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2532 } // getNextSlideShowImage()
2535 * return the previous to be shown slide show image
2537 * this function returns the URL of the previous image
2538 * in the slideshow sequence.
2541 public function getPrevSlideShowImage()
2543 $all_photos = $this->getPhotoSelection();
2545 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2546 $_SESSION['slideshow_img'] = 0;
2548 $_SESSION['slideshow_img']--;
2550 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2552 } // getPrevSlideShowImage()
2554 public function resetSlideShow()
2556 if(isset($_SESSION['slideshow_img']))
2557 unset($_SESSION['slideshow_img']);
2559 } // resetSlideShow()
2564 * this function will get all photos from the fspot
2565 * database and randomly return ONE entry
2567 * saddly there is yet no sqlite3 function which returns
2568 * the bulk result in array, so we have to fill up our
2572 public function get_random_photo()
2581 /* if show_tags is set, only return details for photos which
2582 are specified to be shown
2584 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2586 INNER JOIN photo_tags pt
2591 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2594 $result = $this->db->db_query($query_str);
2596 while($row = $this->db->db_fetch_object($result)) {
2597 array_push($all, $row['id']);
2600 return $all[array_rand($all)];
2602 } // get_random_photo()
2605 * get random photo tag photo
2607 * this function will get all photos tagged with the requested
2608 * tag from the fspot database and randomly return ONE entry
2610 * saddly there is yet no sqlite3 function which returns
2611 * the bulk result in array, so we have to fill up our
2615 public function get_random_tag_photo($tagidx)
2622 INNER JOIN photo_tags pt
2626 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2634 pt.tag_id LIKE '". $tagidx ."'
2637 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2640 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2644 $result = $this->db->db_query($query_str);
2646 while($row = $this->db->db_fetch_object($result)) {
2647 array_push($all, $row['id']);
2650 return $all[array_rand($all)];
2652 } // get_random_tag_photo()
2655 * validates provided date
2657 * this function validates if the provided date
2658 * contains a valid date and will return true
2660 * @param string $date_str
2663 public function isValidDate($date_str)
2665 $timestamp = strtotime($date_str);
2667 if(is_numeric($timestamp))
2675 * timestamp to string conversion
2676 * @param integer $timestamp
2679 private function ts2str($timestamp)
2681 return strftime("%Y-%m-%d", $timestamp);
2685 * extract tag-names from $_GET['tags']
2686 * @param string $tags_str
2689 private function extractTags($tags_str)
2691 $not_validated = split(',', $tags_str);
2692 $validated = array();
2694 foreach($not_validated as $tag) {
2695 if(is_numeric($tag))
2696 array_push($validated, $tag);
2704 * returns the full path to a thumbnail
2705 * @param integer $width
2706 * @param integer $photo
2709 public function get_thumb_path($width, $photo)
2711 $md5 = $this->getMD5($photo);
2712 $sub_path = substr($md5, 0, 2);
2713 return $this->cfg->thumb_path
2721 } // get_thumb_path()
2724 * returns server's virtual host name
2727 private function get_server_name()
2729 return $_SERVER['SERVER_NAME'];
2730 } // get_server_name()
2733 * returns type of webprotocol which is currently used
2736 private function get_web_protocol()
2738 if(!isset($_SERVER['HTTPS']))
2742 } // get_web_protocol()
2745 * return url to this phpfspot installation
2748 private function get_phpfspot_url()
2750 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2751 } // get_phpfspot_url()
2754 * returns the number of photos which are tagged with $tag_id
2755 * @param integer $tag_id
2758 public function get_num_photos($tag_id)
2760 if($result = $this->db->db_fetchSingleRow("
2761 SELECT count(*) as number
2764 tag_id LIKE '". $tag_id ."'")) {
2766 return $result['number'];
2772 } // get_num_photos()
2775 * check file exists and is readable
2777 * returns true, if everything is ok, otherwise false
2778 * if $silent is not set, this function will output and
2780 * @param string $file
2781 * @param boolean $silent
2784 private function check_readable($file, $silent = null)
2786 if(!file_exists($file)) {
2788 print "File \"". $file ."\" does not exist.\n";
2792 if(!is_readable($file)) {
2794 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2800 } // check_readable()
2803 * check if all needed indices are present
2805 * this function checks, if some needed indices are already
2806 * present, or if not, create them on the fly. they are
2807 * necessary to speed up some queries like that one look for
2808 * all tags, when show_tags is specified in the configuration.
2810 private function checkDbIndices()
2812 $result = $this->db->db_exec("
2813 CREATE INDEX IF NOT EXISTS
2820 } // checkDbIndices()
2823 * retrive F-Spot database version
2825 * this function will return the F-Spot database version number
2826 * It is stored within the sqlite3 database in the table meta
2827 * @return string|null
2829 public function getFspotDBVersion()
2831 if($result = $this->db->db_fetchSingleRow("
2832 SELECT data as version
2835 name LIKE 'F-Spot Database Version'
2837 return $result['version'];
2841 } // getFspotDBVersion()
2844 * parse the provided URI and will returned the requested chunk
2845 * @param string $uri
2846 * @param string $mode
2849 public function parse_uri($uri, $mode)
2851 if(($components = parse_url($uri)) !== false) {
2855 return basename($components['path']);
2858 return dirname($components['path']);
2861 return $components['path'];
2871 * validate config options
2873 * this function checks if all necessary configuration options are
2874 * specified and set.
2877 private function check_config_options()
2879 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
2880 $this->_error("Please set \$page_title in phpfspot_cfg");
2882 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
2883 $this->_error("Please set \$base_path in phpfspot_cfg");
2885 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
2886 $this->_error("Please set \$web_path in phpfspot_cfg");
2888 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
2889 $this->_error("Please set \$thumb_path in phpfspot_cfg");
2891 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
2892 $this->_error("Please set \$smarty_path in phpfspot_cfg");
2894 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
2895 $this->_error("Please set \$fspot_db in phpfspot_cfg");
2897 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
2898 $this->_error("Please set \$db_access in phpfspot_cfg");
2900 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
2901 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
2903 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
2904 $this->_error("Please set \$thumb_width in phpfspot_cfg");
2906 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
2907 $this->_error("Please set \$thumb_height in phpfspot_cfg");
2909 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
2910 $this->_error("Please set \$photo_width in phpfspot_cfg");
2912 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
2913 $this->_error("Please set \$mini_width in phpfspot_cfg");
2915 if(!isset($this->cfg->thumbs_per_page))
2916 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
2918 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
2919 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
2921 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
2922 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
2924 if(!isset($this->cfg->hide_tags))
2925 $this->_error("Please set \$hide_tags in phpfspot_cfg");
2927 if(!isset($this->cfg->theme_name))
2928 $this->_error("Please set \$theme_name in phpfspot_cfg");
2930 if(!isset($this->cfg->logging))
2931 $this->_error("Please set \$logging in phpfspot_cfg");
2933 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
2935 if(!isset($this->cfg->log_file))
2936 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
2938 if(!is_writeable($this->cfg->log_file))
2939 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
2943 /* check for pending slash on web_path */
2944 if(!preg_match("/\/$/", $this->cfg->web_path))
2945 $this->cfg->web_path.= "/";
2947 return $this->runtime_error;
2949 } // check_config_options()
2952 * cleanup phpfspot own database
2954 * When photos are getting delete from F-Spot, there will remain
2955 * remain some residues in phpfspot own database. This function
2956 * will try to wipe them out.
2958 public function cleanup_phpfspot_db()
2960 $to_delete = Array();
2962 $result = $this->cfg_db->db_query("
2965 ORDER BY img_idx ASC
2968 while($row = $this->cfg_db->db_fetch_object($result)) {
2969 if(!$this->db->db_fetchSingleRow("
2972 WHERE id='". $row['img_idx'] ."'")) {
2974 array_push($to_delete, $row['img_idx'], ',');
2978 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
2980 $this->cfg_db->db_exec("
2982 WHERE img_idx IN (". implode($to_delete) .")
2985 } // cleanup_phpfspot_db()
2988 * return first image of the page, the $current photo
2991 * this function is used to find out the first photo of the
2992 * current page, in which the $current photo lies. this is
2993 * used to display the correct photo, when calling showPhotoIndex()
2995 * @param integer $current
2996 * @param integer $max
2999 private function getCurrentPage($current, $max)
3001 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3002 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3003 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3009 } // getCurrentPage()
3014 * this function tries to find out the correct mime-type
3015 * for the provided file.
3016 * @param string $file
3019 public function get_mime_info($file)
3021 $details = getimagesize($file);
3023 /* if getimagesize() returns empty, try at least to find out the
3026 if(empty($details) && function_exists('mime_content_type')) {
3028 // mime_content_type is marked as deprecated in the documentation,
3029 // but is it really necessary to force users to install a PECL
3031 $details['mime'] = mime_content_type($file);
3034 return $details['mime'];
3036 } // get_mime_info()
3039 * return tag-name by tag-idx
3041 * this function returns the tag-name for the requested
3042 * tag specified by tag-idx.
3043 * @param integer $idx
3046 public function get_tag_name($idx)
3048 if($result = $this->db->db_fetchSingleRow("
3052 id LIKE '". $idx ."'")) {
3054 return $result['name'];
3063 private function parse_user_friendly_url($request_uri)
3065 if(preg_match('/\/photoview\/|\/photo\//', $request_uri)) {
3067 $options = explode('/', $request_uri);
3069 switch($options[1]) {
3071 if(is_numeric($options[2])) {
3072 return $this->showPhoto($options[2]);
3076 if(is_numeric($options[2])) {
3077 require_once "phpfspot_img.php";
3078 $img = new PHPFSPOT_IMG;
3079 if(isset($options[3]) && is_numeric($options[3]))
3080 $img->showImg($options[2], $options[3]);
3082 $img->showImg($options[2]);
3089 } // parse_user_friendly_url()