3 /***************************************************************************
5 * phpfspot, presents your F-Spot photo collection in Web browsers.
7 * Copyright (c) by Andreas Unterkircher
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 ***************************************************************************/
25 require_once "phpfspot_cfg.php";
26 require_once "phpfspot_db.php";
31 * this class contains the most functions which will to the major
39 * phpfspot configuration
47 * SQLite database handle to f-spot database
55 * SQLite database handle to phpfspot database
63 * Smarty template engine
64 * @link http://smarty.php.net smarty.php.net
65 * @see PHPFSPOT_TMPL()
79 * list of available, not-selected, tags
86 * true if runtime error occued
90 private $runtime_error = false;
93 * F-Spot database version
100 * class constructor ($cfg, $db, $cfg_db, $tmpl, $db_ver)
102 * this function will be called on class construct
103 * and will check requirements, loads configuration,
104 * open databases and start the user session
106 public function __construct()
109 * register PHPFSPOT class global
111 * @global PHPFSPOT $GLOBALS['phpfspot']
114 $GLOBALS['phpfspot'] =& $this;
116 $this->cfg = new PHPFSPOT_CFG;
118 /* verify config settings */
119 if($this->check_config_options()) {
123 /* set application name and version information */
124 $this->cfg->product = "phpfspot";
125 $this->cfg->version = "1.5";
127 $this->sort_orders= array(
128 'date_asc' => 'Date ↑',
129 'date_desc' => 'Date ↓',
130 'name_asc' => 'Name ↑',
131 'name_desc' => 'Name ↓',
132 'tags_asc' => 'Tags ↑',
133 'tags_desc' => 'Tags ↓',
136 /* Check necessary requirements */
137 if(!$this->check_requirements()) {
141 /******* Opening F-Spot's sqlite database *********/
143 /* Check if database file is writeable */
144 if(!is_writeable($this->cfg->fspot_db)) {
145 print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
149 /* open the database */
150 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
152 /* change sqlite temp directory, if requested */
153 if(isset($this->cfg->sqlite_temp_dir)) {
156 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
160 $this->dbver = $this->getFspotDBVersion();
162 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
163 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
167 if(!is_writeable($this->cfg->thumb_path)) {
168 print $this->cfg->thumb_path .": directory is not writeable for user ". $this->getuid() ."\n";
172 /******* Opening phpfspot's sqlite database *********/
174 /* Check if directory where the database file is stored is writeable */
175 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
176 print dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() ."\n";
180 /* Check if database file is writeable */
181 if(!is_writeable($this->cfg->phpfspot_db)) {
182 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
186 /* open the database */
187 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
189 /* change sqlite temp directory, if requested */
190 if(isset($this->cfg->sqlite_temp_dir)) {
191 $this->cfg_db->db_exec("
193 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
197 /* Check if some tables need to be created */
198 $this->check_config_table();
200 /* overload Smarty class with our own template handler */
201 require_once "phpfspot_tmpl.php";
202 $this->tmpl = new PHPFSPOT_TMPL();
204 $this->tmpl->assign('web_path', $this->cfg->web_path);
206 /* check if all necessary indices exist */
207 $this->checkDbIndices();
209 /* if session is not yet started, do it now */
210 if(session_id() == "")
213 if(!isset($_SESSION['tag_condition']))
214 $_SESSION['tag_condition'] = 'or';
216 if(!isset($_SESSION['sort_order']))
217 $_SESSION['sort_order'] = 'date_desc';
219 if(!isset($_SESSION['searchfor_tag']))
220 $_SESSION['searchfor_tag'] = '';
222 // if begin_with is still set but thumbs_per_page is now 0, unset it
223 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
224 unset($_SESSION['begin_with']);
228 public function __destruct()
234 * show - generate html output
236 * this function can be called after the constructor has
237 * prepared everyhing. it will load the index.tpl smarty
238 * template. if necessary it will registere pre-selects
239 * (photo index, photo, tag search, date search) into
242 public function show()
244 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
245 $this->tmpl->assign('page_title', $this->cfg->page_title);
246 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
247 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
250 if($this->is_user_friendly_url()) {
251 $content = $this->parse_user_friendly_url($_SERVER['REQUEST_URI']);
254 if(isset($_GET['mode'])) {
256 $_SESSION['start_action'] = $_GET['mode'];
258 switch($_GET['mode']) {
260 if(isset($_GET['tags'])) {
261 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
263 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
264 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
266 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
267 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
271 if(isset($_GET['tags'])) {
272 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
273 $_SESSION['start_action'] = 'showp';
275 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
276 $_SESSION['current_photo'] = $_GET['id'];
277 $_SESSION['start_action'] = 'showp';
279 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
280 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
282 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
283 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
287 $this->tmpl->show("export.tpl");
291 $this->tmpl->show("slideshow.tpl");
295 if(isset($_GET['tags'])) {
296 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
298 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
299 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
301 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
302 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
310 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
311 $this->tmpl->assign('date_search_enabled', true);
313 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
314 $this->tmpl->assign('from_date', $this->get_calendar('from'));
315 $this->tmpl->assign('to_date', $this->get_calendar('to'));
317 if(!isset($content)) {
318 $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags());
319 $this->tmpl->assign('preset_available_tags', $this->getAvailableTags());
320 $this->tmpl->assign('content_page', $this->tmpl->fetch('welcome.tpl'));
323 $this->tmpl->assign('content_page', $content);
325 $this->tmpl->show("index.tpl");
330 * get_tags - grab all tags of f-spot's database
332 * this function will get all available tags from
333 * the f-spot database and store them within two
334 * arrays within this class for later usage. in
335 * fact, if the user requests (hide_tags) it will
336 * opt-out some of them.
338 * this function is getting called once by show()
340 private function get_tags()
342 $this->avail_tags = Array();
345 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
348 DISTINCT t1.id as id, t1.name as name
351 INNER JOIN photo_tags
352 pt2 ON pt1.photo_id=pt2.photo_id
358 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
360 t1.sort_priority ASC";
362 $result = $this->db->db_query($query_str);
366 $result = $this->db->db_query("
369 ORDER BY sort_priority ASC
373 while($row = $this->db->db_fetch_object($result)) {
375 $tag_id = $row['id'];
376 $tag_name = $row['name'];
378 /* if the user has specified to ignore this tag in phpfspot's
379 configuration, ignore it here so it does not get added to
382 if(in_array($row['name'], $this->cfg->hide_tags))
385 /* if you include the following if-clause and the user has specified
386 to only show certain tags which are specified in phpfspot's
387 configuration, ignore all others so they will not be added to the
389 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
390 !in_array($row['name'], $this->cfg->show_tags))
394 $this->tags[$tag_id] = $tag_name;
395 $this->avail_tags[$count] = $tag_id;
403 * extract all photo details
405 * retrieve all available details from f-spot's
406 * database and return them as object
407 * @param integer $idx
408 * @return object|null
410 public function get_photo_details($idx)
412 if($this->dbver < 9) {
414 SELECT p.id, p.name, p.time, p.directory_path, p.description
420 SELECT p.id, p.uri, p.time, p.description
425 /* if show_tags is set, only return details for photos which
426 are specified to be shown
428 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
430 INNER JOIN photo_tags pt
434 WHERE p.id='". $idx ."'
435 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
439 WHERE p.id='". $idx ."'
443 if($result = $this->db->db_query($query_str)) {
445 $row = $this->db->db_fetch_object($result);
447 if($this->dbver < 9) {
448 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
457 } // get_photo_details
460 * returns aligned photo names
462 * this function returns aligned (length) names for
463 * an specific photo. If the length of the name exceeds
464 * $limit the name will be shrinked (...)
465 * @param integer $idx
466 * @param integer $limit
467 * @return string|null
469 public function getPhotoName($idx, $limit = 0)
471 if($details = $this->get_photo_details($idx)) {
472 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
473 $name = $this->shrink_text($long_name, $limit);
483 * shrink text according provided limit
485 * If the length of the name exceeds $limit the
486 * text will be shortend and some content in between
487 * will be replaced with "..."
489 * @param integer $limit
492 private function shrink_text($text, $limit)
494 if($limit != 0 && strlen($text) > $limit) {
495 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
503 * translate f-spoth photo path
505 * as the full-qualified path recorded in the f-spot database
506 * is usally not the same as on the webserver, this function
507 * will replace the path with that one specified in the cfg
508 * @param string $path
511 public function translate_path($path)
513 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
518 * control HTML ouput for a single photo
520 * this function provides all the necessary information
521 * for the single photo template.
522 * @param integer photo
524 public function showPhoto($photo)
526 /* get all photos from the current photo selection */
527 $all_photos = $this->getPhotoSelection();
528 $count = count($all_photos);
530 for($i = 0; $i < $count; $i++) {
532 // $get_next will be set, when the photo which has to
533 // be displayed has been found - this means that the
534 // next available is in fact the NEXT image (for the
536 if(isset($get_next)) {
537 $next_img = $all_photos[$i];
541 /* the next photo is our NEXT photo */
542 if($all_photos[$i] == $photo) {
546 $previous_img = $all_photos[$i];
549 if($photo == $all_photos[$i]) {
554 $details = $this->get_photo_details($photo);
561 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
562 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
564 if(!file_exists($orig_path)) {
565 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
569 if(!is_readable($orig_path)) {
570 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
574 /* If the thumbnail doesn't exist yet, try to create it */
575 if(!file_exists($thumb_path)) {
576 $this->gen_thumb($photo, true);
577 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
580 /* get mime-type, height and width from the original photo */
581 $info = getimagesize($orig_path);
583 /* get EXIF information if JPEG */
584 if($info['mime'] == "image/jpeg") {
585 $meta = $this->get_meta_informations($orig_path);
588 /* If EXIF data are available, use them */
589 if(isset($meta['ExifImageWidth'])) {
590 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
592 $meta_res = $info[0] ."x". $info[1];
595 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
596 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
597 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
599 $extern_link = "index.php?mode=showp&id=". $photo;
600 $current_tags = $this->getCurrentTags();
601 if($current_tags != "") {
602 $extern_link.= "&tags=". $current_tags;
604 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
605 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
608 $this->tmpl->assign('extern_link', $extern_link);
610 if(!file_exists($thumb_path)) {
611 $this->_error("Can't open file ". $thumb_path ."\n");
615 $info_thumb = getimagesize($thumb_path);
617 $this->tmpl->assign('description', $details['description']);
618 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
620 $this->tmpl->assign('width', $info_thumb[0]);
621 $this->tmpl->assign('height', $info_thumb[1]);
622 $this->tmpl->assign('ExifMadeOn', $meta_date);
623 $this->tmpl->assign('ExifMadeWith', $meta_make);
624 $this->tmpl->assign('ExifOrigResolution', $meta_res);
625 $this->tmpl->assign('ExifFileSize', $meta_size);
627 if($this->is_user_friendly_url()) {
628 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
629 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
632 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
633 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
636 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
638 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
639 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
640 $this->tmpl->assign('current_img', $photo);
643 $this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
644 $this->tmpl->assign('prev_img', $previous_img);
648 $this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
649 $this->tmpl->assign('next_img', $next_img);
651 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
652 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
653 $this->tmpl->assign('photo_number', $i);
654 $this->tmpl->assign('photo_count', count($all_photos));
656 return $this->tmpl->fetch("single_photo.tpl");
661 * all available tags and tag cloud
663 * this function outputs all available tags (time ordered)
664 * and in addition output them as tag cloud (tags which have
665 * many photos will appears more then others)
667 public function getAvailableTags()
669 /* retrive tags from database */
674 $result = $this->db->db_query("
675 SELECT tag_id as id, count(tag_id) as quantity
685 while($row = $this->db->db_fetch_object($result)) {
686 $tags[$row['id']] = $row['quantity'];
689 // change these font sizes if you will
690 $max_size = 125; // max font size in %
691 $min_size = 75; // min font size in %
694 $max_sat = hexdec('cc');
695 $min_sat = hexdec('44');
697 // get the largest and smallest array values
698 $max_qty = max(array_values($tags));
699 $min_qty = min(array_values($tags));
701 // find the range of values
702 $spread = $max_qty - $min_qty;
703 if (0 == $spread) { // we don't want to divide by zero
707 // determine the font-size increment
708 // this is the increase per tag quantity (times used)
709 $step = ($max_size - $min_size)/($spread);
710 $step_sat = ($max_sat - $min_sat)/($spread);
712 // loop through our tag array
713 foreach ($tags as $key => $value) {
715 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
718 // calculate CSS font-size
719 // find the $value in excess of $min_qty
720 // multiply by the font-size increment ($size)
721 // and add the $min_size set above
722 $size = $min_size + (($value - $min_qty) * $step);
723 // uncomment if you want sizes in whole %:
726 $color = $min_sat + ($value - $min_qty) * $step_sat;
732 if(isset($this->tags[$key])) {
733 $output.= "<a href=\"javascript:Tags('add', ". $key .");\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
738 $output = substr($output, 0, strlen($output)-2);
741 } // getAvailableTags()
744 * output all selected tags
746 * this function output all tags which have been selected
747 * by the user. the selected tags are stored in the
748 * session-variable $_SESSION['selected_tags']
751 public function getSelectedTags($type = 'link')
753 /* retrive tags from database */
758 foreach($this->avail_tags as $tag)
760 // return all selected tags
761 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
766 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
770 <div style=\"display: table-cell;\">
771 <div style=\"display: table-row; text-align: center;\">
772 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
773 <img src=\"phpfspot_img.php?tagidx=". $tag ."\" />
776 <div style=\"display: table-row; text-align: center;\">
777 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
778 <img src=\"resources/underbar.png\" />
789 $output = substr($output, 0, strlen($output)-2);
793 return "no tags selected";
796 } // getSelectedTags()
799 * add tag to users session variable
801 * this function will add the specified to users current
802 * tag selection. if a date search has been made before
803 * it will be now cleared
806 public function addTag($tag)
808 if(!isset($_SESSION['selected_tags']))
809 $_SESSION['selected_tags'] = Array();
811 if(isset($_SESSION['searchfor_tag']))
812 unset($_SESSION['searchfor_tag']);
814 // has the user requested to hide this tag, and still someone,
815 // somehow tries to add it, don't allow this.
816 if(!isset($this->cfg->hide_tags) &&
817 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
820 if(!in_array($tag, $_SESSION['selected_tags']))
821 array_push($_SESSION['selected_tags'], $tag);
828 * remove tag to users session variable
830 * this function removes the specified tag from
831 * users current tag selection
835 public function delTag($tag)
837 if(isset($_SESSION['searchfor_tag']))
838 unset($_SESSION['searchfor_tag']);
840 if(isset($_SESSION['selected_tags'])) {
841 $key = array_search($tag, $_SESSION['selected_tags']);
842 unset($_SESSION['selected_tags'][$key]);
843 sort($_SESSION['selected_tags']);
851 * reset tag selection
853 * if there is any tag selection, it will be
856 public function resetTags()
858 if(isset($_SESSION['selected_tags']))
859 unset($_SESSION['selected_tags']);
864 * returns the value for the autocomplet tag-search
867 public function get_xml_tag_list()
869 if(!isset($_GET['search']) || !is_string($_GET['search']))
870 $_GET['search'] = '';
875 /* retrive tags from database */
878 $matched_tags = Array();
880 header("Content-Type: text/xml");
882 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
883 $string.= "<results>\n";
885 foreach($this->avail_tags as $tag)
887 if(!empty($_GET['search']) &&
888 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
889 count($matched_tags) < $length) {
891 $count = $this->get_num_photos($tag);
894 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
897 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
903 /* if we have collected enough items, break out */
904 if(count($matched_tags) >= $length)
908 $string.= "</results>\n";
912 } // get_xml_tag_list()
918 * if a specific photo was requested (external link)
919 * unset the session variable now
921 public function resetPhotoView()
923 if(isset($_SESSION['current_photo']))
924 unset($_SESSION['current_photo']);
926 } // resetPhotoView();
931 * if any tag search has taken place, reset it now
933 public function resetTagSearch()
935 if(isset($_SESSION['searchfor_tag']))
936 unset($_SESSION['searchfor_tag']);
938 } // resetTagSearch()
943 * if any name search has taken place, reset it now
945 public function resetNameSearch()
947 if(isset($_SESSION['searchfor_name']))
948 unset($_SESSION['searchfor_name']);
950 } // resetNameSearch()
955 * if any date search has taken place, reset
958 public function resetDateSearch()
960 if(isset($_SESSION['from_date']))
961 unset($_SESSION['from_date']);
962 if(isset($_SESSION['to_date']))
963 unset($_SESSION['to_date']);
965 } // resetDateSearch();
968 * return all photo according selection
970 * this function returns all photos based on
971 * the tag-selection, tag- or date-search.
972 * the tag-search also has to take care of AND
973 * and OR conjunctions
976 public function getPhotoSelection()
978 $matched_photos = Array();
979 $additional_where_cond = "";
981 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
982 $from_date = $_SESSION['from_date'];
983 $to_date = $_SESSION['to_date'];
984 $additional_where_cond.= "
985 p.time>='". $from_date ."'
987 p.time<='". $to_date ."'
991 if(isset($_SESSION['searchfor_name'])) {
992 if($this->dbver < 9) {
993 $additional_where_cond.= "
995 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
997 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1002 $additional_where_cond.= "
1004 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1006 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1012 if(isset($_SESSION['sort_order'])) {
1013 $order_str = $this->get_sort_order();
1016 /* return a search result */
1017 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1019 SELECT DISTINCT pt1.photo_id
1021 INNER JOIN photo_tags pt2
1022 ON pt1.photo_id=pt2.photo_id
1026 ON pt1.photo_id=p.id
1029 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1031 if(isset($additional_where_cond) && !empty($additional_where_cond))
1032 $query_str.= "AND ". $additional_where_cond ." ";
1034 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1035 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1038 if(isset($order_str))
1039 $query_str.= $order_str;
1041 $result = $this->db->db_query($query_str);
1042 while($row = $this->db->db_fetch_object($result)) {
1043 array_push($matched_photos, $row['photo_id']);
1045 return $matched_photos;
1048 /* return according the selected tags */
1049 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1051 foreach($_SESSION['selected_tags'] as $tag)
1052 $selected.= $tag .",";
1053 $selected = substr($selected, 0, strlen($selected)-1);
1055 /* photo has to match at least on of the selected tags */
1056 if($_SESSION['tag_condition'] == 'or') {
1058 SELECT DISTINCT pt1.photo_id
1060 INNER JOIN photo_tags pt2
1061 ON pt1.photo_id=pt2.photo_id
1065 ON pt1.photo_id=p.id
1066 WHERE pt1.tag_id IN (". $selected .")
1068 if(isset($additional_where_cond) && !empty($additional_where_cond))
1069 $query_str.= "AND ". $additional_where_cond ." ";
1071 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1072 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1075 if(isset($order_str))
1076 $query_str.= $order_str;
1078 /* photo has to match all selected tags */
1079 elseif($_SESSION['tag_condition'] == 'and') {
1081 if(count($_SESSION['selected_tags']) >= 32) {
1082 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1083 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1087 /* Join together a table looking like
1089 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1091 so the query can quickly return all images matching the
1092 selected tags in an AND condition
1097 SELECT DISTINCT pt1.photo_id
1101 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1108 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1110 INNER JOIN photo_tags pt". ($i+2) ."
1111 ON pt1.photo_id=pt". ($i+2) .".photo_id
1116 ON pt1.photo_id=p.id
1118 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1119 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1121 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1124 if(isset($additional_where_cond) && !empty($additional_where_cond))
1125 $query_str.= "AND ". $additional_where_cond;
1127 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1128 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1131 if(isset($order_str))
1132 $query_str.= $order_str;
1136 $result = $this->db->db_query($query_str);
1137 while($row = $this->db->db_fetch_object($result)) {
1138 array_push($matched_photos, $row['photo_id']);
1140 return $matched_photos;
1143 /* return all available photos */
1145 SELECT DISTINCT p.id
1147 LEFT JOIN photo_tags pt
1153 if(isset($additional_where_cond) && !empty($additional_where_cond))
1154 $query_str.= "WHERE ". $additional_where_cond ." ";
1156 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1157 if(isset($additional_where_cond) && !empty($additional_where_cond))
1158 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1160 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1163 if(isset($order_str))
1164 $query_str.= $order_str;
1166 $result = $this->db->db_query($query_str);
1167 while($row = $this->db->db_fetch_object($result)) {
1168 array_push($matched_photos, $row['id']);
1170 return $matched_photos;
1172 } // getPhotoSelection()
1175 * control HTML ouput for photo index
1177 * this function provides all the necessary information
1178 * for the photo index template.
1180 public function showPhotoIndex()
1182 $photos = $this->getPhotoSelection();
1184 $count = count($photos);
1186 /* if all thumbnails should be shown on one page */
1187 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1191 /* thumbnails should be splitted up in several pages */
1192 elseif($this->cfg->thumbs_per_page > 0) {
1194 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1198 $begin_with = $_SESSION['begin_with'];
1201 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1205 $images[$thumbs] = Array();
1206 $img_height[$thumbs] = Array();
1207 $img_width[$thumbs] = Array();
1208 $img_id[$thumbs] = Array();
1209 $img_name[$thumbs] = Array();
1210 $img_fullname[$thumbs] = Array();
1211 $img_title = Array();
1213 for($i = $begin_with; $i < $end_with; $i++) {
1215 if(isset($photos[$i])) {
1217 $images[$thumbs] = $photos[$i];
1218 $img_id[$thumbs] = $i;
1219 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1220 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1221 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1223 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1225 if(file_exists($thumb_path)) {
1226 $info = getimagesize($thumb_path);
1227 $img_width[$thumbs] = $info[0];
1228 $img_height[$thumbs] = $info[1];
1234 // +1 for for smarty's selection iteration
1237 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1238 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1240 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1241 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1242 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1245 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1246 $this->tmpl->assign('tag_result', 1);
1249 /* do we have to display the page selector ? */
1250 if($this->cfg->thumbs_per_page != 0) {
1254 /* calculate the page switchers */
1255 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1256 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1258 if($begin_with != 0)
1259 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1260 if($end_with < $count)
1261 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1263 $photo_per_page = $this->cfg->thumbs_per_page;
1264 $last_page = ceil($count / $photo_per_page);
1266 /* get the current selected page */
1267 if($begin_with == 0) {
1271 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1278 for($i = 1; $i <= $last_page; $i++) {
1280 if($current_page == $i)
1281 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1282 elseif($current_page-1 == $i || $current_page+1 == $i)
1283 $style = "style=\"font-size: 105%;\"";
1284 elseif(($current_page-5 >= $i) && ($i != 1) ||
1285 ($current_page+5 <= $i) && ($i != $last_page))
1286 $style = "style=\"font-size: 75%;\"";
1290 $select = "<a href=\"javascript:showPhotoIndex(". (($i*$photo_per_page)-$photo_per_page) .");\"";
1293 $select.= ">". $i ."</a> ";
1295 // until 9 pages we show the selector from 1-9
1296 if($last_page <= 9) {
1297 $page_select.= $select;
1300 if($i == 1 /* first page */ ||
1301 $i == $last_page /* last page */ ||
1302 $i == $current_page /* current page */ ||
1303 $i == ceil($last_page * 0.25) /* first quater */ ||
1304 $i == ceil($last_page * 0.5) /* half */ ||
1305 $i == ceil($last_page * 0.75) /* third quater */ ||
1306 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1307 (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 */ ||
1308 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1309 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1311 $page_select.= $select;
1319 $page_select.= "......... ";
1324 /* only show the page selector if we have more then one page */
1326 $this->tmpl->assign('page_selector', $page_select);
1330 $current_tags = $this->getCurrentTags();
1331 $extern_link = "index.php?mode=showpi";
1332 $rss_link = "index.php?mode=rss";
1333 if($current_tags != "") {
1334 $extern_link.= "&tags=". $current_tags;
1335 $rss_link.= "&tags=". $current_tags;
1337 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1338 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1339 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1342 $export_link = "index.php?mode=export";
1343 $slideshow_link = "index.php?mode=slideshow";
1345 $this->tmpl->assign('extern_link', $extern_link);
1346 $this->tmpl->assign('slideshow_link', $slideshow_link);
1347 $this->tmpl->assign('export_link', $export_link);
1348 $this->tmpl->assign('rss_link', $rss_link);
1349 $this->tmpl->assign('count', $count);
1350 $this->tmpl->assign('width', $this->cfg->thumb_width);
1351 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1352 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1353 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1354 $this->tmpl->assign('images', $images);
1355 $this->tmpl->assign('img_width', $img_width);
1356 $this->tmpl->assign('img_height', $img_height);
1357 $this->tmpl->assign('img_id', $img_id);
1358 $this->tmpl->assign('img_name', $img_name);
1359 $this->tmpl->assign('img_fullname', $img_fullname);
1360 $this->tmpl->assign('img_title', $img_title);
1361 $this->tmpl->assign('thumbs', $thumbs);
1362 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1364 $result = $this->tmpl->fetch("photo_index.tpl");
1366 /* if we are returning to photo index from an photo-view,
1367 scroll the window to the last shown photo-thumbnail.
1368 after this, unset the last_photo session variable.
1370 if(isset($_SESSION['last_photo'])) {
1371 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1372 unset($_SESSION['last_photo']);
1377 } // showPhotoIndex()
1380 * show credit template
1382 public function showCredits()
1384 $this->tmpl->assign('version', $this->cfg->version);
1385 $this->tmpl->assign('product', $this->cfg->product);
1386 $this->tmpl->assign('db_version', $this->dbver);
1387 $this->tmpl->show("credits.tpl");
1392 * create thumbnails for the requested width
1394 * this function creates image thumbnails of $orig_image
1395 * stored as $thumb_image. It will check if the image is
1396 * in a supported format, if necessary rotate the image
1397 * (based on EXIF orientation meta headers) and re-sizing.
1398 * @param string $orig_image
1399 * @param string $thumb_image
1400 * @param integer $width
1403 public function create_thumbnail($orig_image, $thumb_image, $width)
1405 if(!file_exists($orig_image)) {
1409 $mime = $this->get_mime_info($orig_image);
1411 /* check if original photo is a support image type */
1412 if(!$this->checkifImageSupported($mime))
1419 $meta = $this->get_meta_informations($orig_image);
1425 switch($meta['Orientation']) {
1426 case 1: /* top, left */
1427 /* nothing to do */ break;
1428 case 2: /* top, right */
1429 $rotate = 0; $flip_hori = true; break;
1430 case 3: /* bottom, left */
1431 $rotate = 180; break;
1432 case 4: /* bottom, right */
1433 $flip_vert = true; break;
1434 case 5: /* left side, top */
1435 $rotate = 90; $flip_vert = true; break;
1436 case 6: /* right side, top */
1437 $rotate = 90; break;
1438 case 7: /* left side, bottom */
1439 $rotate = 270; $flip_vert = true; break;
1440 case 8: /* right side, bottom */
1441 $rotate = 270; break;
1444 $src_img = @imagecreatefromjpeg($orig_image);
1450 $src_img = @imagecreatefrompng($orig_image);
1454 case 'image/x-portable-pixmap':
1456 $src_img = new Imagick($orig_image);
1457 $handler = "imagick";
1462 if(!isset($src_img) || empty($src_img)) {
1463 print "Can't load image from ". $orig_image ."\n";
1471 /* grabs the height and width */
1472 $cur_width = imagesx($src_img);
1473 $cur_height = imagesy($src_img);
1475 // If requested width is more then the actual image width,
1476 // do not generate a thumbnail, instead safe the original
1477 // as thumbnail but with lower quality. But if the image
1478 // is to heigh too, then we still have to resize it.
1479 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1480 $result = imagejpeg($src_img, $thumb_image, 75);
1481 imagedestroy($src_img);
1488 $cur_width = $src_img->getImageWidth();
1489 $cur_height = $src_img->getImageHeight();
1491 // If requested width is more then the actual image width,
1492 // do not generate a thumbnail, instead safe the original
1493 // as thumbnail but with lower quality. But if the image
1494 // is to heigh too, then we still have to resize it.
1495 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1496 $src_img->setCompressionQuality(75);
1497 $src_img->setImageFormat('jpeg');
1498 $src_img->writeImage($thumb_image);
1500 $src_img->destroy();
1507 // If the image will be rotate because EXIF orientation said so
1508 // 'virtually rotate' the image for further calculations
1509 if($rotate == 90 || $rotate == 270) {
1511 $cur_width = $cur_height;
1515 /* calculates aspect ratio */
1516 $aspect_ratio = $cur_height / $cur_width;
1519 if($aspect_ratio < 1) {
1521 $new_h = abs($new_w * $aspect_ratio);
1523 /* 'virtually' rotate the image and calculate it's ratio */
1524 $tmp_w = $cur_height;
1525 $tmp_h = $cur_width;
1526 /* now get the ratio from the 'rotated' image */
1527 $tmp_ratio = $tmp_h/$tmp_w;
1528 /* now calculate the new dimensions */
1530 $tmp_h = abs($tmp_w * $tmp_ratio);
1532 // now that we know, how high they photo should be, if it
1533 // gets rotated, use this high to scale the image
1535 $new_w = abs($new_h / $aspect_ratio);
1537 // If the image will be rotate because EXIF orientation said so
1538 // now 'virtually rotate' back the image for the image manipulation
1539 if($rotate == 90 || $rotate == 270) {
1550 /* creates new image of that size */
1551 $dst_img = imagecreatetruecolor($new_w, $new_h);
1553 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1555 /* copies resized portion of original image into new image */
1556 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1558 /* needs the image to be flipped horizontal? */
1560 $this->_debug("(FLIP)");
1561 $dst_img = $this->flipImage($dst_img, 'hori');
1563 /* needs the image to be flipped vertical? */
1565 $this->_debug("(FLIP)");
1566 $dst_img = $this->flipImage($dst_img, 'vert');
1570 $this->_debug("(ROTATE)");
1571 $dst_img = $this->rotateImage($dst_img, $rotate);
1574 /* write down new generated file */
1575 $result = imagejpeg($dst_img, $thumb_image, 75);
1577 /* free your mind */
1578 imagedestroy($dst_img);
1579 imagedestroy($src_img);
1581 if($result === false) {
1582 print "Can't write thumbnail ". $thumb_image ."\n";
1592 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1594 /* needs the image to be flipped horizontal? */
1596 $this->_debug("(FLIP)");
1597 $src_img->rotateImage(new ImagickPixel(), 90);
1598 $src_img->flipImage();
1599 $src_img->rotateImage(new ImagickPixel(), -90);
1601 /* needs the image to be flipped vertical? */
1603 $this->_debug("(FLIP)");
1604 $src_img->flipImage();
1608 $this->_debug("(ROTATE)");
1609 $src_img->rotateImage(new ImagickPixel(), $rotate);
1612 $src_img->setCompressionQuality(75);
1613 $src_img->setImageFormat('jpeg');
1615 if(!$src_img->writeImage($thumb_image)) {
1616 print "Can't write thumbnail ". $thumb_image ."\n";
1621 $src_img->destroy();
1628 } // create_thumbnail()
1631 * return all exif meta data from the file
1632 * @param string $file
1635 public function get_meta_informations($file)
1637 return exif_read_data($file);
1639 } // get_meta_informations()
1642 * create phpfspot own sqlite database
1644 * this function creates phpfspots own sqlite database
1645 * if it does not exist yet. this own is used to store
1646 * some necessary informations (md5 sum's, ...).
1648 public function check_config_table()
1650 // if the config table doesn't exist yet, create it
1651 if(!$this->cfg_db->db_check_table_exists("images")) {
1652 $this->cfg_db->db_exec("
1653 CREATE TABLE images (
1654 img_idx int primary key,
1660 } // check_config_table
1663 * Generates a thumbnail from photo idx
1665 * This function will generate JPEG thumbnails from provided F-Spot photo
1668 * 1. Check if all thumbnail generations (width) are already in place and
1670 * 2. Check if the md5sum of the original file has changed
1671 * 3. Generate the thumbnails if needed
1672 * @param integer $idx
1673 * @param integer $force
1674 * @param boolean $overwrite
1676 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1680 $resolutions = Array(
1681 $this->cfg->thumb_width,
1682 $this->cfg->photo_width,
1683 $this->cfg->mini_width,
1687 /* get details from F-Spot's database */
1688 $details = $this->get_photo_details($idx);
1690 /* calculate file MD5 sum */
1691 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1693 if(!file_exists($full_path)) {
1694 $this->_error("File ". $full_path ." does not exist\n");
1698 if(!is_readable($full_path)) {
1699 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1703 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1705 /* If Nikon NEF format, we need to treat it another way */
1706 if(isset($this->cfg->dcraw_bin) &&
1707 file_exists($this->cfg->dcraw_bin) &&
1708 is_executable($this->cfg->dcraw_bin) &&
1709 preg_match('/\.nef$/i', $details['uri'])) {
1711 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1713 /* if PPM file does not exist, let dcraw convert it from NEF */
1714 if(!file_exists($ppm_path)) {
1715 system($this->cfg->dcraw_bin ." -a ". $full_path);
1718 /* for now we handle the PPM instead of the NEF */
1719 $full_path = $ppm_path;
1723 $file_md5 = md5_file($full_path);
1726 foreach($resolutions as $resolution) {
1728 $generate_it = false;
1730 $thumb_sub_path = substr($file_md5, 0, 2);
1731 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1733 /* if thumbnail-subdirectory does not exist yet, create it */
1734 if(!file_exists(dirname($thumb_path))) {
1735 mkdir(dirname($thumb_path), 0755);
1738 /* if the thumbnail file doesn't exist, create it */
1739 if(!file_exists($thumb_path)) {
1740 $generate_it = true;
1742 /* if the file hasn't changed there is no need to regen the thumb */
1743 elseif($file_md5 != $this->getMD5($idx) || $force) {
1744 $generate_it = true;
1747 if($generate_it || $overwrite) {
1749 $this->_debug(" ". $resolution ."px");
1750 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1758 $this->_debug(" already exist");
1761 /* set the new/changed MD5 sum for the current photo */
1763 $this->setMD5($idx, $file_md5);
1766 $this->_debug("\n");
1771 * returns stored md5 sum for a specific photo
1773 * this function queries the phpfspot database for a
1774 * stored MD5 checksum of the specified photo
1775 * @param integer $idx
1776 * @return string|null
1778 public function getMD5($idx)
1780 $result = $this->cfg_db->db_query("
1783 WHERE img_idx='". $idx ."'
1789 $img = $this->cfg_db->db_fetch_object($result);
1790 return $img['img_md5'];
1795 * set MD5 sum for the specific photo
1796 * @param integer $idx
1797 * @param string $md5
1799 private function setMD5($idx, $md5)
1801 $result = $this->cfg_db->db_exec("
1802 REPLACE INTO images (img_idx, img_md5)
1803 VALUES ('". $idx ."', '". $md5 ."')
1809 * store current tag condition
1811 * this function stores the current tag condition
1812 * (AND or OR) in the users session variables
1813 * @param string $mode
1816 public function setTagCondition($mode)
1818 $_SESSION['tag_condition'] = $mode;
1822 } // setTagCondition()
1825 * invoke tag & date search
1827 * this function will return all matching tags and store
1828 * them in the session variable selected_tags. furthermore
1829 * it also handles the date search.
1830 * getPhotoSelection() will then only return the matching
1834 public function startSearch()
1836 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
1837 $from = $_POST['from'];
1839 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
1843 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
1844 $searchfor_tag = $_POST['for_tag'];
1845 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
1848 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
1849 $searchfor_name = $_POST['for_name'];
1850 $_SESSION['searchfor_name'] = $_POST['for_name'];
1855 if(isset($from) && !empty($from))
1856 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
1858 unset($_SESSION['from_date']);
1860 if(isset($to) && !empty($to))
1861 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
1863 unset($_SESSION['to_date']);
1865 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
1866 /* new search, reset the current selected tags */
1867 $_SESSION['selected_tags'] = Array();
1868 foreach($this->avail_tags as $tag) {
1869 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
1870 array_push($_SESSION['selected_tags'], $tag);
1879 * updates sort order in session variable
1881 * this function is invoked by RPC and will sort the requested
1882 * sort order in the session variable.
1883 * @param string $sort_order
1886 public function updateSortOrder($order)
1888 if(isset($this->sort_orders[$order])) {
1889 $_SESSION['sort_order'] = $order;
1893 return "unkown error";
1895 } // updateSortOrder()
1900 * this function rotates the image according the
1902 * @param string $img
1903 * @param integer $degress
1906 private function rotateImage($img, $degrees)
1908 if(function_exists("imagerotate")) {
1909 $img = imagerotate($img, $degrees, 0);
1911 function imagerotate($src_img, $angle)
1913 $src_x = imagesx($src_img);
1914 $src_y = imagesy($src_img);
1915 if ($angle == 180) {
1919 elseif ($src_x <= $src_y) {
1923 elseif ($src_x >= $src_y) {
1928 $rotate=imagecreatetruecolor($dest_x,$dest_y);
1929 imagealphablending($rotate, false);
1934 for ($y = 0; $y < ($src_y); $y++) {
1935 for ($x = 0; $x < ($src_x); $x++) {
1936 $color = imagecolorat($src_img, $x, $y);
1937 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
1943 for ($y = 0; $y < ($src_y); $y++) {
1944 for ($x = 0; $x < ($src_x); $x++) {
1945 $color = imagecolorat($src_img, $x, $y);
1946 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
1952 for ($y = 0; $y < ($src_y); $y++) {
1953 for ($x = 0; $x < ($src_x); $x++) {
1954 $color = imagecolorat($src_img, $x, $y);
1955 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
1969 $img = imagerotate($img, $degrees);
1978 * returns flipped image
1980 * this function will return an either horizontal or
1981 * vertical flipped truecolor image.
1982 * @param string $image
1983 * @param string $mode
1986 private function flipImage($image, $mode)
1988 $w = imagesx($image);
1989 $h = imagesy($image);
1990 $flipped = imagecreatetruecolor($w, $h);
1994 for ($y = 0; $y < $h; $y++) {
1995 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
1999 for ($x = 0; $x < $w; $x++) {
2000 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2010 * return all assigned tags for the specified photo
2011 * @param integer $idx
2014 private function get_photo_tags($idx)
2016 $result = $this->db->db_query("
2019 INNER JOIN photo_tags pt
2021 WHERE pt.photo_id='". $idx ."'
2026 while($row = $this->db->db_fetch_object($result)) {
2027 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2029 $tags[$row['id']] = $row['name'];
2034 } // get_photo_tags()
2037 * create on-the-fly images with text within
2038 * @param string $txt
2039 * @param string $color
2040 * @param integer $space
2041 * @param integer $font
2044 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2046 if (strlen($color) != 6)
2049 $int = hexdec($color);
2050 $h = imagefontheight($font);
2051 $fw = imagefontwidth($font);
2052 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2053 $lines = count($txt);
2054 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2055 $bg = imagecolorallocate($im, 255, 255, 255);
2056 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2059 foreach ($txt as $text) {
2060 $x = (($w - ($fw * strlen($text))) / 2);
2061 imagestring($im, $font, $x, $y, $text, $color);
2062 $y += ($h + $space);
2065 Header("Content-type: image/png");
2068 } // showTextImage()
2071 * check if all requirements are met
2074 private function check_requirements()
2076 if(!function_exists("imagecreatefromjpeg")) {
2077 print "PHP GD library extension is missing<br />\n";
2081 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2082 print "PHP SQLite3 library extension is missing<br />\n";
2086 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2087 ini_set('track_errors', 1);
2088 @include_once 'HTML/AJAX/Server.php';
2089 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2090 print "PEAR HTML_AJAX package is missing<br />\n";
2093 @include_once 'Calendar/Calendar.php';
2094 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2095 print "PEAR Calendar package is missing<br />\n";
2098 @include_once 'Console/Getopt.php';
2099 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2100 print "PEAR Console_Getopt package is missing<br />\n";
2103 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2104 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2105 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2108 ini_restore('track_errors');
2115 } // check_requirements()
2117 private function _debug($text)
2119 if($this->fromcmd) {
2126 * check if specified MIME type is supported
2127 * @param string $mime
2130 public function checkifImageSupported($mime)
2132 $supported_types = Array(
2135 "image/x-portable-pixmap",
2139 if(in_array($mime, $supported_types))
2144 } // checkifImageSupported()
2148 * @param string $text
2150 public function _error($text)
2152 switch($this->cfg->logging) {
2155 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2156 print $text ."<br />\n";
2162 error_log($text, 3, $his->cfg->log_file);
2166 $this->runtime_error = true;
2171 * output calendard input fields
2172 * @param string $mode
2175 private function get_calendar($mode)
2177 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2178 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2179 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2181 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2182 if(!isset($_SESSION[$mode .'_date']))
2183 $output.= " disabled=\"disabled\"";
2185 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2186 if(!isset($_SESSION[$mode .'_date']))
2187 $output.= " disabled=\"disabled\"";
2189 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2190 if(!isset($_SESSION[$mode .'_date']))
2191 $output.= " disabled=\"disabled\"";
2199 * output calendar matrix
2200 * @param integer $year
2201 * @param integer $month
2202 * @param integer $day
2204 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2206 if (!isset($year)) $year = date('Y');
2207 if (!isset($month)) $month = date('m');
2208 if (!isset($day)) $day = date('d');
2213 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2214 require_once CALENDAR_ROOT.'Day.php';
2217 $month = new Calendar_Month_Weekdays($year,$month);
2220 $prevStamp = $month->prevMonth(true);
2221 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2222 $nextStamp = $month->nextMonth(true);
2223 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2225 $selectedDays = array (
2226 new Calendar_Day($year,$month,$day),
2227 new Calendar_Day($year,12,25),
2230 // Build the days in the month
2231 $month->build($selectedDays);
2233 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2234 $this->tmpl->assign('prev_month', $prev);
2235 $this->tmpl->assign('next_month', $next);
2237 while ( $day = $month->fetch() ) {
2239 if(!isset($matrix[$rows]))
2240 $matrix[$rows] = Array();
2244 $dayStamp = $day->thisDay(true);
2245 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2247 // isFirst() to find start of week
2248 if ( $day->isFirst() )
2251 if ( $day->isSelected() ) {
2252 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2253 } else if ( $day->isEmpty() ) {
2254 $string.= "<td> </td>\n";
2256 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2259 // isLast() to find end of week
2260 if ( $day->isLast() )
2261 $string.= "</tr>\n";
2263 $matrix[$rows][$cols] = $string;
2273 $this->tmpl->assign('matrix', $matrix);
2274 $this->tmpl->assign('rows', $rows);
2275 $this->tmpl->show("calendar.tpl");
2277 } // get_calendar_matrix()
2280 * output export page
2281 * @param string $mode
2283 public function getExport($mode)
2285 $pictures = $this->getPhotoSelection();
2286 $current_tags = $this->getCurrentTags();
2288 foreach($pictures as $picture) {
2290 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2291 if($current_tags != "") {
2292 $orig_url.= "&tags=". $current_tags;
2294 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2295 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2298 if($this->is_user_friendly_url()) {
2299 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2302 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2308 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2309 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2313 // "[%pictureurl% %thumbnailurl%]"
2314 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2317 case 'MoinMoinList':
2318 // " * [%pictureurl% %thumbnailurl%]"
2319 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2330 public function getRSSFeed()
2332 Header("Content-type: text/xml; charset=utf-8");
2333 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2336 xmlns:media="http://search.yahoo.com/mrss/"
2337 xmlns:dc="http://purl.org/dc/elements/1.1/"
2340 <title>phpfspot</title>
2341 <description>phpfspot RSS feed</description>
2342 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2343 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2344 <generator>phpfspot</generator>
2347 $pictures = $this->getPhotoSelection();
2348 $current_tags = $this->getCurrentTags();
2350 foreach($pictures as $picture) {
2352 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2353 if($current_tags != "") {
2354 $orig_url.= "&tags=". $current_tags;
2356 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2357 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2360 $details = $this->get_photo_details($picture);
2362 if($this->is_user_friendly_url()) {
2363 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2366 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2369 $thumb_html = htmlspecialchars("
2370 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2372 ". $details['description']);
2374 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2376 /* get EXIF information if JPEG */
2377 if($details['mime'] == "image/jpeg") {
2378 $meta = $this->get_meta_informations($orig_path);
2381 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2385 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2386 <link><?php print htmlspecialchars($orig_url); ?></link>
2387 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2388 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2390 <?php print $thumb_html; ?>
2392 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2407 * return all selected tags as one string
2410 private function getCurrentTags()
2413 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2414 foreach($_SESSION['selected_tags'] as $tag)
2415 $current_tags.= $tag .",";
2416 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2418 return $current_tags;
2420 } // getCurrentTags()
2423 * return the current photo
2425 public function getCurrentPhoto()
2427 if(isset($_SESSION['current_photo'])) {
2428 print $_SESSION['current_photo'];
2430 } // getCurrentPhoto()
2433 * tells the client browser what to do
2435 * this function is getting called via AJAX by the
2436 * client browsers. it will tell them what they have
2437 * to do next. This is necessary for directly jumping
2438 * into photo index or single photo view when the are
2439 * requested with specific URLs
2442 public function whatToDo()
2444 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2446 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2447 return "showpi_tags";
2449 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2456 * return the current process-user
2459 private function getuid()
2461 if($uid = posix_getuid()) {
2462 if($user = posix_getpwuid($uid)) {
2463 return $user['name'];
2472 * returns a select-dropdown box to select photo index sort parameters
2473 * @param array $params
2474 * @param smarty $smarty
2477 public function smarty_sort_select_list($params, &$smarty)
2481 foreach($this->sort_orders as $key => $value) {
2482 $output.= "<option value=\"". $key ."\"";
2483 if($key == $_SESSION['sort_order']) {
2484 $output.= " selected=\"selected\"";
2486 $output.= ">". $value ."</option>";
2491 } // smarty_sort_select_list()
2494 * returns the currently selected sort order
2497 private function get_sort_order()
2499 switch($_SESSION['sort_order']) {
2501 return " ORDER BY p.time ASC";
2504 return " ORDER BY p.time DESC";
2507 if($this->dbver < 9) {
2508 return " ORDER BY p.name ASC";
2511 return " ORDER BY basename(p.uri) ASC";
2515 if($this->dbver < 9) {
2516 return " ORDER BY p.name DESC";
2519 return " ORDER BY basename(p.uri) DESC";
2523 return " ORDER BY t.name ASC ,p.time ASC";
2526 return " ORDER BY t.name DESC ,p.time ASC";
2530 } // get_sort_order()
2533 * return the next to be shown slide show image
2535 * this function returns the URL of the next image
2536 * in the slideshow sequence.
2539 public function getNextSlideShowImage()
2541 $all_photos = $this->getPhotoSelection();
2543 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2544 $_SESSION['slideshow_img'] = 0;
2546 $_SESSION['slideshow_img']++;
2548 if($this->is_user_friendly_url()) {
2549 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2552 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2554 } // getNextSlideShowImage()
2557 * return the previous to be shown slide show image
2559 * this function returns the URL of the previous image
2560 * in the slideshow sequence.
2563 public function getPrevSlideShowImage()
2565 $all_photos = $this->getPhotoSelection();
2567 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2568 $_SESSION['slideshow_img'] = 0;
2570 $_SESSION['slideshow_img']--;
2572 if($this->is_user_friendly_url()) {
2573 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2576 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2578 } // getPrevSlideShowImage()
2580 public function resetSlideShow()
2582 if(isset($_SESSION['slideshow_img']))
2583 unset($_SESSION['slideshow_img']);
2585 } // resetSlideShow()
2590 * this function will get all photos from the fspot
2591 * database and randomly return ONE entry
2593 * saddly there is yet no sqlite3 function which returns
2594 * the bulk result in array, so we have to fill up our
2598 public function get_random_photo()
2607 /* if show_tags is set, only return details for photos which
2608 are specified to be shown
2610 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2612 INNER JOIN photo_tags pt
2617 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2620 $result = $this->db->db_query($query_str);
2622 while($row = $this->db->db_fetch_object($result)) {
2623 array_push($all, $row['id']);
2626 return $all[array_rand($all)];
2628 } // get_random_photo()
2631 * get random photo tag photo
2633 * this function will get all photos tagged with the requested
2634 * tag from the fspot database and randomly return ONE entry
2636 * saddly there is yet no sqlite3 function which returns
2637 * the bulk result in array, so we have to fill up our
2641 public function get_random_tag_photo($tagidx)
2648 INNER JOIN photo_tags pt
2652 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2660 pt.tag_id LIKE '". $tagidx ."'
2663 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2666 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2670 $result = $this->db->db_query($query_str);
2672 while($row = $this->db->db_fetch_object($result)) {
2673 array_push($all, $row['id']);
2676 return $all[array_rand($all)];
2678 } // get_random_tag_photo()
2681 * validates provided date
2683 * this function validates if the provided date
2684 * contains a valid date and will return true
2686 * @param string $date_str
2689 public function isValidDate($date_str)
2691 $timestamp = strtotime($date_str);
2693 if(is_numeric($timestamp))
2701 * timestamp to string conversion
2702 * @param integer $timestamp
2705 private function ts2str($timestamp)
2707 return strftime("%Y-%m-%d", $timestamp);
2711 * extract tag-names from $_GET['tags']
2712 * @param string $tags_str
2715 private function extractTags($tags_str)
2717 $not_validated = split(',', $tags_str);
2718 $validated = array();
2720 foreach($not_validated as $tag) {
2721 if(is_numeric($tag))
2722 array_push($validated, $tag);
2730 * returns the full path to a thumbnail
2731 * @param integer $width
2732 * @param integer $photo
2735 public function get_thumb_path($width, $photo)
2737 $md5 = $this->getMD5($photo);
2738 $sub_path = substr($md5, 0, 2);
2739 return $this->cfg->thumb_path
2747 } // get_thumb_path()
2750 * returns server's virtual host name
2753 private function get_server_name()
2755 return $_SERVER['SERVER_NAME'];
2756 } // get_server_name()
2759 * returns type of webprotocol which is currently used
2762 private function get_web_protocol()
2764 if(!isset($_SERVER['HTTPS']))
2768 } // get_web_protocol()
2771 * return url to this phpfspot installation
2774 private function get_phpfspot_url()
2776 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2778 } // get_phpfspot_url()
2781 * returns the number of photos which are tagged with $tag_id
2782 * @param integer $tag_id
2785 public function get_num_photos($tag_id)
2787 if($result = $this->db->db_fetchSingleRow("
2788 SELECT count(*) as number
2791 tag_id LIKE '". $tag_id ."'")) {
2793 return $result['number'];
2799 } // get_num_photos()
2802 * check file exists and is readable
2804 * returns true, if everything is ok, otherwise false
2805 * if $silent is not set, this function will output and
2807 * @param string $file
2808 * @param boolean $silent
2811 private function check_readable($file, $silent = null)
2813 if(!file_exists($file)) {
2815 print "File \"". $file ."\" does not exist.\n";
2819 if(!is_readable($file)) {
2821 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
2827 } // check_readable()
2830 * check if all needed indices are present
2832 * this function checks, if some needed indices are already
2833 * present, or if not, create them on the fly. they are
2834 * necessary to speed up some queries like that one look for
2835 * all tags, when show_tags is specified in the configuration.
2837 private function checkDbIndices()
2839 $result = $this->db->db_exec("
2840 CREATE INDEX IF NOT EXISTS
2847 } // checkDbIndices()
2850 * retrive F-Spot database version
2852 * this function will return the F-Spot database version number
2853 * It is stored within the sqlite3 database in the table meta
2854 * @return string|null
2856 public function getFspotDBVersion()
2858 if($result = $this->db->db_fetchSingleRow("
2859 SELECT data as version
2862 name LIKE 'F-Spot Database Version'
2864 return $result['version'];
2868 } // getFspotDBVersion()
2871 * parse the provided URI and will returned the requested chunk
2872 * @param string $uri
2873 * @param string $mode
2876 public function parse_uri($uri, $mode)
2878 if(($components = parse_url($uri)) !== false) {
2882 return basename($components['path']);
2885 return dirname($components['path']);
2888 return $components['path'];
2898 * validate config options
2900 * this function checks if all necessary configuration options are
2901 * specified and set.
2904 private function check_config_options()
2906 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
2907 $this->_error("Please set \$page_title in phpfspot_cfg");
2909 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
2910 $this->_error("Please set \$base_path in phpfspot_cfg");
2912 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
2913 $this->_error("Please set \$web_path in phpfspot_cfg");
2915 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
2916 $this->_error("Please set \$thumb_path in phpfspot_cfg");
2918 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
2919 $this->_error("Please set \$smarty_path in phpfspot_cfg");
2921 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
2922 $this->_error("Please set \$fspot_db in phpfspot_cfg");
2924 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
2925 $this->_error("Please set \$db_access in phpfspot_cfg");
2927 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
2928 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
2930 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
2931 $this->_error("Please set \$thumb_width in phpfspot_cfg");
2933 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
2934 $this->_error("Please set \$thumb_height in phpfspot_cfg");
2936 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
2937 $this->_error("Please set \$photo_width in phpfspot_cfg");
2939 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
2940 $this->_error("Please set \$mini_width in phpfspot_cfg");
2942 if(!isset($this->cfg->thumbs_per_page))
2943 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
2945 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
2946 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
2948 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
2949 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
2951 if(!isset($this->cfg->hide_tags))
2952 $this->_error("Please set \$hide_tags in phpfspot_cfg");
2954 if(!isset($this->cfg->theme_name))
2955 $this->_error("Please set \$theme_name in phpfspot_cfg");
2957 if(!isset($this->cfg->logging))
2958 $this->_error("Please set \$logging in phpfspot_cfg");
2960 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
2962 if(!isset($this->cfg->log_file))
2963 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
2965 if(!is_writeable($this->cfg->log_file))
2966 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
2970 /* remove trailing slash, if set */
2971 if($this->cfg->web_path == "/")
2972 $this->cfg->web_path = "";
2973 elseif(preg_match('/\/$/', $this->cfg->web_path))
2974 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
2976 return $this->runtime_error;
2978 } // check_config_options()
2981 * cleanup phpfspot own database
2983 * When photos are getting delete from F-Spot, there will remain
2984 * remain some residues in phpfspot own database. This function
2985 * will try to wipe them out.
2987 public function cleanup_phpfspot_db()
2989 $to_delete = Array();
2991 $result = $this->cfg_db->db_query("
2994 ORDER BY img_idx ASC
2997 while($row = $this->cfg_db->db_fetch_object($result)) {
2998 if(!$this->db->db_fetchSingleRow("
3001 WHERE id='". $row['img_idx'] ."'")) {
3003 array_push($to_delete, $row['img_idx'], ',');
3007 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3009 $this->cfg_db->db_exec("
3011 WHERE img_idx IN (". implode($to_delete) .")
3014 } // cleanup_phpfspot_db()
3017 * return first image of the page, the $current photo
3020 * this function is used to find out the first photo of the
3021 * current page, in which the $current photo lies. this is
3022 * used to display the correct photo, when calling showPhotoIndex()
3024 * @param integer $current
3025 * @param integer $max
3028 private function getCurrentPage($current, $max)
3030 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3031 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3032 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3038 } // getCurrentPage()
3043 * this function tries to find out the correct mime-type
3044 * for the provided file.
3045 * @param string $file
3048 public function get_mime_info($file)
3050 $details = getimagesize($file);
3052 /* if getimagesize() returns empty, try at least to find out the
3055 if(empty($details) && function_exists('mime_content_type')) {
3057 // mime_content_type is marked as deprecated in the documentation,
3058 // but is it really necessary to force users to install a PECL
3060 $details['mime'] = mime_content_type($file);
3063 return $details['mime'];
3065 } // get_mime_info()
3068 * return tag-name by tag-idx
3070 * this function returns the tag-name for the requested
3071 * tag specified by tag-idx.
3072 * @param integer $idx
3075 public function get_tag_name($idx)
3077 if($result = $this->db->db_fetchSingleRow("
3081 id LIKE '". $idx ."'")) {
3083 return $result['name'];
3092 * parse user friendly url which got rewritten by the websever
3093 * @param string $request_uri
3096 private function parse_user_friendly_url($request_uri)
3098 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3100 unset($_SESSION['start_action']);
3101 unset($_SESSION['selected_tags']);
3103 $options = explode('/', $request_uri);
3105 switch($options[1]) {
3107 if(is_numeric($options[2])) {
3108 $_GET['mode'] = 'showp';
3109 return $this->showPhoto($options[2]);
3113 if(is_numeric($options[2])) {
3114 require_once "phpfspot_img.php";
3115 $img = new PHPFSPOT_IMG;
3116 if(isset($options[3]) && is_numeric($options[3]))
3117 $img->showImg($options[2], $options[3]);
3119 $img->showImg($options[2]);
3124 if(is_numeric($options[2])) {
3125 $_GET['mode'] = 'showpi';
3126 $_SESSION['selected_tags'] = Array($options[2]);
3127 $_GET['tags'] = $options[2];
3128 return $this->showPhotoIndex();
3134 } // parse_user_friendly_url()
3137 * check if user-friendly-urls are enabled
3139 * this function will return true, if the config option
3140 * $user_friendly_url has been set. Otherwise false.
3143 private function is_user_friendly_url()
3145 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3150 } // is_user_friendly_url()