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_width', $this->cfg->photo_width);
550 $this->tmpl->assign('photo_number', $i);
551 $this->tmpl->assign('photo_count', count($all_photos));
553 $this->tmpl->show("single_photo.tpl");
558 * all available tags and tag cloud
560 * this function outputs all available tags (time ordered)
561 * and in addition output them as tag cloud (tags which have
562 * many photos will appears more then others)
564 public function getAvailableTags()
566 /* retrive tags from database */
571 $result = $this->db->db_query("
572 SELECT tag_id as id, count(tag_id) as quantity
582 while($row = $this->db->db_fetch_object($result)) {
583 $tags[$row['id']] = $row['quantity'];
586 // change these font sizes if you will
587 $max_size = 125; // max font size in %
588 $min_size = 75; // min font size in %
591 $max_sat = hexdec('cc');
592 $min_sat = hexdec('44');
594 // get the largest and smallest array values
595 $max_qty = max(array_values($tags));
596 $min_qty = min(array_values($tags));
598 // find the range of values
599 $spread = $max_qty - $min_qty;
600 if (0 == $spread) { // we don't want to divide by zero
604 // determine the font-size increment
605 // this is the increase per tag quantity (times used)
606 $step = ($max_size - $min_size)/($spread);
607 $step_sat = ($max_sat - $min_sat)/($spread);
609 // loop through our tag array
610 foreach ($tags as $key => $value) {
612 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
615 // calculate CSS font-size
616 // find the $value in excess of $min_qty
617 // multiply by the font-size increment ($size)
618 // and add the $min_size set above
619 $size = $min_size + (($value - $min_qty) * $step);
620 // uncomment if you want sizes in whole %:
623 $color = $min_sat + ($value - $min_qty) * $step_sat;
629 if(isset($this->tags[$key])) {
630 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
635 $output = substr($output, 0, strlen($output)-2);
638 } // getAvailableTags()
641 * output all selected tags
643 * this function output all tags which have been selected
644 * by the user. the selected tags are stored in the
645 * session-variable $_SESSION['selected_tags']
647 public function getSelectedTags()
649 /* retrive tags from database */
654 foreach($this->avail_tags as $tag)
656 // return all selected tags
657 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
658 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
663 $output = substr($output, 0, strlen($output)-2);
667 return "no tags selected";
670 } // getSelectedTags()
673 * add tag to users session variable
675 * this function will add the specified to users current
676 * tag selection. if a date search has been made before
677 * it will be now cleared
679 public function addTag($tag)
681 if(!isset($_SESSION['selected_tags']))
682 $_SESSION['selected_tags'] = Array();
684 if(isset($_SESSION['searchfor_tag']))
685 unset($_SESSION['searchfor_tag']);
687 if(!in_array($tag, $_SESSION['selected_tags']))
688 array_push($_SESSION['selected_tags'], $tag);
696 * remove tag to users session variable
698 * this function removes the specified tag from
699 * users current tag selection
701 public function delTag($tag)
703 if(isset($_SESSION['searchfor_tag']))
704 unset($_SESSION['searchfor_tag']);
706 if(isset($_SESSION['selected_tags'])) {
707 $key = array_search($tag, $_SESSION['selected_tags']);
708 unset($_SESSION['selected_tags'][$key]);
709 sort($_SESSION['selected_tags']);
717 * reset tag selection
719 * if there is any tag selection, it will be
722 public function resetTags()
724 if(isset($_SESSION['selected_tags']))
725 unset($_SESSION['selected_tags']);
730 * returns the value for the autocomplet tag-search
732 public function get_xml_tag_list()
734 if(!isset($_GET['search']) || !is_string($_GET['search']))
735 $_GET['search'] = '';
740 /* retrive tags from database */
743 $matched_tags = Array();
745 header("Content-Type: text/xml");
747 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
748 $string.= "<results>\n";
750 foreach($this->avail_tags as $tag)
752 if(!empty($_GET['search']) &&
753 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
754 count($matched_tags) < $length) {
756 $count = $this->get_num_photos($tag);
759 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
762 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
768 /* if we have collected enough items, break out */
769 if(count($matched_tags) >= $length)
773 $string.= "</results>\n";
777 } // get_xml_tag_list()
783 * if a specific photo was requested (external link)
784 * unset the session variable now
786 public function resetPhotoView()
788 if(isset($_SESSION['current_photo']))
789 unset($_SESSION['current_photo']);
791 } // resetPhotoView();
796 * if any tag search has taken place, reset it now
798 public function resetTagSearch()
800 if(isset($_SESSION['searchfor_tag']))
801 unset($_SESSION['searchfor_tag']);
803 } // resetTagSearch()
808 * if any name search has taken place, reset it now
810 public function resetNameSearch()
812 if(isset($_SESSION['searchfor_name']))
813 unset($_SESSION['searchfor_name']);
815 } // resetNameSearch()
820 * if any date search has taken place, reset
823 public function resetDateSearch()
825 if(isset($_SESSION['from_date']))
826 unset($_SESSION['from_date']);
827 if(isset($_SESSION['to_date']))
828 unset($_SESSION['to_date']);
830 } // resetDateSearch();
833 * return all photo according selection
835 * this function returns all photos based on
836 * the tag-selection, tag- or date-search.
837 * the tag-search also has to take care of AND
838 * and OR conjunctions
840 public function getPhotoSelection()
842 $matched_photos = Array();
843 $additional_where_cond = "";
845 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
846 $from_date = $_SESSION['from_date'];
847 $to_date = $_SESSION['to_date'];
848 $additional_where_cond.= "
849 p.time>='". $from_date ."'
851 p.time<='". $to_date ."'
855 if(isset($_SESSION['searchfor_name'])) {
856 if($this->dbver < 9) {
857 $additional_where_cond.= "
859 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
861 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
866 $additional_where_cond.= "
868 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
870 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
876 if(isset($_SESSION['sort_order'])) {
877 $order_str = $this->get_sort_order();
880 /* return a search result */
881 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
883 SELECT DISTINCT pt1.photo_id
885 INNER JOIN photo_tags pt2
886 ON pt1.photo_id=pt2.photo_id
893 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
895 if(isset($additional_where_cond) && !empty($additional_where_cond))
896 $query_str.= "AND ". $additional_where_cond ." ";
898 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
899 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
902 if(isset($order_str))
903 $query_str.= $order_str;
905 $result = $this->db->db_query($query_str);
906 while($row = $this->db->db_fetch_object($result)) {
907 array_push($matched_photos, $row['photo_id']);
909 return $matched_photos;
912 /* return according the selected tags */
913 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
915 foreach($_SESSION['selected_tags'] as $tag)
916 $selected.= $tag .",";
917 $selected = substr($selected, 0, strlen($selected)-1);
919 /* photo has to match at least on of the selected tags */
920 if($_SESSION['tag_condition'] == 'or') {
922 SELECT DISTINCT pt1.photo_id
924 INNER JOIN photo_tags pt2
925 ON pt1.photo_id=pt2.photo_id
930 WHERE pt1.tag_id IN (". $selected .")
932 if(isset($additional_where_cond) && !empty($additional_where_cond))
933 $query_str.= "AND ". $additional_where_cond ." ";
935 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
936 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
939 if(isset($order_str))
940 $query_str.= $order_str;
942 /* photo has to match all selected tags */
943 elseif($_SESSION['tag_condition'] == 'and') {
945 if(count($_SESSION['selected_tags']) >= 32) {
946 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
947 print "evaluate your tag selection. Please remove some tags from your selection.\n";
951 /* Join together a table looking like
953 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
955 so the query can quickly return all images matching the
956 selected tags in an AND condition
961 SELECT DISTINCT pt1.photo_id
965 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
972 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
974 INNER JOIN photo_tags pt". ($i+2) ."
975 ON pt1.photo_id=pt". ($i+2) .".photo_id
982 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
983 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
985 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
988 if(isset($additional_where_cond) && !empty($additional_where_cond))
989 $query_str.= "AND ". $additional_where_cond;
991 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
992 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
995 if(isset($order_str))
996 $query_str.= $order_str;
1000 $result = $this->db->db_query($query_str);
1001 while($row = $this->db->db_fetch_object($result)) {
1002 array_push($matched_photos, $row['photo_id']);
1004 return $matched_photos;
1007 /* return all available photos */
1009 SELECT DISTINCT p.id
1011 LEFT JOIN photo_tags pt
1017 if(isset($additional_where_cond) && !empty($additional_where_cond))
1018 $query_str.= "WHERE ". $additional_where_cond ." ";
1020 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1021 if(isset($additional_where_cond) && !empty($additional_where_cond))
1022 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1024 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1027 if(isset($order_str))
1028 $query_str.= $order_str;
1030 $result = $this->db->db_query($query_str);
1031 while($row = $this->db->db_fetch_object($result)) {
1032 array_push($matched_photos, $row['id']);
1034 return $matched_photos;
1036 } // getPhotoSelection()
1039 * control HTML ouput for photo index
1041 * this function provides all the necessary information
1042 * for the photo index template.
1044 public function showPhotoIndex()
1046 $photos = $this->getPhotoSelection();
1048 $count = count($photos);
1050 /* if all thumbnails should be shown on one page */
1051 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1055 /* thumbnails should be splitted up in several pages */
1056 elseif($this->cfg->thumbs_per_page > 0) {
1058 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1062 $begin_with = $_SESSION['begin_with'];
1065 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1069 $images[$thumbs] = Array();
1070 $img_height[$thumbs] = Array();
1071 $img_width[$thumbs] = Array();
1072 $img_id[$thumbs] = Array();
1073 $img_name[$thumbs] = Array();
1074 $img_fullname[$thumbs] = Array();
1075 $img_title = Array();
1077 for($i = $begin_with; $i < $end_with; $i++) {
1079 if(isset($photos[$i])) {
1081 $images[$thumbs] = $photos[$i];
1082 $img_id[$thumbs] = $i;
1083 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1084 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1085 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1087 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1089 if(file_exists($thumb_path)) {
1090 $info = getimagesize($thumb_path);
1091 $img_width[$thumbs] = $info[0];
1092 $img_height[$thumbs] = $info[1];
1098 // +1 for for smarty's selection iteration
1101 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1102 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1104 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1105 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1106 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1109 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1110 $this->tmpl->assign('tag_result', 1);
1113 /* do we have to display the page selector ? */
1114 if($this->cfg->thumbs_per_page != 0) {
1118 /* calculate the page switchers */
1119 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1120 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1122 if($begin_with != 0)
1123 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1124 if($end_with < $count)
1125 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1127 $photo_per_page = $this->cfg->thumbs_per_page;
1128 $last_page = ceil($count / $photo_per_page);
1130 /* get the current selected page */
1131 if($begin_with == 0) {
1135 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1142 for($i = 1; $i <= $last_page; $i++) {
1144 if($current_page == $i)
1145 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1146 elseif($current_page-1 == $i || $current_page+1 == $i)
1147 $style = "style=\"font-size: 105%;\"";
1148 elseif(($current_page-5 >= $i) && ($i != 1) ||
1149 ($current_page+5 <= $i) && ($i != $last_page))
1150 $style = "style=\"font-size: 75%;\"";
1154 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1157 $select.= ">". $i ."</a> ";
1159 // until 9 pages we show the selector from 1-9
1160 if($last_page <= 9) {
1161 $page_select.= $select;
1164 if($i == 1 /* first page */ ||
1165 $i == $last_page /* last page */ ||
1166 $i == $current_page /* current page */ ||
1167 $i == ceil($last_page * 0.25) /* first quater */ ||
1168 $i == ceil($last_page * 0.5) /* half */ ||
1169 $i == ceil($last_page * 0.75) /* third quater */ ||
1170 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1171 (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 */ ||
1172 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1173 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1175 $page_select.= $select;
1183 $page_select.= "......... ";
1188 /* only show the page selector if we have more then one page */
1190 $this->tmpl->assign('page_selector', $page_select);
1194 $current_tags = $this->getCurrentTags();
1195 $extern_link = "index.php?mode=showpi";
1196 $rss_link = "index.php?mode=rss";
1197 if($current_tags != "") {
1198 $extern_link.= "&tags=". $current_tags;
1199 $rss_link.= "&tags=". $current_tags;
1201 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1202 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1203 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1206 $export_link = "index.php?mode=export";
1207 $slideshow_link = "index.php?mode=slideshow";
1209 $this->tmpl->assign('extern_link', $extern_link);
1210 $this->tmpl->assign('slideshow_link', $slideshow_link);
1211 $this->tmpl->assign('export_link', $export_link);
1212 $this->tmpl->assign('rss_link', $rss_link);
1213 $this->tmpl->assign('count', $count);
1214 $this->tmpl->assign('width', $this->cfg->thumb_width);
1215 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1216 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1217 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1218 $this->tmpl->assign('images', $images);
1219 $this->tmpl->assign('img_width', $img_width);
1220 $this->tmpl->assign('img_height', $img_height);
1221 $this->tmpl->assign('img_id', $img_id);
1222 $this->tmpl->assign('img_name', $img_name);
1223 $this->tmpl->assign('img_fullname', $img_fullname);
1224 $this->tmpl->assign('img_title', $img_title);
1225 $this->tmpl->assign('thumbs', $thumbs);
1227 $this->tmpl->show("photo_index.tpl");
1229 /* if we are returning to photo index from an photo-view,
1230 scroll the window to the last shown photo-thumbnail.
1231 after this, unset the last_photo session variable.
1233 if(isset($_SESSION['last_photo'])) {
1234 print "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1235 unset($_SESSION['last_photo']);
1238 } // showPhotoIndex()
1241 * show credit template
1243 public function showCredits()
1245 $this->tmpl->assign('version', $this->cfg->version);
1246 $this->tmpl->assign('product', $this->cfg->product);
1247 $this->tmpl->assign('db_version', $this->dbver);
1248 $this->tmpl->show("credits.tpl");
1253 * create_thumbnails for the requested width
1255 * this function creates image thumbnails of $orig_image
1256 * stored as $thumb_image. It will check if the image is
1257 * in a supported format, if necessary rotate the image
1258 * (based on EXIF orientation meta headers) and re-sizing.
1260 public function create_thumbnail($orig_image, $thumb_image, $width)
1262 if(!file_exists($orig_image)) {
1266 $details = getimagesize($orig_image);
1268 /* check if original photo is a support image type */
1269 if(!$this->checkifImageSupported($details['mime']))
1272 switch($details['mime']) {
1276 $meta = $this->get_meta_informations($orig_image);
1282 switch($meta['Orientation']) {
1283 case 1: /* top, left */
1284 /* nothing to do */ break;
1285 case 2: /* top, right */
1286 $rotate = 0; $flip_hori = true; break;
1287 case 3: /* bottom, left */
1288 $rotate = 180; break;
1289 case 4: /* bottom, right */
1290 $flip_vert = true; break;
1291 case 5: /* left side, top */
1292 $rotate = 90; $flip_vert = true; break;
1293 case 6: /* right side, top */
1294 $rotate = 90; break;
1295 case 7: /* left side, bottom */
1296 $rotate = 270; $flip_vert = true; break;
1297 case 8: /* right side, bottom */
1298 $rotate = 270; break;
1301 $src_img = @imagecreatefromjpeg($orig_image);
1307 $src_img = @imagecreatefrompng($orig_image);
1313 $src_img = new Imagick($orig_image);
1314 print_r($src_img->queryFormats());
1316 $handler = "imagick";
1327 if(!isset($src_img) || empty($src_img)) {
1328 print "Can't load image from ". $orig_image ."\n";
1332 /* grabs the height and width */
1333 $cur_width = imagesx($src_img);
1334 $cur_height = imagesy($src_img);
1336 // If requested width is more then the actual image width,
1337 // do not generate a thumbnail, instead safe the original
1338 // as thumbnail but with lower quality. But if the image
1339 // is to heigh too, then we still have to resize it.
1340 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1341 $result = imagejpeg($src_img, $thumb_image, 75);
1342 imagedestroy($src_img);
1346 // If the image will be rotate because EXIF orientation said so
1347 // 'virtually rotate' the image for further calculations
1348 if($rotate == 90 || $rotate == 270) {
1350 $cur_width = $cur_height;
1354 /* calculates aspect ratio */
1355 $aspect_ratio = $cur_height / $cur_width;
1358 if($aspect_ratio < 1) {
1360 $new_h = abs($new_w * $aspect_ratio);
1362 /* 'virtually' rotate the image and calculate it's ratio */
1363 $tmp_w = $cur_height;
1364 $tmp_h = $cur_width;
1365 /* now get the ratio from the 'rotated' image */
1366 $tmp_ratio = $tmp_h/$tmp_w;
1367 /* now calculate the new dimensions */
1369 $tmp_h = abs($tmp_w * $tmp_ratio);
1371 // now that we know, how high they photo should be, if it
1372 // gets rotated, use this high to scale the image
1374 $new_w = abs($new_h / $aspect_ratio);
1376 // If the image will be rotate because EXIF orientation said so
1377 // now 'virtually rotate' back the image for the image manipulation
1378 if($rotate == 90 || $rotate == 270) {
1385 /* creates new image of that size */
1386 $dst_img = imagecreatetruecolor($new_w, $new_h);
1388 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1390 /* copies resized portion of original image into new image */
1391 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1393 /* needs the image to be flipped horizontal? */
1395 $this->_debug("(FLIP)");
1396 $dst_img = $this->flipImage($dst_img, 'hori');
1398 /* needs the image to be flipped vertical? */
1400 $this->_debug("(FLIP)");
1401 $dst_img = $this->flipImage($dst_img, 'vert');
1405 $this->_debug("(ROTATE)");
1406 $dst_img = $this->rotateImage($dst_img, $rotate);
1409 /* write down new generated file */
1410 $result = imagejpeg($dst_img, $thumb_image, 75);
1412 /* free your mind */
1413 imagedestroy($dst_img);
1414 imagedestroy($src_img);
1416 if($result === false) {
1417 print "Can't write thumbnail ". $thumb_image ."\n";
1431 } // create_thumbnail()
1434 * return all exif meta data from the file
1436 public function get_meta_informations($file)
1438 return exif_read_data($file);
1440 } // get_meta_informations()
1443 * create phpfspot own sqlite database
1445 * this function creates phpfspots own sqlite database
1446 * if it does not exist yet. this own is used to store
1447 * some necessary informations (md5 sum's, ...).
1449 public function check_config_table()
1451 // if the config table doesn't exist yet, create it
1452 if(!$this->cfg_db->db_check_table_exists("images")) {
1453 $this->cfg_db->db_exec("
1454 CREATE TABLE images (
1455 img_idx int primary key,
1461 } // check_config_table
1464 * Generates a thumbnail from photo idx
1466 * This function will generate JPEG thumbnails from provided F-Spot photo
1469 * 1. Check if all thumbnail generations (width) are already in place and
1471 * 2. Check if the md5sum of the original file has changed
1472 * 3. Generate the thumbnails if needed
1474 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1478 $resolutions = Array(
1479 $this->cfg->thumb_width,
1480 $this->cfg->photo_width,
1481 $this->cfg->mini_width,
1484 /* get details from F-Spot's database */
1485 $details = $this->get_photo_details($idx);
1487 /* calculate file MD5 sum */
1488 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1490 if(!file_exists($full_path)) {
1491 $this->_error("File ". $full_path ." does not exist\n");
1495 if(!is_readable($full_path)) {
1496 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1500 $file_md5 = md5_file($full_path);
1502 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1506 foreach($resolutions as $resolution) {
1508 $generate_it = false;
1510 $thumb_sub_path = substr($file_md5, 0, 2);
1511 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1513 /* if thumbnail-subdirectory does not exist yet, create it */
1514 if(!file_exists(dirname($thumb_path))) {
1515 mkdir(dirname($thumb_path), 0755);
1518 /* if the thumbnail file doesn't exist, create it */
1519 if(!file_exists($thumb_path)) {
1520 $generate_it = true;
1522 /* if the file hasn't changed there is no need to regen the thumb */
1523 elseif($file_md5 != $this->getMD5($idx) || $force) {
1524 $generate_it = true;
1527 if($generate_it || $overwrite) {
1529 $this->_debug(" ". $resolution ."px");
1530 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1538 $this->_debug(" already exist");
1541 /* set the new/changed MD5 sum for the current photo */
1543 $this->setMD5($idx, $file_md5);
1546 $this->_debug("\n");
1551 * returns stored md5 sum for a specific photo
1553 * this function queries the phpfspot database for a
1554 * stored MD5 checksum of the specified photo
1556 public function getMD5($idx)
1558 $result = $this->cfg_db->db_query("
1561 WHERE img_idx='". $idx ."'
1567 $img = $this->cfg_db->db_fetch_object($result);
1568 return $img['img_md5'];
1573 * set MD5 sum for the specific photo
1575 private function setMD5($idx, $md5)
1577 $result = $this->cfg_db->db_exec("
1578 REPLACE INTO images (img_idx, img_md5)
1579 VALUES ('". $idx ."', '". $md5 ."')
1585 * store current tag condition
1587 * this function stores the current tag condition
1588 * (AND or OR) in the users session variables
1590 public function setTagCondition($mode)
1592 $_SESSION['tag_condition'] = $mode;
1596 } // setTagCondition()
1599 * invoke tag & date search
1601 * this function will return all matching tags and store
1602 * them in the session variable selected_tags. furthermore
1603 * it also handles the date search.
1604 * getPhotoSelection() will then only return the matching
1607 public function startSearch()
1609 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1610 $from = $_POST['from'];
1612 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
1616 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
1617 $searchfor_tag = $_POST['for_tag'];
1618 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
1621 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
1622 $searchfor_name = $_POST['for_name'];
1623 $_SESSION['searchfor_name'] = $_POST['for_name'];
1628 if(isset($from) && !empty($from))
1629 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1631 unset($_SESSION['from_date']);
1633 if(isset($to) && !empty($to))
1634 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1636 unset($_SESSION['to_date']);
1638 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
1639 /* new search, reset the current selected tags */
1640 $_SESSION['selected_tags'] = Array();
1641 foreach($this->avail_tags as $tag) {
1642 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
1643 array_push($_SESSION['selected_tags'], $tag);
1652 * updates sort order in session variable
1654 * this function is invoked by RPC and will sort the requested
1655 * sort order in the session variable.
1657 public function updateSortOrder($order)
1659 if(isset($this->sort_orders[$order])) {
1660 $_SESSION['sort_order'] = $order;
1664 return "unkown error";
1666 } // updateSortOrder()
1671 * this function rotates the image according the
1674 private function rotateImage($img, $degrees)
1676 if(function_exists("imagerotate")) {
1677 $img = imagerotate($img, $degrees, 0);
1679 function imagerotate($src_img, $angle)
1681 $src_x = imagesx($src_img);
1682 $src_y = imagesy($src_img);
1683 if ($angle == 180) {
1687 elseif ($src_x <= $src_y) {
1691 elseif ($src_x >= $src_y) {
1696 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1697 imagealphablending($rotate, false);
1702 for ($y = 0; $y < ($src_y); $y++) {
1703 for ($x = 0; $x < ($src_x); $x++) {
1704 $color = imagecolorat($src_img, $x, $y);
1705 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1711 for ($y = 0; $y < ($src_y); $y++) {
1712 for ($x = 0; $x < ($src_x); $x++) {
1713 $color = imagecolorat($src_img, $x, $y);
1714 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1720 for ($y = 0; $y < ($src_y); $y++) {
1721 for ($x = 0; $x < ($src_x); $x++) {
1722 $color = imagecolorat($src_img, $x, $y);
1723 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1737 $img = imagerotate($img, $degrees);
1746 * returns flipped image
1748 * this function will return an either horizontal or
1749 * vertical flipped truecolor image.
1751 private function flipImage($image, $mode)
1753 $w = imagesx($image);
1754 $h = imagesy($image);
1755 $flipped = imagecreatetruecolor($w, $h);
1759 for ($y = 0; $y < $h; $y++) {
1760 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1764 for ($x = 0; $x < $w; $x++) {
1765 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
1775 * return all assigned tags for the specified photo
1777 private function get_photo_tags($idx)
1779 $result = $this->db->db_query("
1782 INNER JOIN photo_tags pt
1784 WHERE pt.photo_id='". $idx ."'
1789 while($row = $this->db->db_fetch_object($result))
1790 $tags[$row['id']] = $row['name'];
1794 } // get_photo_tags()
1797 * create on-the-fly images with text within
1799 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
1801 if (strlen($color) != 6)
1804 $int = hexdec($color);
1805 $h = imagefontheight($font);
1806 $fw = imagefontwidth($font);
1807 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
1808 $lines = count($txt);
1809 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
1810 $bg = imagecolorallocate($im, 255, 255, 255);
1811 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
1814 foreach ($txt as $text) {
1815 $x = (($w - ($fw * strlen($text))) / 2);
1816 imagestring($im, $font, $x, $y, $text, $color);
1817 $y += ($h + $space);
1820 Header("Content-type: image/png");
1823 } // showTextImage()
1826 * check if all requirements are met
1828 private function check_requirements()
1830 if(!function_exists("imagecreatefromjpeg")) {
1831 print "PHP GD library extension is missing<br />\n";
1835 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
1836 print "PHP SQLite3 library extension is missing<br />\n";
1840 /* Check for HTML_AJAX PEAR package, lent from Horde project */
1841 ini_set('track_errors', 1);
1842 @include_once 'HTML/AJAX/Server.php';
1843 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1844 print "PEAR HTML_AJAX package is missing<br />\n";
1847 @include_once 'Calendar/Calendar.php';
1848 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1849 print "PEAR Calendar package is missing<br />\n";
1852 @include_once 'Console/Getopt.php';
1853 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1854 print "PEAR Console_Getopt package is missing<br />\n";
1857 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
1858 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
1859 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
1862 ini_restore('track_errors');
1869 } // check_requirements()
1871 private function _debug($text)
1873 if($this->fromcmd) {
1880 * check if specified MIME type is supported
1882 public function checkifImageSupported($mime)
1884 if(in_array($mime, Array("image/jpeg", "image/png", "image/tiff")))
1889 } // checkifImageSupported()
1891 public function _error($text)
1893 switch($this->cfg->logging) {
1896 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
1897 print $text ."<br />\n";
1903 error_log($text, 3, $his->cfg->log_file);
1907 $this->runtime_error = true;
1912 * output calendard input fields
1914 private function get_calendar($mode)
1916 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
1917 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
1918 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
1920 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
1921 if(!isset($_SESSION[$mode .'_date']))
1922 $output.= " disabled=\"disabled\"";
1924 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
1925 if(!isset($_SESSION[$mode .'_date']))
1926 $output.= " disabled=\"disabled\"";
1928 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
1929 if(!isset($_SESSION[$mode .'_date']))
1930 $output.= " disabled=\"disabled\"";
1938 * output calendar matrix
1940 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
1942 if (!isset($year)) $year = date('Y');
1943 if (!isset($month)) $month = date('m');
1944 if (!isset($day)) $day = date('d');
1949 require_once CALENDAR_ROOT.'Month/Weekdays.php';
1950 require_once CALENDAR_ROOT.'Day.php';
1953 $month = new Calendar_Month_Weekdays($year,$month);
1956 $prevStamp = $month->prevMonth(true);
1957 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
1958 $nextStamp = $month->nextMonth(true);
1959 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
1961 $selectedDays = array (
1962 new Calendar_Day($year,$month,$day),
1963 new Calendar_Day($year,12,25),
1966 // Build the days in the month
1967 $month->build($selectedDays);
1969 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
1970 $this->tmpl->assign('prev_month', $prev);
1971 $this->tmpl->assign('next_month', $next);
1973 while ( $day = $month->fetch() ) {
1975 if(!isset($matrix[$rows]))
1976 $matrix[$rows] = Array();
1980 $dayStamp = $day->thisDay(true);
1981 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
1983 // isFirst() to find start of week
1984 if ( $day->isFirst() )
1987 if ( $day->isSelected() ) {
1988 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
1989 } else if ( $day->isEmpty() ) {
1990 $string.= "<td> </td>\n";
1992 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
1995 // isLast() to find end of week
1996 if ( $day->isLast() )
1997 $string.= "</tr>\n";
1999 $matrix[$rows][$cols] = $string;
2009 $this->tmpl->assign('matrix', $matrix);
2010 $this->tmpl->assign('rows', $rows);
2011 $this->tmpl->show("calendar.tpl");
2013 } // get_calendar_matrix()
2016 * output export page
2018 public function getExport($mode)
2020 $pictures = $this->getPhotoSelection();
2021 $current_tags = $this->getCurrentTags();
2023 foreach($pictures as $picture) {
2025 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2026 if($current_tags != "") {
2027 $orig_url.= "&tags=". $current_tags;
2029 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2030 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2033 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2038 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2039 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2043 // "[%pictureurl% %thumbnailurl%]"
2044 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2047 case 'MoinMoinList':
2048 // " * [%pictureurl% %thumbnailurl%]"
2049 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2060 public function getRSSFeed()
2062 Header("Content-type: text/xml; charset=utf-8");
2063 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2066 xmlns:media="http://search.yahoo.com/mrss/"
2067 xmlns:dc="http://purl.org/dc/elements/1.1/"
2070 <title>phpfspot</title>
2071 <description>phpfspot RSS feed</description>
2072 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2073 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2074 <generator>phpfspot</generator>
2077 $pictures = $this->getPhotoSelection();
2078 $current_tags = $this->getCurrentTags();
2080 foreach($pictures as $picture) {
2082 $orig_url = $this->get_phpfspot_url() ."index.php?mode=showp&id=". $picture;
2083 if($current_tags != "") {
2084 $orig_url.= "&tags=". $current_tags;
2086 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2087 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2090 $details = $this->get_photo_details($picture);
2092 $thumb_url = $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2093 $thumb_html = htmlspecialchars("
2094 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2096 ". $details['description']);
2098 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2100 /* get EXIF information if JPEG */
2101 if($details['mime'] == "image/jpeg") {
2102 $meta = $this->get_meta_informations($orig_path);
2105 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2109 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2110 <link><?php print htmlspecialchars($orig_url); ?></link>
2111 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2112 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2114 <?php print $thumb_html; ?>
2116 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2131 * return all selected tags as one string
2133 private function getCurrentTags()
2136 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2137 foreach($_SESSION['selected_tags'] as $tag)
2138 $current_tags.= $tag .",";
2139 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2141 return $current_tags;
2143 } // getCurrentTags()
2146 * return the current photo
2148 public function getCurrentPhoto()
2150 if(isset($_SESSION['current_photo'])) {
2151 print $_SESSION['current_photo'];
2153 } // getCurrentPhoto()
2156 * tells the client browser what to do
2158 * this function is getting called via AJAX by the
2159 * client browsers. it will tell them what they have
2160 * to do next. This is necessary for directly jumping
2161 * into photo index or single photo view when the are
2162 * requested with specific URLs
2164 public function whatToDo()
2166 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2167 return "show_photo";
2169 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2170 return "showpi_tags";
2172 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2176 return "nothing special";
2181 * return the current process-user
2183 private function getuid()
2185 if($uid = posix_getuid()) {
2186 if($user = posix_getpwuid($uid)) {
2187 return $user['name'];
2196 * returns a select-dropdown box to select photo index sort parameters
2198 public function smarty_sort_select_list($params, &$smarty)
2202 foreach($this->sort_orders as $key => $value) {
2203 $output.= "<option value=\"". $key ."\"";
2204 if($key == $_SESSION['sort_order']) {
2205 $output.= " selected=\"selected\"";
2207 $output.= ">". $value ."</option>";
2212 } // smarty_sort_select_list()
2215 * returns the currently selected sort order
2217 private function get_sort_order()
2219 switch($_SESSION['sort_order']) {
2221 return " ORDER BY p.time ASC";
2224 return " ORDER BY p.time DESC";
2227 if($this->dbver < 9) {
2228 return " ORDER BY p.name ASC";
2231 return " ORDER BY basename(p.uri) ASC";
2235 if($this->dbver < 9) {
2236 return " ORDER BY p.name DESC";
2239 return " ORDER BY basename(p.uri) DESC";
2243 return " ORDER BY t.name ASC ,p.time ASC";
2246 return " ORDER BY t.name DESC ,p.time ASC";
2250 } // get_sort_order()
2253 * return the next to be shown slide show image
2255 * this function returns the URL of the next image
2256 * in the slideshow sequence.
2258 public function getNextSlideShowImage()
2260 $all_photos = $this->getPhotoSelection();
2262 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2263 $_SESSION['slideshow_img'] = 0;
2265 $_SESSION['slideshow_img']++;
2267 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2269 } // getNextSlideShowImage()
2272 * return the previous to be shown slide show image
2274 * this function returns the URL of the previous image
2275 * in the slideshow sequence.
2277 public function getPrevSlideShowImage()
2279 $all_photos = $this->getPhotoSelection();
2281 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2282 $_SESSION['slideshow_img'] = 0;
2284 $_SESSION['slideshow_img']--;
2286 return $this->get_phpfspot_url() ."phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2288 } // getPrevSlideShowImage()
2290 public function resetSlideShow()
2292 if(isset($_SESSION['slideshow_img']))
2293 unset($_SESSION['slideshow_img']);
2295 } // resetSlideShow()
2300 * this function will get all photos from the fspot
2301 * database and randomly return ONE entry
2303 * saddly there is yet no sqlite3 function which returns
2304 * the bulk result in array, so we have to fill up our
2307 public function get_random_photo()
2311 $result = $this->db->db_query("
2316 while($row = $this->db->db_fetch_object($result)) {
2317 array_push($all, $row['id']);
2320 return $all[array_rand($all)];
2322 } // get_random_photo()
2325 * validates provided date
2327 * this function validates if the provided date
2328 * contains a valid date and will return true
2331 public function isValidDate($date_str)
2333 $timestamp = strtotime($date_str);
2335 if(is_numeric($timestamp))
2343 * timestamp to string conversion
2345 private function ts2str($timestamp)
2347 return strftime("%Y-%m-%d", $timestamp);
2350 private function extractTags($tags_str)
2352 $not_validated = split(',', $_GET['tags']);
2353 $validated = array();
2355 foreach($not_validated as $tag) {
2356 if(is_numeric($tag))
2357 array_push($validated, $tag);
2365 * returns the full path to a thumbnail
2367 public function get_thumb_path($width, $photo)
2369 $md5 = $this->getMD5($photo);
2370 $sub_path = substr($md5, 0, 2);
2371 return $this->cfg->thumb_path
2379 } // get_thumb_path()
2382 * returns server's virtual host name
2384 private function get_server_name()
2386 return $_SERVER['SERVER_NAME'];
2387 } // get_server_name()
2390 * returns type of webprotocol which is
2393 private function get_web_protocol()
2395 if(!isset($_SERVER['HTTPS']))
2399 } // get_web_protocol()
2402 * return url to this phpfspot installation
2404 private function get_phpfspot_url()
2406 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2407 } // get_phpfspot_url()
2410 * returns the number of photos which are tagged with $tag_id
2412 public function get_num_photos($tag_id)
2414 if($result = $this->db->db_fetchSingleRow("
2415 SELECT count(*) as number
2418 tag_id LIKE '". $tag_id ."'")) {
2420 return $result['number'];
2426 } // get_num_photos()
2429 * check file exists and is readable
2431 * returns true, if everything is ok, otherwise false
2432 * if $silent is not set, this function will output and
2435 private function check_readable($file, $silent = null)
2437 if(!file_exists($file)) {
2439 print "File \"". $file ."\" does not exist.\n";
2443 if(!is_readable($file)) {
2445 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2451 } // check_readable()
2454 * check if all needed indices are present
2456 * this function checks, if some needed indices are already
2457 * present, or if not, create them on the fly. they are
2458 * necessary to speed up some queries like that one look for
2459 * all tags, when show_tags is specified in the configuration.
2461 private function checkDbIndices()
2463 $result = $this->db->db_exec("
2464 CREATE INDEX IF NOT EXISTS
2471 } // checkDbIndices()
2474 * retrive F-Spot database version
2476 * this function will return the F-Spot database version number
2477 * It is stored within the sqlite3 database in the table meta
2479 public function getFspotDBVersion()
2481 if($result = $this->db->db_fetchSingleRow("
2482 SELECT data as version
2485 name LIKE 'F-Spot Database Version'
2487 return $result['version'];
2491 } // getFspotDBVersion()
2494 * parse the provided URI and will returned the
2497 public function parse_uri($uri, $mode)
2499 if(($components = parse_url($uri)) !== false) {
2503 return basename($components['path']);
2506 return dirname($components['path']);
2509 return $components['path'];
2519 * validate config options
2521 * this function checks if all necessary configuration options are
2522 * specified and set.
2524 private function check_config_options()
2526 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
2527 $this->_error("Please set \$page_title in phpfspot_cfg");
2529 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
2530 $this->_error("Please set \$base_path in phpfspot_cfg");
2532 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
2533 $this->_error("Please set \$web_path in phpfspot_cfg");
2535 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
2536 $this->_error("Please set \$thumb_path in phpfspot_cfg");
2538 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
2539 $this->_error("Please set \$smarty_path in phpfspot_cfg");
2541 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
2542 $this->_error("Please set \$fspot_db in phpfspot_cfg");
2544 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
2545 $this->_error("Please set \$db_access in phpfspot_cfg");
2547 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
2548 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
2550 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
2551 $this->_error("Please set \$thumb_width in phpfspot_cfg");
2553 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
2554 $this->_error("Please set \$thumb_height in phpfspot_cfg");
2556 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
2557 $this->_error("Please set \$photo_width in phpfspot_cfg");
2559 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
2560 $this->_error("Please set \$mini_width in phpfspot_cfg");
2562 if(!isset($this->cfg->thumbs_per_page))
2563 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
2565 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
2566 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
2568 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
2569 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
2571 if(!isset($this->cfg->hide_tags))
2572 $this->_error("Please set \$hide_tags in phpfspot_cfg");
2574 if(!isset($this->cfg->theme_name))
2575 $this->_error("Please set \$theme_name in phpfspot_cfg");
2577 if(!isset($this->cfg->logging))
2578 $this->_error("Please set \$logging in phpfspot_cfg");
2580 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
2582 if(!isset($this->cfg->log_file))
2583 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
2585 if(!is_writeable($this->cfg->log_file))
2586 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
2590 /* check for pending slash on web_path */
2591 if(!preg_match("/\/$/", $this->cfg->web_path))
2592 $this->cfg->web_path.= "/";
2594 return $this->runtime_error;
2596 } // check_config_options()
2599 * cleanup phpfspot own database
2601 * When photos are getting delete from F-Spot, there will remain
2602 * remain some residues in phpfspot own database. This function
2603 * will try to wipe them out.
2605 public function cleanup_phpfspot_db()
2607 $to_delete = Array();
2609 $result = $this->cfg_db->db_query("
2612 ORDER BY img_idx ASC
2615 while($row = $this->cfg_db->db_fetch_object($result)) {
2616 if(!$this->db->db_fetchSingleRow("
2619 WHERE id='". $row['img_idx'] ."'")) {
2621 array_push($to_delete, $row['img_idx'], ',');
2625 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
2627 $this->cfg_db->db_exec("
2629 WHERE img_idx IN (". implode($to_delete) .")
2632 } // cleanup_phpfspot_db()
2635 * return first image of the page, the $current photo
2638 * this function is used to find out the first photo of the
2639 * current page, in which the $current photo lies. this is
2640 * used to display the correct photo, when calling showPhotoIndex()
2643 private function getCurrentPage($current, $max)
2645 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
2646 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
2647 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
2653 } // getCurrentPage()