3 /***************************************************************************
5 * Copyright (c) by Andreas Unterkircher, unki@netshadow.at
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 ***************************************************************************/
24 require_once "phpfspot_cfg.php";
25 require_once "phpfspot_db.php";
36 private $runtime_error = false;
42 * this function will be called on class construct
43 * and will check requirements, loads configuration,
44 * open databases and start the user session
46 public function __construct()
48 $this->cfg = new PHPFSPOT_CFG;
50 /* verify config settings */
51 if($this->check_config_options()) {
55 /* set application name and version information */
56 $this->cfg->product = "phpfspot";
57 $this->cfg->version = "1.4";
59 $this->sort_orders= array(
60 'date_asc' => 'Date ↑',
61 'date_desc' => 'Date ↓',
62 'name_asc' => 'Name ↑',
63 'name_desc' => 'Name ↓',
64 'tags_asc' => 'Tags ↑',
65 'tags_desc' => 'Tags ↓',
68 /* Check necessary requirements */
69 if(!$this->check_requirements()) {
73 /******* Opening F-Spot's sqlite database *********/
75 /* Check if database file is writeable */
76 if(!is_writeable($this->cfg->fspot_db)) {
77 print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
81 /* open the database */
82 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
84 /* change sqlite temp directory, if requested */
85 if(isset($this->cfg->sqlite_temp_dir)) {
88 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
92 $this->dbver = $this->getFspotDBVersion();
94 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
95 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
99 if(!is_writeable($this->cfg->thumb_path)) {
100 print $this->cfg->thumb_path .": directory is not writeable for user ". $this->getuid() ."\n";
104 /******* Opening phpfspot's sqlite database *********/
106 /* Check if directory where the database file is stored is writeable */
107 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
108 print dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() ."\n";
112 /* Check if database file is writeable */
113 if(!is_writeable($this->cfg->phpfspot_db)) {
114 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
118 /* open the database */
119 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
121 /* change sqlite temp directory, if requested */
122 if(isset($this->cfg->sqlite_temp_dir)) {
123 $this->cfg_db->db_exec("
125 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
129 /* Check if some tables need to be created */
130 $this->check_config_table();
132 /* overload Smarty class with our own template handler */
133 require_once "phpfspot_tmpl.php";
134 $this->tmpl = new PHPFSPOT_TMPL($this);
136 /* check if all necessary indices exist */
137 $this->checkDbIndices();
139 /* if session is not yet started, do it now */
140 if(session_id() == "")
143 if(!isset($_SESSION['tag_condition']))
144 $_SESSION['tag_condition'] = 'or';
146 if(!isset($_SESSION['sort_order']))
147 $_SESSION['sort_order'] = 'date_desc';
149 if(!isset($_SESSION['searchfor_tag']))
150 $_SESSION['searchfor_tag'] = '';
152 // if begin_with is still set but thumbs_per_page is now 0, unset it
153 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
154 unset($_SESSION['begin_with']);
158 public function __destruct()
164 * show - generate html output
166 * this function can be called after the constructor has
167 * prepared everyhing. it will load the index.tpl smarty
168 * template. if necessary it will registere pre-selects
169 * (photo index, photo, tag search, date search) into
172 public function show()
174 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
175 $this->tmpl->assign('page_title', $this->cfg->page_title);
176 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
177 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
179 if(isset($_GET['mode'])) {
181 $_SESSION['start_action'] = $_GET['mode'];
183 switch($_GET['mode']) {
185 if(isset($_GET['tags'])) {
186 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
188 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
189 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
191 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
192 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
196 if(isset($_GET['tags'])) {
197 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
198 $_SESSION['start_action'] = 'showp';
200 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
201 $_SESSION['current_photo'] = $_GET['id'];
202 $_SESSION['start_action'] = 'showp';
204 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
205 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
207 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
208 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
212 $this->tmpl->show("export.tpl");
216 $this->tmpl->show("slideshow.tpl");
220 if(isset($_GET['tags'])) {
221 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
223 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
224 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
226 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
227 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
235 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
236 $this->tmpl->assign('date_search_enabled', true);
238 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
239 $this->tmpl->assign('from_date', $this->get_calendar('from'));
240 $this->tmpl->assign('to_date', $this->get_calendar('to'));
241 $this->tmpl->assign('content_page', 'welcome.tpl');
242 $this->tmpl->show("index.tpl");
247 * get_tags - grab all tags of f-spot's database
249 * this function will get all available tags from
250 * the f-spot database and store them within two
251 * arrays within this class for later usage. in
252 * fact, if the user requests (hide_tags) it will
253 * opt-out some of them.
255 * this function is getting called once by show()
257 private function get_tags()
259 $this->avail_tags = Array();
262 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
265 DISTINCT t1.id as id, t1.name as name
268 INNER JOIN photo_tags
269 pt2 ON pt1.photo_id=pt2.photo_id
275 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
277 t1.sort_priority ASC";
279 $result = $this->db->db_query($query_str);
283 $result = $this->db->db_query("
286 ORDER BY sort_priority ASC
290 while($row = $this->db->db_fetch_object($result)) {
292 $tag_id = $row['id'];
293 $tag_name = $row['name'];
295 /* if the user has specified to ignore this tag in phpfspot's
296 configuration, ignore it here so it does not get added to
299 if(in_array($row['name'], $this->cfg->hide_tags))
302 /* if you include the following if-clause and the user has specified
303 to only show certain tags which are specified in phpfspot's
304 configuration, ignore all others so they will not be added to the
306 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
307 !in_array($row['name'], $this->cfg->show_tags))
311 $this->tags[$tag_id] = $tag_name;
312 $this->avail_tags[$count] = $tag_id;
320 * extract all photo details
322 * retrieve all available details from f-spot's
323 * database and return them as object
325 public function get_photo_details($idx)
327 if($this->dbver < 9) {
329 SELECT p.id, p.name, p.time, p.directory_path, p.description
335 SELECT p.id, p.uri, p.time, p.description
340 /* if show_tags is set, only return details for photos which
341 are specified to be shown
343 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
345 INNER JOIN photo_tags pt
349 WHERE p.id='". $idx ."'
350 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
354 WHERE p.id='". $idx ."'
358 if($result = $this->db->db_query($query_str)) {
360 $row = $this->db->db_fetch_object($result);
362 if($this->dbver < 9) {
363 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
372 } // get_photo_details
375 * returns aligned photo names
377 * this function returns aligned (length) names for
378 * an specific photo. If the length of the name exceeds
379 * $limit the name will be shrinked (...)
381 public function getPhotoName($idx, $limit = 0)
383 if($details = $this->get_photo_details($idx)) {
384 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
385 $name = $this->shrink_text($long_name, $limit);
395 * shrink text according provided limit
397 * If the length of the name exceeds $limit the
398 * text will be shortend and some content in between
399 * will be replaced with "..."
401 private function shrink_text($text, $limit)
403 if($limit != 0 && strlen($text) > $limit) {
404 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
412 * translate f-spoth photo path
414 * as the full-qualified path recorded in the f-spot database
415 * is usally not the same as on the webserver, this function
416 * will replace the path with that one specified in the cfg
418 public function translate_path($path, $width = 0)
420 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
425 * control HTML ouput for a single photo
427 * this function provides all the necessary information
428 * for the single photo template.
430 public function showPhoto($photo)
432 /* get all photos from the current photo selection */
433 $all_photos = $this->getPhotoSelection();
434 $count = count($all_photos);
436 for($i = 0; $i < $count; $i++) {
438 // $get_next will be set, when the photo which has to
439 // be displayed has been found - this means that the
440 // next available is in fact the NEXT image (for the
442 if(isset($get_next)) {
443 $next_img = $all_photos[$i];
447 /* the next photo is our NEXT photo */
448 if($all_photos[$i] == $photo) {
452 $previous_img = $all_photos[$i];
455 if($photo == $all_photos[$i]) {
460 $details = $this->get_photo_details($photo);
467 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
468 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
470 if(!file_exists($orig_path)) {
471 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
475 if(!is_readable($orig_path)) {
476 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
480 /* If the thumbnail doesn't exist yet, try to create it */
481 if(!file_exists($thumb_path)) {
482 $this->gen_thumb($photo, true);
483 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
486 /* get EXIF information if JPEG */
487 if($details['mime'] == "image/jpeg") {
488 $meta = $this->get_meta_informations($orig_path);
491 /* If EXIF data are available, use them */
492 if(isset($meta['ExifImageWidth'])) {
493 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
495 $info = getimagesize($orig_path);
496 $meta_res = $info[0] ."x". $info[1];
499 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
500 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
501 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
503 $extern_link = "index.php?mode=showp&id=". $photo;
504 $current_tags = $this->getCurrentTags();
505 if($current_tags != "") {
506 $extern_link.= "&tags=". $current_tags;
508 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
509 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
512 $this->tmpl->assign('extern_link', $extern_link);
514 if(!file_exists($thumb_path)) {
515 $this->_error("Can't open file ". $thumb_path ."\n");
519 $info = getimagesize($thumb_path);
521 $this->tmpl->assign('description', $details['description']);
522 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
524 $this->tmpl->assign('width', $info[0]);
525 $this->tmpl->assign('height', $info[1]);
526 $this->tmpl->assign('ExifMadeOn', $meta_date);
527 $this->tmpl->assign('ExifMadeWith', $meta_make);
528 $this->tmpl->assign('ExifOrigResolution', $meta_res);
529 $this->tmpl->assign('ExifFileSize', $meta_size);
531 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
532 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
533 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
535 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
536 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
537 $this->tmpl->assign('current_img', $photo);
540 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
541 $this->tmpl->assign('prev_img', $previous_img);
545 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
546 $this->tmpl->assign('next_img', $next_img);
548 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
549 $this->tmpl->assign('photo_number', $i);
550 $this->tmpl->assign('photo_count', count($all_photos));
552 $this->tmpl->show("single_photo.tpl");
557 * all available tags and tag cloud
559 * this function outputs all available tags (time ordered)
560 * and in addition output them as tag cloud (tags which have
561 * many photos will appears more then others)
563 public function getAvailableTags()
565 /* retrive tags from database */
570 $result = $this->db->db_query("
571 SELECT tag_id as id, count(tag_id) as quantity
581 while($row = $this->db->db_fetch_object($result)) {
582 $tags[$row['id']] = $row['quantity'];
585 // change these font sizes if you will
586 $max_size = 125; // max font size in %
587 $min_size = 75; // min font size in %
590 $max_sat = hexdec('cc');
591 $min_sat = hexdec('44');
593 // get the largest and smallest array values
594 $max_qty = max(array_values($tags));
595 $min_qty = min(array_values($tags));
597 // find the range of values
598 $spread = $max_qty - $min_qty;
599 if (0 == $spread) { // we don't want to divide by zero
603 // determine the font-size increment
604 // this is the increase per tag quantity (times used)
605 $step = ($max_size - $min_size)/($spread);
606 $step_sat = ($max_sat - $min_sat)/($spread);
608 // loop through our tag array
609 foreach ($tags as $key => $value) {
611 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
614 // calculate CSS font-size
615 // find the $value in excess of $min_qty
616 // multiply by the font-size increment ($size)
617 // and add the $min_size set above
618 $size = $min_size + (($value - $min_qty) * $step);
619 // uncomment if you want sizes in whole %:
622 $color = $min_sat + ($value - $min_qty) * $step_sat;
628 if(isset($this->tags[$key])) {
629 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
634 $output = substr($output, 0, strlen($output)-2);
637 } // getAvailableTags()
640 * output all selected tags
642 * this function output all tags which have been selected
643 * by the user. the selected tags are stored in the
644 * session-variable $_SESSION['selected_tags']
646 public function getSelectedTags()
648 /* retrive tags from database */
653 foreach($this->avail_tags as $tag)
655 // return all selected tags
656 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
657 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
662 $output = substr($output, 0, strlen($output)-2);
666 return "no tags selected";
669 } // getSelectedTags()
672 * add tag to users session variable
674 * this function will add the specified to users current
675 * tag selection. if a date search has been made before
676 * it will be now cleared
678 public function addTag($tag)
680 if(!isset($_SESSION['selected_tags']))
681 $_SESSION['selected_tags'] = Array();
683 if(isset($_SESSION['searchfor_tag']))
684 unset($_SESSION['searchfor_tag']);
686 if(!in_array($tag, $_SESSION['selected_tags']))
687 array_push($_SESSION['selected_tags'], $tag);
695 * remove tag to users session variable
697 * this function removes the specified tag from
698 * users current tag selection
700 public function delTag($tag)
702 if(isset($_SESSION['searchfor_tag']))
703 unset($_SESSION['searchfor_tag']);
705 if(isset($_SESSION['selected_tags'])) {
706 $key = array_search($tag, $_SESSION['selected_tags']);
707 unset($_SESSION['selected_tags'][$key]);
708 sort($_SESSION['selected_tags']);
716 * reset tag selection
718 * if there is any tag selection, it will be
721 public function resetTags()
723 if(isset($_SESSION['selected_tags']))
724 unset($_SESSION['selected_tags']);
729 * returns the value for the autocomplet tag-search
731 public function get_xml_tag_list()
733 if(!isset($_GET['search']) || !is_string($_GET['search']))
734 $_GET['search'] = '';
739 /* retrive tags from database */
742 $matched_tags = Array();
744 header("Content-Type: text/xml");
746 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
747 $string.= "<results>\n";
749 foreach($this->avail_tags as $tag)
751 if(!empty($_GET['search']) &&
752 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
753 count($matched_tags) < $length) {
755 $count = $this->get_num_photos($tag);
758 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
761 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
767 /* if we have collected enough items, break out */
768 if(count($matched_tags) >= $length)
772 $string.= "</results>\n";
776 } // get_xml_tag_list()
782 * if a specific photo was requested (external link)
783 * unset the session variable now
785 public function resetPhotoView()
787 if(isset($_SESSION['current_photo']))
788 unset($_SESSION['current_photo']);
790 } // resetPhotoView();
795 * if any tag search has taken place, reset it now
797 public function resetTagSearch()
799 if(isset($_SESSION['searchfor_tag']))
800 unset($_SESSION['searchfor_tag']);
802 } // resetTagSearch()
807 * if any name search has taken place, reset it now
809 public function resetNameSearch()
811 if(isset($_SESSION['searchfor_name']))
812 unset($_SESSION['searchfor_name']);
814 } // resetNameSearch()
819 * if any date search has taken place, reset
822 public function resetDateSearch()
824 if(isset($_SESSION['from_date']))
825 unset($_SESSION['from_date']);
826 if(isset($_SESSION['to_date']))
827 unset($_SESSION['to_date']);
829 } // resetDateSearch();
832 * return all photo according selection
834 * this function returns all photos based on
835 * the tag-selection, tag- or date-search.
836 * the tag-search also has to take care of AND
837 * and OR conjunctions
839 public function getPhotoSelection()
841 $matched_photos = Array();
842 $additional_where_cond = "";
844 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
845 $from_date = $_SESSION['from_date'];
846 $to_date = $_SESSION['to_date'];
847 $additional_where_cond.= "
848 p.time>='". $from_date ."'
850 p.time<='". $to_date ."'
854 if(isset($_SESSION['searchfor_name'])) {
855 if($this->dbver < 9) {
856 $additional_where_cond.= "
858 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
860 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
865 $additional_where_cond.= "
867 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
869 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
875 if(isset($_SESSION['sort_order'])) {
876 $order_str = $this->get_sort_order();
879 /* return a search result */
880 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
882 SELECT DISTINCT pt1.photo_id
884 INNER JOIN photo_tags pt2
885 ON pt1.photo_id=pt2.photo_id
892 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
894 if(isset($additional_where_cond) && !empty($additional_where_cond))
895 $query_str.= "AND ". $additional_where_cond ." ";
897 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
898 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
901 if(isset($order_str))
902 $query_str.= $order_str;
904 $result = $this->db->db_query($query_str);
905 while($row = $this->db->db_fetch_object($result)) {
906 array_push($matched_photos, $row['photo_id']);
908 return $matched_photos;
911 /* return according the selected tags */
912 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
914 foreach($_SESSION['selected_tags'] as $tag)
915 $selected.= $tag .",";
916 $selected = substr($selected, 0, strlen($selected)-1);
918 /* photo has to match at least on of the selected tags */
919 if($_SESSION['tag_condition'] == 'or') {
921 SELECT DISTINCT pt1.photo_id
923 INNER JOIN photo_tags pt2
924 ON pt1.photo_id=pt2.photo_id
929 WHERE pt1.tag_id IN (". $selected .")
931 if(isset($additional_where_cond) && !empty($additional_where_cond))
932 $query_str.= "AND ". $additional_where_cond ." ";
934 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
935 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
938 if(isset($order_str))
939 $query_str.= $order_str;
941 /* photo has to match all selected tags */
942 elseif($_SESSION['tag_condition'] == 'and') {
944 if(count($_SESSION['selected_tags']) >= 32) {
945 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
946 print "evaluate your tag selection. Please remove some tags from your selection.\n";
950 /* Join together a table looking like
952 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
954 so the query can quickly return all images matching the
955 selected tags in an AND condition
960 SELECT DISTINCT pt1.photo_id
964 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
971 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
973 INNER JOIN photo_tags pt". ($i+2) ."
974 ON pt1.photo_id=pt". ($i+2) .".photo_id
981 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
982 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
984 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
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 t.name IN ('".implode("','",$this->cfg->show_tags). "')";
994 if(isset($order_str))
995 $query_str.= $order_str;
999 $result = $this->db->db_query($query_str);
1000 while($row = $this->db->db_fetch_object($result)) {
1001 array_push($matched_photos, $row['photo_id']);
1003 return $matched_photos;
1006 /* return all available photos */
1008 SELECT DISTINCT p.id
1010 LEFT JOIN photo_tags pt
1016 if(isset($additional_where_cond) && !empty($additional_where_cond))
1017 $query_str.= "WHERE ". $additional_where_cond ." ";
1019 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1020 if(isset($additional_where_cond) && !empty($additional_where_cond))
1021 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1023 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1026 if(isset($order_str))
1027 $query_str.= $order_str;
1029 $result = $this->db->db_query($query_str);
1030 while($row = $this->db->db_fetch_object($result)) {
1031 array_push($matched_photos, $row['id']);
1033 return $matched_photos;
1035 } // getPhotoSelection()
1038 * control HTML ouput for photo index
1040 * this function provides all the necessary information
1041 * for the photo index template.
1043 public function showPhotoIndex()
1045 $photos = $this->getPhotoSelection();
1047 $count = count($photos);
1049 /* if all thumbnails should be shown on one page */
1050 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1054 /* thumbnails should be splitted up in several pages */
1055 elseif($this->cfg->thumbs_per_page > 0) {
1057 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1061 $begin_with = $_SESSION['begin_with'];
1064 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1068 $images[$thumbs] = Array();
1069 $img_height[$thumbs] = Array();
1070 $img_width[$thumbs] = Array();
1071 $img_id[$thumbs] = Array();
1072 $img_name[$thumbs] = Array();
1073 $img_fullname[$thumbs] = Array();
1074 $img_title = Array();
1076 for($i = $begin_with; $i < $end_with; $i++) {
1078 if(isset($photos[$i])) {
1080 $images[$thumbs] = $photos[$i];
1081 $img_id[$thumbs] = $i;
1082 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1083 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1084 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1086 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1088 if(file_exists($thumb_path)) {
1089 $info = getimagesize($thumb_path);
1090 $img_width[$thumbs] = $info[0];
1091 $img_height[$thumbs] = $info[1];
1097 // +1 for for smarty's selection iteration
1100 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1101 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1103 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1104 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1105 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1108 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1109 $this->tmpl->assign('tag_result', 1);
1112 /* do we have to display the page selector ? */
1113 if($this->cfg->thumbs_per_page != 0) {
1117 /* calculate the page switchers */
1118 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1119 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1121 if($begin_with != 0)
1122 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1123 if($end_with < $count)
1124 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1126 $photo_per_page = $this->cfg->thumbs_per_page;
1127 $last_page = ceil($count / $photo_per_page);
1129 /* get the current selected page */
1130 if($begin_with == 0) {
1134 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1141 for($i = 1; $i <= $last_page; $i++) {
1143 if($current_page == $i)
1144 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1145 elseif($current_page-1 == $i || $current_page+1 == $i)
1146 $style = "style=\"font-size: 105%;\"";
1147 elseif(($current_page-5 >= $i) && ($i != 1) ||
1148 ($current_page+5 <= $i) && ($i != $last_page))
1149 $style = "style=\"font-size: 75%;\"";
1153 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1156 $select.= ">". $i ."</a> ";
1158 // until 9 pages we show the selector from 1-9
1159 if($last_page <= 9) {
1160 $page_select.= $select;
1163 if($i == 1 /* first page */ ||
1164 $i == $last_page /* last page */ ||
1165 $i == $current_page /* current page */ ||
1166 $i == ceil($last_page * 0.25) /* first quater */ ||
1167 $i == ceil($last_page * 0.5) /* half */ ||
1168 $i == ceil($last_page * 0.75) /* third quater */ ||
1169 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1170 (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 */ ||
1171 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1172 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1174 $page_select.= $select;
1182 $page_select.= "......... ";
1187 /* only show the page selector if we have more then one page */
1189 $this->tmpl->assign('page_selector', $page_select);
1193 $current_tags = $this->getCurrentTags();
1194 $extern_link = "index.php?mode=showpi";
1195 $rss_link = "index.php?mode=rss";
1196 if($current_tags != "") {
1197 $extern_link.= "&tags=". $current_tags;
1198 $rss_link.= "&tags=". $current_tags;
1200 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1201 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1202 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1205 $export_link = "index.php?mode=export";
1206 $slideshow_link = "index.php?mode=slideshow";
1208 $this->tmpl->assign('extern_link', $extern_link);
1209 $this->tmpl->assign('slideshow_link', $slideshow_link);
1210 $this->tmpl->assign('export_link', $export_link);
1211 $this->tmpl->assign('rss_link', $rss_link);
1212 $this->tmpl->assign('count', $count);
1213 $this->tmpl->assign('width', $this->cfg->thumb_width);
1214 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1215 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1216 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1217 $this->tmpl->assign('images', $images);
1218 $this->tmpl->assign('img_width', $img_width);
1219 $this->tmpl->assign('img_height', $img_height);
1220 $this->tmpl->assign('img_id', $img_id);
1221 $this->tmpl->assign('img_name', $img_name);
1222 $this->tmpl->assign('img_fullname', $img_fullname);
1223 $this->tmpl->assign('img_title', $img_title);
1224 $this->tmpl->assign('thumbs', $thumbs);
1226 $this->tmpl->show("photo_index.tpl");
1228 /* if we are returning to photo index from an photo-view,
1229 scroll the window to the last shown photo-thumbnail.
1230 after this, unset the last_photo session variable.
1232 if(isset($_SESSION['last_photo'])) {
1233 print "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1234 unset($_SESSION['last_photo']);
1237 } // showPhotoIndex()
1240 * show credit template
1242 public function showCredits()
1244 $this->tmpl->assign('version', $this->cfg->version);
1245 $this->tmpl->assign('product', $this->cfg->product);
1246 $this->tmpl->assign('db_version', $this->dbver);
1247 $this->tmpl->show("credits.tpl");
1252 * create_thumbnails for the requested width
1254 * this function creates image thumbnails of $orig_image
1255 * stored as $thumb_image. It will check if the image is
1256 * in a supported format, if necessary rotate the image
1257 * (based on EXIF orientation meta headers) and re-sizing.
1259 public function create_thumbnail($orig_image, $thumb_image, $width)
1261 if(!file_exists($orig_image)) {
1265 $details = getimagesize($orig_image);
1267 /* check if original photo is a support image type */
1268 if(!$this->checkifImageSupported($details['mime']))
1271 switch($details['mime']) {
1275 $meta = $this->get_meta_informations($orig_image);
1281 switch($meta['Orientation']) {
1282 case 1: /* top, left */
1283 /* nothing to do */ break;
1284 case 2: /* top, right */
1285 $rotate = 0; $flip_hori = true; break;
1286 case 3: /* bottom, left */
1287 $rotate = 180; break;
1288 case 4: /* bottom, right */
1289 $flip_vert = true; break;
1290 case 5: /* left side, top */
1291 $rotate = 90; $flip_vert = true; break;
1292 case 6: /* right side, top */
1293 $rotate = 90; break;
1294 case 7: /* left side, bottom */
1295 $rotate = 270; $flip_vert = true; break;
1296 case 8: /* right side, bottom */
1297 $rotate = 270; break;
1300 $src_img = @imagecreatefromjpeg($orig_image);
1305 $src_img = @imagecreatefrompng($orig_image);
1311 print "Can't load image from ". $orig_image ."\n";
1315 /* grabs the height and width */
1316 $cur_width = imagesx($src_img);
1317 $cur_height = imagesy($src_img);
1319 // If requested width is more then the actual image width,
1320 // do not generate a thumbnail, instead safe the original
1321 // as thumbnail but with lower quality. But if the image
1322 // is to heigh too, then we still have to resize it.
1323 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1324 $result = imagejpeg($src_img, $thumb_image, 75);
1325 imagedestroy($src_img);
1329 // If the image will be rotate because EXIF orientation said so
1330 // 'virtually rotate' the image for further calculations
1331 if($rotate == 90 || $rotate == 270) {
1333 $cur_width = $cur_height;
1337 /* calculates aspect ratio */
1338 $aspect_ratio = $cur_height / $cur_width;
1341 if($aspect_ratio < 1) {
1343 $new_h = abs($new_w * $aspect_ratio);
1345 /* 'virtually' rotate the image and calculate it's ratio */
1346 $tmp_w = $cur_height;
1347 $tmp_h = $cur_width;
1348 /* now get the ratio from the 'rotated' image */
1349 $tmp_ratio = $tmp_h/$tmp_w;
1350 /* now calculate the new dimensions */
1352 $tmp_h = abs($tmp_w * $tmp_ratio);
1354 // now that we know, how high they photo should be, if it
1355 // gets rotated, use this high to scale the image
1357 $new_w = abs($new_h / $aspect_ratio);
1359 // If the image will be rotate because EXIF orientation said so
1360 // now 'virtually rotate' back the image for the image manipulation
1361 if($rotate == 90 || $rotate == 270) {
1368 /* creates new image of that size */
1369 $dst_img = imagecreatetruecolor($new_w, $new_h);
1371 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1373 /* copies resized portion of original image into new image */
1374 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1376 /* needs the image to be flipped horizontal? */
1378 $this->_debug("(FLIP)");
1379 $dst_img = $this->flipImage($dst_img, 'hori');
1381 /* needs the image to be flipped vertical? */
1383 $this->_debug("(FLIP)");
1384 $dst_img = $this->flipImage($dst_img, 'vert');
1388 $this->_debug("(ROTATE)");
1389 $dst_img = $this->rotateImage($dst_img, $rotate);
1392 /* write down new generated file */
1393 $result = imagejpeg($dst_img, $thumb_image, 75);
1395 /* free your mind */
1396 imagedestroy($dst_img);
1397 imagedestroy($src_img);
1399 if($result === false) {
1400 print "Can't write thumbnail ". $thumb_image ."\n";
1406 } // create_thumbnail()
1409 * return all exif meta data from the file
1411 public function get_meta_informations($file)
1413 return exif_read_data($file);
1415 } // get_meta_informations()
1418 * create phpfspot own sqlite database
1420 * this function creates phpfspots own sqlite database
1421 * if it does not exist yet. this own is used to store
1422 * some necessary informations (md5 sum's, ...).
1424 public function check_config_table()
1426 // if the config table doesn't exist yet, create it
1427 if(!$this->cfg_db->db_check_table_exists("images")) {
1428 $this->cfg_db->db_exec("
1429 CREATE TABLE images (
1430 img_idx int primary key,
1436 } // check_config_table
1439 * Generates a thumbnail from photo idx
1441 * This function will generate JPEG thumbnails from provided F-Spot photo
1444 * 1. Check if all thumbnail generations (width) are already in place and
1446 * 2. Check if the md5sum of the original file has changed
1447 * 3. Generate the thumbnails if needed
1449 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1453 $resolutions = Array(
1454 $this->cfg->thumb_width,
1455 $this->cfg->photo_width,
1456 $this->cfg->mini_width,
1459 /* get details from F-Spot's database */
1460 $details = $this->get_photo_details($idx);
1462 /* calculate file MD5 sum */
1463 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1465 if(!file_exists($full_path)) {
1466 $this->_error("File ". $full_path ." does not exist\n");
1470 if(!is_readable($full_path)) {
1471 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1475 $file_md5 = md5_file($full_path);
1477 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1481 foreach($resolutions as $resolution) {
1483 $generate_it = false;
1485 $thumb_sub_path = substr($file_md5, 0, 2);
1486 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1488 /* if thumbnail-subdirectory does not exist yet, create it */
1489 if(!file_exists(dirname($thumb_path))) {
1490 mkdir(dirname($thumb_path), 0755);
1493 /* if the thumbnail file doesn't exist, create it */
1494 if(!file_exists($thumb_path)) {
1495 $generate_it = true;
1497 /* if the file hasn't changed there is no need to regen the thumb */
1498 elseif($file_md5 != $this->getMD5($idx) || $force) {
1499 $generate_it = true;
1502 if($generate_it || $overwrite) {
1504 $this->_debug(" ". $resolution ."px");
1505 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1513 $this->_debug(" already exist");
1516 /* set the new/changed MD5 sum for the current photo */
1518 $this->setMD5($idx, $file_md5);
1521 $this->_debug("\n");
1526 * returns stored md5 sum for a specific photo
1528 * this function queries the phpfspot database for a
1529 * stored MD5 checksum of the specified photo
1531 public function getMD5($idx)
1533 $result = $this->cfg_db->db_query("
1536 WHERE img_idx='". $idx ."'
1542 $img = $this->cfg_db->db_fetch_object($result);
1543 return $img['img_md5'];
1548 * set MD5 sum for the specific photo
1550 private function setMD5($idx, $md5)
1552 $result = $this->cfg_db->db_exec("
1553 REPLACE INTO images (img_idx, img_md5)
1554 VALUES ('". $idx ."', '". $md5 ."')
1560 * store current tag condition
1562 * this function stores the current tag condition
1563 * (AND or OR) in the users session variables
1565 public function setTagCondition($mode)
1567 $_SESSION['tag_condition'] = $mode;
1571 } // setTagCondition()
1574 * invoke tag & date search
1576 * this function will return all matching tags and store
1577 * them in the session variable selected_tags. furthermore
1578 * it also handles the date search.
1579 * getPhotoSelection() will then only return the matching
1582 public function startSearch()
1584 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1585 $from = $_POST['from'];
1587 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
1591 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
1592 $searchfor_tag = $_POST['for_tag'];
1593 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
1596 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
1597 $searchfor_name = $_POST['for_name'];
1598 $_SESSION['searchfor_name'] = $_POST['for_name'];
1603 if(isset($from) && !empty($from))
1604 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1606 unset($_SESSION['from_date']);
1608 if(isset($to) && !empty($to))
1609 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1611 unset($_SESSION['to_date']);
1613 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
1614 /* new search, reset the current selected tags */
1615 $_SESSION['selected_tags'] = Array();
1616 foreach($this->avail_tags as $tag) {
1617 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
1618 array_push($_SESSION['selected_tags'], $tag);
1627 * updates sort order in session variable
1629 * this function is invoked by RPC and will sort the requested
1630 * sort order in the session variable.
1632 public function updateSortOrder($order)
1634 if(isset($this->sort_orders[$order])) {
1635 $_SESSION['sort_order'] = $order;
1639 return "unkown error";
1641 } // updateSortOrder()
1646 * this function rotates the image according the
1649 private function rotateImage($img, $degrees)
1651 if(function_exists("imagerotate")) {
1652 $img = imagerotate($img, $degrees, 0);
1654 function imagerotate($src_img, $angle)
1656 $src_x = imagesx($src_img);
1657 $src_y = imagesy($src_img);
1658 if ($angle == 180) {
1662 elseif ($src_x <= $src_y) {
1666 elseif ($src_x >= $src_y) {
1671 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1672 imagealphablending($rotate, false);
1677 for ($y = 0; $y < ($src_y); $y++) {
1678 for ($x = 0; $x < ($src_x); $x++) {
1679 $color = imagecolorat($src_img, $x, $y);
1680 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1686 for ($y = 0; $y < ($src_y); $y++) {
1687 for ($x = 0; $x < ($src_x); $x++) {
1688 $color = imagecolorat($src_img, $x, $y);
1689 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1695 for ($y = 0; $y < ($src_y); $y++) {
1696 for ($x = 0; $x < ($src_x); $x++) {
1697 $color = imagecolorat($src_img, $x, $y);
1698 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1712 $img = imagerotate($img, $degrees);
1721 * returns flipped image
1723 * this function will return an either horizontal or
1724 * vertical flipped truecolor image.
1726 private function flipImage($image, $mode)
1728 $w = imagesx($image);
1729 $h = imagesy($image);
1730 $flipped = imagecreatetruecolor($w, $h);
1734 for ($y = 0; $y < $h; $y++) {
1735 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1739 for ($x = 0; $x < $w; $x++) {
1740 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1750 * return all assigned tags for the specified photo
1752 private function get_photo_tags($idx)
1754 $result = $this->db->db_query("
1757 INNER JOIN photo_tags pt
1759 WHERE pt.photo_id='". $idx ."'
1764 while($row = $this->db->db_fetch_object($result))
1765 $tags[$row['id']] = $row['name'];
1769 } // get_photo_tags()
1772 * create on-the-fly images with text within
1774 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1776 if (strlen($color) != 6)
1779 $int = hexdec($color);
1780 $h = imagefontheight($font);
1781 $fw = imagefontwidth($font);
1782 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1783 $lines = count($txt);
1784 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1785 $bg = imagecolorallocate($im, 255, 255, 255);
1786 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1789 foreach ($txt as $text) {
1790 $x = (($w - ($fw * strlen($text))) / 2);
1791 imagestring($im, $font, $x, $y, $text, $color);
1792 $y += ($h + $space);
1795 Header("Content-type: image/png");
1798 } // showTextImage()
1801 * check if all requirements are met
1803 private function check_requirements()
1805 if(!function_exists("imagecreatefromjpeg")) {
1806 print "PHP GD library extension is missing<br />\n";
1810 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1811 print "PHP SQLite3 library extension is missing<br />\n";
1815 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1816 ini_set('track_errors', 1);
1817 @include_once 'HTML/AJAX/Server.php';
1818 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1819 print "PEAR HTML_AJAX package is missing<br />\n";
1822 @include_once 'Calendar/Calendar.php';
1823 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1824 print "PEAR Calendar package is missing<br />\n";
1827 @include_once 'Console/Getopt.php';
1828 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1829 print "PEAR Console_Getopt package is missing<br />\n";
1832 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
1833 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1834 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
1837 ini_restore('track_errors');
1844 } // check_requirements()
1846 private function _debug($text)
1848 if($this->fromcmd) {
1855 * check if specified MIME type is supported
1857 public function checkifImageSupported($mime)
1859 if(in_array($mime, Array("image/jpeg", "image/png")))
1864 } // checkifImageSupported()
1866 public function _error($text)
1868 switch($this->cfg->logging) {
1871 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1872 print $text ."<br />\n";
1878 error_log($text, 3, $his->cfg->log_file);
1882 $this->runtime_error = true;
1887 * output calendard input fields
1889 private function get_calendar($mode)
1891 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1892 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1893 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1895 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1896 if(!isset($_SESSION[$mode .'_date']))
1897 $output.= " disabled=\"disabled\"";
1899 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1900 if(!isset($_SESSION[$mode .'_date']))
1901 $output.= " disabled=\"disabled\"";
1903 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1904 if(!isset($_SESSION[$mode .'_date']))
1905 $output.= " disabled=\"disabled\"";
1913 * output calendar matrix
1915 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1917 if (!isset($year)) $year = date('Y');
1918 if (!isset($month)) $month = date('m');
1919 if (!isset($day)) $day = date('d');
1924 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1925 require_once CALENDAR_ROOT.'Day.php';
1928 $month = new Calendar_Month_Weekdays($year,$month);
1931 $prevStamp = $month->prevMonth(true);
1932 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1933 $nextStamp = $month->nextMonth(true);
1934 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1936 $selectedDays = array (
1937 new Calendar_Day($year,$month,$day),
1938 new Calendar_Day($year,12,25),
1941 // Build the days in the month
1942 $month->build($selectedDays);
1944 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1945 $this->tmpl->assign('prev_month', $prev);
1946 $this->tmpl->assign('next_month', $next);
1948 while ( $day = $month->fetch() ) {
1950 if(!isset($matrix[$rows]))
1951 $matrix[$rows] = Array();
1955 $dayStamp = $day->thisDay(true);
1956 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1958 // isFirst() to find start of week
1959 if ( $day->isFirst() )
1962 if ( $day->isSelected() ) {
1963 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1964 } else if ( $day->isEmpty() ) {
1965 $string.= "<td> </td>\n";
1967 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1970 // isLast() to find end of week
1971 if ( $day->isLast() )
1972 $string.= "</tr>\n";
1974 $matrix[$rows][$cols] = $string;
1984 $this->tmpl->assign('matrix', $matrix);
1985 $this->tmpl->assign('rows', $rows);
1986 $this->tmpl->show("calendar.tpl");
1988 } // get_calendar_matrix()
1991 * output export page
1993 public function getExport($mode)
1995 $pictures = $this->getPhotoSelection();
1996 $current_tags = $this->getCurrentTags();
1998 foreach($pictures as $picture) {
2000 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2001 if($current_tags != "") {
2002 $orig_url.= "&tags=". $current_tags;
2004 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2005 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2008 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2013 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2014 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2018 // "[%pictureurl% %thumbnailurl%]"
2019 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2022 case 'MoinMoinList':
2023 // " * [%pictureurl% %thumbnailurl%]"
2024 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2035 public function getRSSFeed()
2037 Header("Content-type: text/xml; charset=utf-8");
2038 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2041 xmlns:media="http://search.yahoo.com/mrss/"
2042 xmlns:dc="http://purl.org/dc/elements/1.1/"
2045 <title>phpfspot</title>
2046 <description>phpfspot RSS feed</description>
2047 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2048 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2049 <generator>phpfspot</generator>
2052 $pictures = $this->getPhotoSelection();
2053 $current_tags = $this->getCurrentTags();
2055 foreach($pictures as $picture) {
2057 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2058 if($current_tags != "") {
2059 $orig_url.= "&tags=". $current_tags;
2061 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2062 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2065 $details = $this->get_photo_details($picture);
2067 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2068 $thumb_html = htmlspecialchars("
2069 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2071 ". $details['description']);
2073 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2075 /* get EXIF information if JPEG */
2076 if($details['mime'] == "image/jpeg") {
2077 $meta = $this->get_meta_informations($orig_path);
2080 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2084 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2085 <link><?php print htmlspecialchars($orig_url); ?></link>
2086 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2087 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2089 <?php print $thumb_html; ?>
2091 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2106 * return all selected tags as one string
2108 private function getCurrentTags()
2111 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2112 foreach($_SESSION['selected_tags'] as $tag)
2113 $current_tags.= $tag .",";
2114 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2116 return $current_tags;
2118 } // getCurrentTags()
2121 * return the current photo
2123 public function getCurrentPhoto()
2125 if(isset($_SESSION['current_photo'])) {
2126 print $_SESSION['current_photo'];
2128 } // getCurrentPhoto()
2131 * tells the client browser what to do
2133 * this function is getting called via AJAX by the
2134 * client browsers. it will tell them what they have
2135 * to do next. This is necessary for directly jumping
2136 * into photo index or single photo view when the are
2137 * requested with specific URLs
2139 public function whatToDo()
2141 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2142 return "show_photo";
2144 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2145 return "showpi_tags";
2147 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2151 return "nothing special";
2156 * return the current process-user
2158 private function getuid()
2160 if($uid = posix_getuid()) {
2161 if($user = posix_getpwuid($uid)) {
2162 return $user['name'];
2171 * returns a select-dropdown box to select photo index sort parameters
2173 public function smarty_sort_select_list($params, &$smarty)
2177 foreach($this->sort_orders as $key => $value) {
2178 $output.= "<option value=\"". $key ."\"";
2179 if($key == $_SESSION['sort_order']) {
2180 $output.= " selected=\"selected\"";
2182 $output.= ">". $value ."</option>";
2187 } // smarty_sort_select_list()
2190 * returns the currently selected sort order
2192 private function get_sort_order()
2194 switch($_SESSION['sort_order']) {
2196 return " ORDER BY p.time ASC";
2199 return " ORDER BY p.time DESC";
2202 if($this->dbver < 9) {
2203 return " ORDER BY p.name ASC";
2206 return " ORDER BY basename(p.uri) ASC";
2210 if($this->dbver < 9) {
2211 return " ORDER BY p.name DESC";
2214 return " ORDER BY basename(p.uri) DESC";
2218 return " ORDER BY t.name ASC ,p.time ASC";
2221 return " ORDER BY t.name DESC ,p.time ASC";
2225 } // get_sort_order()
2228 * return the next to be shown slide show image
2230 * this function returns the URL of the next image
2231 * in the slideshow sequence.
2233 public function getNextSlideShowImage()
2235 $all_photos = $this->getPhotoSelection();
2237 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2238 $_SESSION['slideshow_img'] = 0;
2240 $_SESSION['slideshow_img']++;
2242 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2244 } // getNextSlideShowImage()
2247 * return the previous to be shown slide show image
2249 * this function returns the URL of the previous image
2250 * in the slideshow sequence.
2252 public function getPrevSlideShowImage()
2254 $all_photos = $this->getPhotoSelection();
2256 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2257 $_SESSION['slideshow_img'] = 0;
2259 $_SESSION['slideshow_img']--;
2261 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2263 } // getPrevSlideShowImage()
2265 public function resetSlideShow()
2267 if(isset($_SESSION['slideshow_img']))
2268 unset($_SESSION['slideshow_img']);
2270 } // resetSlideShow()
2275 * this function will get all photos from the fspot
2276 * database and randomly return ONE entry
2278 * saddly there is yet no sqlite3 function which returns
2279 * the bulk result in array, so we have to fill up our
2282 public function get_random_photo()
2286 $result = $this->db->db_query("
2291 while($row = $this->db->db_fetch_object($result)) {
2292 array_push($all, $row['id']);
2295 return $all[array_rand($all)];
2297 } // get_random_photo()
2300 * validates provided date
2302 * this function validates if the provided date
2303 * contains a valid date and will return true
2306 public function isValidDate($date_str)
2308 $timestamp = strtotime($date_str);
2310 if(is_numeric($timestamp))
2318 * timestamp to string conversion
2320 private function ts2str($timestamp)
2322 return strftime("%Y-%m-%d", $timestamp);
2325 private function extractTags($tags_str)
2327 $not_validated = split(',', $_GET['tags']);
2328 $validated = array();
2330 foreach($not_validated as $tag) {
2331 if(is_numeric($tag))
2332 array_push($validated, $tag);
2340 * returns the full path to a thumbnail
2342 public function get_thumb_path($width, $photo)
2344 $md5 = $this->getMD5($photo);
2345 $sub_path = substr($md5, 0, 2);
2346 return $this->cfg->thumb_path
2354 } // get_thumb_path()
2357 * returns server's virtual host name
2359 private function get_server_name()
2361 return $_SERVER['SERVER_NAME'];
2362 } // get_server_name()
2365 * returns type of webprotocol which is
2368 private function get_web_protocol()
2370 if(!isset($_SERVER['HTTPS']))
2374 } // get_web_protocol()
2377 * return url to this phpfspot installation
2379 private function get_phpfspot_url()
2381 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2382 } // get_phpfspot_url()
2385 * returns the number of photos which are tagged with $tag_id
2387 public function get_num_photos($tag_id)
2389 if($result = $this->db->db_fetchSingleRow("
2390 SELECT count(*) as number
2393 tag_id LIKE '". $tag_id ."'")) {
2395 return $result['number'];
2401 } // get_num_photos()
2404 * check file exists and is readable
2406 * returns true, if everything is ok, otherwise false
2407 * if $silent is not set, this function will output and
2410 private function check_readable($file, $silent = null)
2412 if(!file_exists($file)) {
2414 print "File \"". $file ."\" does not exist.\n";
2418 if(!is_readable($file)) {
2420 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2426 } // check_readable()
2429 * check if all needed indices are present
2431 * this function checks, if some needed indices are already
2432 * present, or if not, create them on the fly. they are
2433 * necessary to speed up some queries like that one look for
2434 * all tags, when show_tags is specified in the configuration.
2436 private function checkDbIndices()
2438 $result = $this->db->db_exec("
2439 CREATE INDEX IF NOT EXISTS
2446 } // checkDbIndices()
2449 * retrive F-Spot database version
2451 * this function will return the F-Spot database version number
2452 * It is stored within the sqlite3 database in the table meta
2454 public function getFspotDBVersion()
2456 if($result = $this->db->db_fetchSingleRow("
2457 SELECT data as version
2460 name LIKE 'F-Spot Database Version'
2462 return $result['version'];
2466 } // getFspotDBVersion()
2469 * parse the provided URI and will returned the
2472 public function parse_uri($uri, $mode)
2474 if(($components = parse_url($uri)) !== false) {
2478 return basename($components['path']);
2481 return dirname($components['path']);
2484 return $components['path'];
2494 * validate config options
2496 * this function checks if all necessary configuration options are
2497 * specified and set.
2499 private function check_config_options()
2501 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
2502 $this->_error("Please set \$page_title in phpfspot_cfg");
2504 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
2505 $this->_error("Please set \$base_path in phpfspot_cfg");
2507 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
2508 $this->_error("Please set \$web_path in phpfspot_cfg");
2510 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
2511 $this->_error("Please set \$thumb_path in phpfspot_cfg");
2513 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
2514 $this->_error("Please set \$smarty_path in phpfspot_cfg");
2516 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
2517 $this->_error("Please set \$fspot_db in phpfspot_cfg");
2519 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
2520 $this->_error("Please set \$db_access in phpfspot_cfg");
2522 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
2523 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
2525 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
2526 $this->_error("Please set \$thumb_width in phpfspot_cfg");
2528 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
2529 $this->_error("Please set \$thumb_height in phpfspot_cfg");
2531 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
2532 $this->_error("Please set \$photo_width in phpfspot_cfg");
2534 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
2535 $this->_error("Please set \$mini_width in phpfspot_cfg");
2537 if(!isset($this->cfg->thumbs_per_page))
2538 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
2540 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
2541 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
2543 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
2544 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
2546 if(!isset($this->cfg->hide_tags))
2547 $this->_error("Please set \$hide_tags in phpfspot_cfg");
2549 if(!isset($this->cfg->theme_name))
2550 $this->_error("Please set \$theme_name in phpfspot_cfg");
2552 if(!isset($this->cfg->logging))
2553 $this->_error("Please set \$logging in phpfspot_cfg");
2555 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
2557 if(!isset($this->cfg->log_file))
2558 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
2560 if(!is_writeable($this->cfg->log_file))
2561 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
2565 /* check for pending slash on web_path */
2566 if(!preg_match("/\/$/", $this->cfg->web_path))
2567 $this->cfg->web_path.= "/";
2569 return $this->runtime_error;
2571 } // check_config_options()
2574 * cleanup phpfspot own database
2576 * When photos are getting delete from F-Spot, there will remain
2577 * remain some residues in phpfspot own database. This function
2578 * will try to wipe them out.
2580 public function cleanup_phpfspot_db()
2582 $to_delete = Array();
2584 $result = $this->cfg_db->db_query("
2587 ORDER BY img_idx ASC
2590 while($row = $this->cfg_db->db_fetch_object($result)) {
2591 if(!$this->db->db_fetchSingleRow("
2594 WHERE id='". $row['img_idx'] ."'")) {
2596 array_push($to_delete, $row['img_idx'], ',');
2600 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
2602 $this->cfg_db->db_exec("
2604 WHERE img_idx IN (". implode($to_delete) .")
2607 } // cleanup_phpfspot_db()
2610 * return first image of the page, the $current photo
2613 * this function is used to find out the first photo of the
2614 * current page, in which the $current photo lies. this is
2615 * used to display the correct photo, when calling showPhotoIndex()
2618 private function getCurrentPage($current, $max)
2620 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
2621 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
2622 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
2628 } // getCurrentPage()