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 /* get F-Spot database version */
161 $this->dbver = $this->getFspotDBVersion();
163 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
164 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
168 if(!is_writeable($this->cfg->thumb_path)) {
169 print $this->cfg->thumb_path .": directory is not writeable for user ". $this->getuid() ."\n";
173 /******* Opening phpfspot's sqlite database *********/
175 /* Check if directory where the database file is stored is writeable */
176 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
177 print dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() ."\n";
181 /* Check if database file is writeable */
182 if(!is_writeable($this->cfg->phpfspot_db)) {
183 print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
187 /* open the database */
188 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
190 /* change sqlite temp directory, if requested */
191 if(isset($this->cfg->sqlite_temp_dir)) {
192 $this->cfg_db->db_exec("
194 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
198 /* Check if some tables need to be created */
199 $this->check_config_table();
201 /* overload Smarty class with our own template handler */
202 require_once "phpfspot_tmpl.php";
203 $this->tmpl = new PHPFSPOT_TMPL();
205 $this->tmpl->assign('web_path', $this->cfg->web_path);
207 /* Starting with F-Spot 0.4.2, the rating-feature was available */
208 if($this->dbver > 10) {
209 $this->tmpl->assign('has_rating', true);
210 $this->sort_orders = array_merge($this->sort_orders, array(
211 'rate_asc' => 'Rate ↑',
212 'rate_desc' => 'Rate ↓',
216 /* check if all necessary indices exist */
217 $this->checkDbIndices();
219 /* if session is not yet started, do it now */
220 if(session_id() == "")
223 if(!isset($_SESSION['tag_condition']))
224 $_SESSION['tag_condition'] = 'or';
226 if(!isset($_SESSION['sort_order']))
227 $_SESSION['sort_order'] = 'date_desc';
229 if(!isset($_SESSION['searchfor_tag']))
230 $_SESSION['searchfor_tag'] = '';
232 // if begin_with is still set but thumbs_per_page is now 0, unset it
233 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
234 unset($_SESSION['begin_with']);
236 // if user-friendly-url's are enabled, set also a flag for the template handler
237 if($this->is_user_friendly_url()) {
238 $this->tmpl->assign('user_friendly_url', 'true');
243 public function __destruct()
249 * show - generate html output
251 * this function can be called after the constructor has
252 * prepared everyhing. it will load the index.tpl smarty
253 * template. if necessary it will registere pre-selects
254 * (photo index, photo, tag search, date search) into
257 public function show()
259 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
260 $this->tmpl->assign('page_title', $this->cfg->page_title);
261 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
262 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
265 if($this->is_user_friendly_url()) {
266 $content = $this->parse_user_friendly_url($_SERVER['REQUEST_URI']);
269 if(isset($_GET['mode'])) {
271 $_SESSION['start_action'] = $_GET['mode'];
273 switch($_GET['mode']) {
275 if(isset($_GET['tags'])) {
276 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
278 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
279 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
281 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
282 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
286 if(isset($_GET['tags'])) {
287 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
288 $_SESSION['start_action'] = 'showp';
290 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
291 $_SESSION['current_photo'] = $_GET['id'];
292 $_SESSION['start_action'] = 'showp';
294 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
295 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
297 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
298 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
302 /* fetch export template */
303 print $this->tmpl->fetch("export.tpl");
304 /* no further execution necessary. */
308 /* fetch slideshow template */
309 print $this->tmpl->show("slideshow.tpl");
310 /* no further execution necessary. */
314 if(isset($_GET['tags'])) {
315 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
317 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
318 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
320 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
321 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
329 /* if date-search variables are registered in the session, set the check
330 for "consider date-range" in the html output
332 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
333 $this->tmpl->assign('date_search_enabled', true);
335 /* if rate-search variables are registered in the session, set the check
336 for "consider rate-range" in the html output
338 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
339 $this->tmpl->assign('rate_search_enabled', true);
342 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
343 $this->tmpl->assign('search_from_date', $this->get_calendar('from'));
344 $this->tmpl->assign('search_to_date', $this->get_calendar('to'));
346 $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags());
347 $this->tmpl->assign('preset_available_tags', $this->getAvailableTags());
348 $this->tmpl->assign('rate_search', $this->get_rate_search());
350 /* if no site-content has been set yet... */
351 if(!isset($content)) {
352 /* if tags are already selected, we can immediately display photo-index */
353 if((isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']) &&
354 isset($_SESSION['start_action']) && $_SESSION['start_action'] != 'showp') ||
355 (isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi'))
356 $this->tmpl->assign('initial_content', $this->showPhotoIndex());
358 /* if a photo is already selected, we can immediately display single-photo */
359 if(isset($_SESSION['current_photo']) && !empty($_SESSION['current_photo']))
360 $this->tmpl->assign('initial_content', $this->showPhoto($_SESSION['current_photo']));
362 /* ok, then let us show the welcome page... */
363 $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl'));
368 $this->tmpl->assign('initial_content', $content);
370 $this->tmpl->show("index.tpl");
375 * get_tags - grab all tags of f-spot's database
377 * this function will get all available tags from
378 * the f-spot database and store them within two
379 * arrays within this class for later usage. in
380 * fact, if the user requests (hide_tags) it will
381 * opt-out some of them.
383 * this function is getting called once by show()
385 private function get_tags()
387 $this->avail_tags = Array();
390 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
393 DISTINCT t1.id as id, t1.name as name
396 INNER JOIN photo_tags
397 pt2 ON pt1.photo_id=pt2.photo_id
403 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
405 t1.sort_priority ASC";
407 $result = $this->db->db_query($query_str);
411 $result = $this->db->db_query("
414 ORDER BY sort_priority ASC
418 while($row = $this->db->db_fetch_object($result)) {
420 $tag_id = $row['id'];
421 $tag_name = $row['name'];
423 /* if the user has specified to ignore this tag in phpfspot's
424 configuration, ignore it here so it does not get added to
427 if(in_array($row['name'], $this->cfg->hide_tags))
430 /* if you include the following if-clause and the user has specified
431 to only show certain tags which are specified in phpfspot's
432 configuration, ignore all others so they will not be added to the
434 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
435 !in_array($row['name'], $this->cfg->show_tags))
439 $this->tags[$tag_id] = $tag_name;
440 $this->avail_tags[$count] = $tag_id;
448 * extract all photo details
450 * retrieve all available details from f-spot's
451 * database and return them as object
452 * @param integer $idx
453 * @return object|null
455 public function get_photo_details($idx)
457 if($this->dbver < 9) {
459 SELECT p.id, p.name, p.time, p.directory_path, p.description
464 /* till F-Spot version 0.4.1 */
465 if($this->dbver < 11) {
467 SELECT p.id, p.uri, p.time, p.description
473 SELECT p.id, p.uri, p.time, p.description, p.rating
479 /* if show_tags is set, only return details for photos which
480 are specified to be shown
482 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
484 INNER JOIN photo_tags pt
488 WHERE p.id='". $idx ."'
489 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
493 WHERE p.id='". $idx ."'
497 if($result = $this->db->db_query($query_str)) {
499 $row = $this->db->db_fetch_object($result);
501 if($this->dbver < 9) {
502 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
511 } // get_photo_details
514 * returns aligned photo names
516 * this function returns aligned (length) names for
517 * an specific photo. If the length of the name exceeds
518 * $limit the name will be shrinked (...)
519 * @param integer $idx
520 * @param integer $limit
521 * @return string|null
523 public function getPhotoName($idx, $limit = 0)
525 if($details = $this->get_photo_details($idx)) {
526 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
527 $name = $this->shrink_text($long_name, $limit);
537 * get photo rating level
539 * this function will return the integer-based rating
540 * level of the photo. This can only be done, if the F-Spot
541 * database is at a specific level. If rating value can not
542 * be found, zero will be returned indicating no rating value
547 public function get_photo_rating($idx)
549 if($detail = $this->get_photo_details($idx)) {
550 if(isset($detail['rating']))
551 return $detail['rating'];
556 } // get_photo_rating()
559 * get rate-search bars
561 * this function will return the rating-bars for the
565 public function get_rate_search()
569 for($i = 1; $i <= 5; $i++) {
571 $bar.= "<img id=\"rate_from_". $i ."\" src=\"";
573 if(isset($_SESSION['rate_from']) && $i <= $_SESSION['rate_from'])
574 $bar.= $this->cfg->web_path ."/resources/star.png";
576 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
579 onmouseover=\"show_rate('from', ". $i .");\"
580 onmouseout=\"reset_rate('from');\"
581 onclick=\"set_rate('from', ". $i .")\" />";
586 for($i = 1; $i <= 5; $i++) {
588 $bar.= "<img id=\"rate_to_". $i ."\" src=\"";
590 if(isset($_SESSION['rate_to']) && $i <= $_SESSION['rate_to'])
591 $bar.= $this->cfg->web_path ."/resources/star.png";
593 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
596 onmouseover=\"show_rate('to', ". $i .");\"
597 onmouseout=\"reset_rate('to');\"
598 onclick=\"set_rate('to', ". $i .");\" />";
603 } // get_rate_search()
606 * shrink text according provided limit
608 * If the length of the name exceeds $limit the
609 * text will be shortend and some content in between
610 * will be replaced with "..."
612 * @param integer $limit
615 private function shrink_text($text, $limit)
617 if($limit != 0 && strlen($text) > $limit) {
618 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
626 * translate f-spoth photo path
628 * as the full-qualified path recorded in the f-spot database
629 * is usally not the same as on the webserver, this function
630 * will replace the path with that one specified in the cfg
631 * @param string $path
634 public function translate_path($path)
636 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
641 * control HTML ouput for a single photo
643 * this function provides all the necessary information
644 * for the single photo template.
645 * @param integer photo
647 public function showPhoto($photo)
649 /* get all photos from the current photo selection */
650 $all_photos = $this->getPhotoSelection();
651 $count = count($all_photos);
653 for($i = 0; $i < $count; $i++) {
655 // $get_next will be set, when the photo which has to
656 // be displayed has been found - this means that the
657 // next available is in fact the NEXT image (for the
659 if(isset($get_next)) {
660 $next_img = $all_photos[$i];
664 /* the next photo is our NEXT photo */
665 if($all_photos[$i] == $photo) {
669 $previous_img = $all_photos[$i];
672 if($photo == $all_photos[$i]) {
677 $details = $this->get_photo_details($photo);
684 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
685 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
687 if(!file_exists($orig_path)) {
688 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
692 if(!is_readable($orig_path)) {
693 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
697 /* If the thumbnail doesn't exist yet, try to create it */
698 if(!file_exists($thumb_path)) {
699 $this->gen_thumb($photo, true);
700 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
703 /* get mime-type, height and width from the original photo */
704 $info = getimagesize($orig_path);
706 /* get EXIF information if JPEG */
707 if(isset($info['mime']) && $info['mime'] == "image/jpeg") {
708 $meta = $this->get_meta_informations($orig_path);
711 /* If EXIF data are available, use them */
712 if(isset($meta['ExifImageWidth'])) {
713 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
715 $meta_res = $info[0] ."x". $info[1];
718 $meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
719 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
720 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
722 $extern_link = "index.php?mode=showp&id=". $photo;
723 $current_tags = $this->getCurrentTags();
724 if($current_tags != "") {
725 $extern_link.= "&tags=". $current_tags;
727 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
728 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
731 $this->tmpl->assign('extern_link', $extern_link);
733 if(!file_exists($thumb_path)) {
734 $this->_error("Can't open file ". $thumb_path ."\n");
738 $info_thumb = getimagesize($thumb_path);
740 $this->tmpl->assign('description', $details['description']);
741 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
742 $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
744 $this->tmpl->assign('width', $info_thumb[0]);
745 $this->tmpl->assign('height', $info_thumb[1]);
746 $this->tmpl->assign('ExifMadeOn', $meta_date);
747 $this->tmpl->assign('ExifMadeWith', $meta_make);
748 $this->tmpl->assign('ExifOrigResolution', $meta_res);
749 $this->tmpl->assign('ExifFileSize', $meta_size);
751 if($this->is_user_friendly_url()) {
752 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
753 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
756 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
757 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
760 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
762 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
763 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
764 $this->tmpl->assign('current_img', $photo);
766 if(isset($previous_img)) {
767 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
768 $this->tmpl->assign('prev_img', $previous_img);
771 if(isset($next_img)) {
772 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
773 $this->tmpl->assign('next_img', $next_img);
776 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
777 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
778 $this->tmpl->assign('photo_number', $i);
779 $this->tmpl->assign('photo_count', count($all_photos));
781 return $this->tmpl->fetch("single_photo.tpl");
786 * all available tags and tag cloud
788 * this function outputs all available tags (time ordered)
789 * and in addition output them as tag cloud (tags which have
790 * many photos will appears more then others)
792 public function getAvailableTags()
794 /* retrive tags from database */
799 $result = $this->db->db_query("
800 SELECT tag_id as id, count(tag_id) as quantity
810 while($row = $this->db->db_fetch_object($result)) {
811 $tags[$row['id']] = $row['quantity'];
814 // change these font sizes if you will
815 $max_size = 125; // max font size in %
816 $min_size = 75; // min font size in %
819 $max_sat = hexdec('cc');
820 $min_sat = hexdec('44');
822 // get the largest and smallest array values
823 $max_qty = max(array_values($tags));
824 $min_qty = min(array_values($tags));
826 // find the range of values
827 $spread = $max_qty - $min_qty;
828 if (0 == $spread) { // we don't want to divide by zero
832 // determine the font-size increment
833 // this is the increase per tag quantity (times used)
834 $step = ($max_size - $min_size)/($spread);
835 $step_sat = ($max_sat - $min_sat)/($spread);
837 // loop through our tag array
838 foreach ($tags as $key => $value) {
840 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
843 // calculate CSS font-size
844 // find the $value in excess of $min_qty
845 // multiply by the font-size increment ($size)
846 // and add the $min_size set above
847 $size = $min_size + (($value - $min_qty) * $step);
848 // uncomment if you want sizes in whole %:
851 $color = $min_sat + ($value - $min_qty) * $step_sat;
857 if(isset($this->tags[$key])) {
858 if($this->is_user_friendly_url())
859 $output.= "<a href=\"". $this->cfg->web_path ."/tag/". $key ."\" onclick=\"Tags('add', ". $key ."); return false;\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
861 $output.= "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi\" onclick=\"Tags('add', ". $key ."); return false;\" class=\"tag\" style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\">". $this->tags[$key] ."</a>, ";
865 $output = substr($output, 0, strlen($output)-2);
868 } // getAvailableTags()
871 * output all selected tags
873 * this function output all tags which have been selected
874 * by the user. the selected tags are stored in the
875 * session-variable $_SESSION['selected_tags']
878 public function getSelectedTags($type = 'link')
880 /* retrive tags from database */
885 foreach($this->avail_tags as $tag)
887 // return all selected tags
888 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
893 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
897 <div class=\"tagresulttag\">
898 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
899 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
909 $output = substr($output, 0, strlen($output)-2);
913 return "no tags selected";
916 } // getSelectedTags()
919 * add tag to users session variable
921 * this function will add the specified to users current
922 * tag selection. if a date search has been made before
923 * it will be now cleared
926 public function addTag($tag)
928 if(!isset($_SESSION['selected_tags']))
929 $_SESSION['selected_tags'] = Array();
931 if(isset($_SESSION['searchfor_tag']))
932 unset($_SESSION['searchfor_tag']);
934 // has the user requested to hide this tag, and still someone,
935 // somehow tries to add it, don't allow this.
936 if(!isset($this->cfg->hide_tags) &&
937 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
940 if(!in_array($tag, $_SESSION['selected_tags']))
941 array_push($_SESSION['selected_tags'], $tag);
948 * remove tag to users session variable
950 * this function removes the specified tag from
951 * users current tag selection
955 public function delTag($tag)
957 if(isset($_SESSION['searchfor_tag']))
958 unset($_SESSION['searchfor_tag']);
960 if(isset($_SESSION['selected_tags'])) {
961 $key = array_search($tag, $_SESSION['selected_tags']);
962 unset($_SESSION['selected_tags'][$key]);
963 sort($_SESSION['selected_tags']);
971 * reset tag selection
973 * if there is any tag selection, it will be
976 public function resetTags()
978 if(isset($_SESSION['selected_tags']))
979 unset($_SESSION['selected_tags']);
984 * returns the value for the autocomplete tag-search
987 public function get_xml_tag_list()
989 if(!isset($_GET['search']) || !is_string($_GET['search']))
990 $_GET['search'] = '';
995 /* retrive tags from database */
998 $matched_tags = Array();
1000 header("Content-Type: text/xml");
1002 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
1003 $string.= "<results>\n";
1005 foreach($this->avail_tags as $tag)
1007 if(!empty($_GET['search']) &&
1008 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
1009 count($matched_tags) < $length) {
1011 $count = $this->get_num_photos($tag);
1014 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1017 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1023 /* if we have collected enough items, break out */
1024 if(count($matched_tags) >= $length)
1028 $string.= "</results>\n";
1032 } // get_xml_tag_list()
1036 * reset single photo
1038 * if a specific photo was requested (external link)
1039 * unset the session variable now
1041 public function resetPhotoView()
1043 if(isset($_SESSION['current_photo']))
1044 unset($_SESSION['current_photo']);
1046 } // resetPhotoView();
1051 * if any tag search has taken place, reset it now
1053 public function resetTagSearch()
1055 if(isset($_SESSION['searchfor_tag']))
1056 unset($_SESSION['searchfor_tag']);
1058 } // resetTagSearch()
1063 * if any name search has taken place, reset it now
1065 public function resetNameSearch()
1067 if(isset($_SESSION['searchfor_name']))
1068 unset($_SESSION['searchfor_name']);
1070 } // resetNameSearch()
1075 * if any date search has taken place, reset it now.
1077 public function resetDateSearch()
1079 if(isset($_SESSION['from_date']))
1080 unset($_SESSION['from_date']);
1081 if(isset($_SESSION['to_date']))
1082 unset($_SESSION['to_date']);
1084 } // resetDateSearch();
1089 * if any rate search has taken place, reset it now.
1091 public function resetRateSearch()
1093 if(isset($_SESSION['rate_from']))
1094 unset($_SESSION['rate_from']);
1095 if(isset($_SESSION['rate_to']))
1096 unset($_SESSION['rate_to']);
1098 } // resetRateSearch();
1101 * return all photo according selection
1103 * this function returns all photos based on
1104 * the tag-selection, tag- or date-search.
1105 * the tag-search also has to take care of AND
1106 * and OR conjunctions
1109 public function getPhotoSelection()
1111 $matched_photos = Array();
1112 $additional_where_cond = "";
1114 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1115 $from_date = $_SESSION['from_date'];
1116 $to_date = $_SESSION['to_date'];
1117 $additional_where_cond.= "
1118 p.time>='". $from_date ."'
1120 p.time<='". $to_date ."'
1124 if(isset($_SESSION['searchfor_name'])) {
1126 /* check for previous conditions. if so add 'AND' */
1127 if(!empty($additional_where_cond)) {
1128 $additional_where_cond.= " AND ";
1131 if($this->dbver < 9) {
1132 $additional_where_cond.= "
1134 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1136 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1141 $additional_where_cond.= "
1143 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1145 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1151 /* limit result based on rate-search */
1152 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1154 if($this->dbver > 10) {
1156 /* check for previous conditions. if so add 'AND' */
1157 if(!empty($additional_where_cond)) {
1158 $additional_where_cond.= " AND ";
1161 $additional_where_cond.= "
1162 p.rating >= ". $_SESSION['rate_from'] ."
1164 p.rating <= ". $_SESSION['rate_to'] ."
1169 if(isset($_SESSION['sort_order'])) {
1170 $order_str = $this->get_sort_order();
1173 /* return a search result */
1174 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1176 SELECT DISTINCT pt1.photo_id
1178 INNER JOIN photo_tags pt2
1179 ON pt1.photo_id=pt2.photo_id
1183 ON pt1.photo_id=p.id
1186 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1188 if(!empty($additional_where_cond))
1189 $query_str.= "AND ". $additional_where_cond ." ";
1191 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1192 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1195 if(isset($order_str))
1196 $query_str.= $order_str;
1198 $result = $this->db->db_query($query_str);
1199 while($row = $this->db->db_fetch_object($result)) {
1200 array_push($matched_photos, $row['photo_id']);
1202 return $matched_photos;
1205 /* return according the selected tags */
1206 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1208 foreach($_SESSION['selected_tags'] as $tag)
1209 $selected.= $tag .",";
1210 $selected = substr($selected, 0, strlen($selected)-1);
1212 /* photo has to match at least on of the selected tags */
1213 if($_SESSION['tag_condition'] == 'or') {
1215 SELECT DISTINCT pt1.photo_id
1217 INNER JOIN photo_tags pt2
1218 ON pt1.photo_id=pt2.photo_id
1222 ON pt1.photo_id=p.id
1223 WHERE pt1.tag_id IN (". $selected .")
1225 if(!empty($additional_where_cond))
1226 $query_str.= "AND ". $additional_where_cond ." ";
1228 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1229 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1232 if(isset($order_str))
1233 $query_str.= $order_str;
1235 /* photo has to match all selected tags */
1236 elseif($_SESSION['tag_condition'] == 'and') {
1238 if(count($_SESSION['selected_tags']) >= 32) {
1239 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1240 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1244 /* Join together a table looking like
1246 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1248 so the query can quickly return all images matching the
1249 selected tags in an AND condition
1254 SELECT DISTINCT pt1.photo_id
1258 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1265 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1267 INNER JOIN photo_tags pt". ($i+2) ."
1268 ON pt1.photo_id=pt". ($i+2) .".photo_id
1273 ON pt1.photo_id=p.id
1275 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1276 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1278 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1281 if(!empty($additional_where_cond))
1282 $query_str.= "AND ". $additional_where_cond;
1284 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1285 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1288 if(isset($order_str))
1289 $query_str.= $order_str;
1293 $result = $this->db->db_query($query_str);
1294 while($row = $this->db->db_fetch_object($result)) {
1295 array_push($matched_photos, $row['photo_id']);
1297 return $matched_photos;
1300 /* return all available photos */
1302 SELECT DISTINCT p.id
1304 LEFT JOIN photo_tags pt
1310 if(!empty($additional_where_cond))
1311 $query_str.= "WHERE ". $additional_where_cond ." ";
1313 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1314 if(!empty($additional_where_cond))
1315 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1317 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1320 if(isset($order_str))
1321 $query_str.= $order_str;
1323 $result = $this->db->db_query($query_str);
1324 while($row = $this->db->db_fetch_object($result)) {
1325 array_push($matched_photos, $row['id']);
1327 return $matched_photos;
1329 } // getPhotoSelection()
1332 * control HTML ouput for photo index
1334 * this function provides all the necessary information
1335 * for the photo index template.
1338 public function showPhotoIndex()
1340 $photos = $this->getPhotoSelection();
1341 $current_tags = $this->getCurrentTags();
1343 $count = count($photos);
1345 /* if all thumbnails should be shown on one page */
1346 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1350 /* thumbnails should be splitted up in several pages */
1351 elseif($this->cfg->thumbs_per_page > 0) {
1353 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1357 $begin_with = $_SESSION['begin_with'];
1360 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1364 $images[$thumbs] = Array();
1365 $img_height[$thumbs] = Array();
1366 $img_width[$thumbs] = Array();
1367 $img_id[$thumbs] = Array();
1368 $img_name[$thumbs] = Array();
1369 $img_fullname[$thumbs] = Array();
1370 $img_title = Array();
1371 $img_rating = Array();
1373 for($i = $begin_with; $i < $end_with; $i++) {
1375 if(isset($photos[$i])) {
1377 $images[$thumbs] = $photos[$i];
1378 $img_id[$thumbs] = $i;
1379 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1380 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1381 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1382 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1384 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1386 if(file_exists($thumb_path)) {
1387 $info = getimagesize($thumb_path);
1388 $img_width[$thumbs] = $info[0];
1389 $img_height[$thumbs] = $info[1];
1395 // +1 for for smarty's selection iteration
1398 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1399 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1401 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1402 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1403 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1406 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1407 $this->tmpl->assign('tag_result', 1);
1410 /* do we have to display the page selector ? */
1411 if($this->cfg->thumbs_per_page != 0) {
1415 /* calculate the page switchers */
1416 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1417 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1419 if($begin_with != 0)
1420 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1421 if($end_with < $count)
1422 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1424 $photo_per_page = $this->cfg->thumbs_per_page;
1425 $last_page = ceil($count / $photo_per_page);
1427 /* get the current selected page */
1428 if($begin_with == 0) {
1432 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1439 for($i = 1; $i <= $last_page; $i++) {
1441 if($current_page == $i)
1442 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1443 elseif($current_page-1 == $i || $current_page+1 == $i)
1444 $style = "style=\"font-size: 105%;\"";
1445 elseif(($current_page-5 >= $i) && ($i != 1) ||
1446 ($current_page+5 <= $i) && ($i != $last_page))
1447 $style = "style=\"font-size: 75%;\"";
1451 $start_with = ($i*$photo_per_page)-$photo_per_page;
1453 if($this->is_user_friendly_url()) {
1454 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1457 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1459 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1463 $select.= ">". $i ."</a> ";
1465 // until 9 pages we show the selector from 1-9
1466 if($last_page <= 9) {
1467 $page_select.= $select;
1470 if($i == 1 /* first page */ ||
1471 $i == $last_page /* last page */ ||
1472 $i == $current_page /* current page */ ||
1473 $i == ceil($last_page * 0.25) /* first quater */ ||
1474 $i == ceil($last_page * 0.5) /* half */ ||
1475 $i == ceil($last_page * 0.75) /* third quater */ ||
1476 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1477 (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 */ ||
1478 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1479 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1481 $page_select.= $select;
1489 $page_select.= "......... ";
1494 /* only show the page selector if we have more then one page */
1496 $this->tmpl->assign('page_selector', $page_select);
1499 $extern_link = "index.php?mode=showpi";
1500 $rss_link = "index.php?mode=rss";
1501 if($current_tags != "") {
1502 $extern_link.= "&tags=". $current_tags;
1503 $rss_link.= "&tags=". $current_tags;
1505 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1506 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1507 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1510 $export_link = "index.php?mode=export";
1511 $slideshow_link = "index.php?mode=slideshow";
1513 $this->tmpl->assign('extern_link', $extern_link);
1514 $this->tmpl->assign('slideshow_link', $slideshow_link);
1515 $this->tmpl->assign('export_link', $export_link);
1516 $this->tmpl->assign('rss_link', $rss_link);
1517 $this->tmpl->assign('count', $count);
1518 $this->tmpl->assign('width', $this->cfg->thumb_width);
1519 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1520 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1521 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1522 $this->tmpl->assign('images', $images);
1523 $this->tmpl->assign('img_width', $img_width);
1524 $this->tmpl->assign('img_height', $img_height);
1525 $this->tmpl->assign('img_id', $img_id);
1526 $this->tmpl->assign('img_name', $img_name);
1527 $this->tmpl->assign('img_fullname', $img_fullname);
1528 $this->tmpl->assign('img_title', $img_title);
1529 $this->tmpl->assign('img_rating', $img_rating);
1530 $this->tmpl->assign('thumbs', $thumbs);
1531 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1533 $result = $this->tmpl->fetch("photo_index.tpl");
1535 /* if we are returning to photo index from an photo-view,
1536 scroll the window to the last shown photo-thumbnail.
1537 after this, unset the last_photo session variable.
1539 if(isset($_SESSION['last_photo'])) {
1540 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1541 unset($_SESSION['last_photo']);
1546 } // showPhotoIndex()
1549 * show credit template
1551 public function showCredits()
1553 $this->tmpl->assign('version', $this->cfg->version);
1554 $this->tmpl->assign('product', $this->cfg->product);
1555 $this->tmpl->assign('db_version', $this->dbver);
1556 $this->tmpl->show("credits.tpl");
1561 * create thumbnails for the requested width
1563 * this function creates image thumbnails of $orig_image
1564 * stored as $thumb_image. It will check if the image is
1565 * in a supported format, if necessary rotate the image
1566 * (based on EXIF orientation meta headers) and re-sizing.
1567 * @param string $orig_image
1568 * @param string $thumb_image
1569 * @param integer $width
1572 public function create_thumbnail($orig_image, $thumb_image, $width)
1574 if(!file_exists($orig_image)) {
1578 $mime = $this->get_mime_info($orig_image);
1580 /* check if original photo is a support image type */
1581 if(!$this->checkifImageSupported($mime))
1588 $meta = $this->get_meta_informations($orig_image);
1594 if(isset($meta['Orientation'])) {
1595 switch($meta['Orientation']) {
1596 case 1: /* top, left */
1597 /* nothing to do */ break;
1598 case 2: /* top, right */
1599 $rotate = 0; $flip_hori = true; break;
1600 case 3: /* bottom, left */
1601 $rotate = 180; break;
1602 case 4: /* bottom, right */
1603 $flip_vert = true; break;
1604 case 5: /* left side, top */
1605 $rotate = 90; $flip_vert = true; break;
1606 case 6: /* right side, top */
1607 $rotate = 90; break;
1608 case 7: /* left side, bottom */
1609 $rotate = 270; $flip_vert = true; break;
1610 case 8: /* right side, bottom */
1611 $rotate = 270; break;
1615 $src_img = @imagecreatefromjpeg($orig_image);
1621 $src_img = @imagecreatefrompng($orig_image);
1625 case 'image/x-portable-pixmap':
1627 $src_img = new Imagick($orig_image);
1628 $handler = "imagick";
1633 if(!isset($src_img) || empty($src_img)) {
1634 print "Can't load image from ". $orig_image ."\n";
1642 /* grabs the height and width */
1643 $cur_width = imagesx($src_img);
1644 $cur_height = imagesy($src_img);
1646 // If requested width is more then the actual image width,
1647 // do not generate a thumbnail, instead safe the original
1648 // as thumbnail but with lower quality. But if the image
1649 // is to heigh too, then we still have to resize it.
1650 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1651 $result = imagejpeg($src_img, $thumb_image, 75);
1652 imagedestroy($src_img);
1659 $cur_width = $src_img->getImageWidth();
1660 $cur_height = $src_img->getImageHeight();
1662 // If requested width is more then the actual image width,
1663 // do not generate a thumbnail, instead safe the original
1664 // as thumbnail but with lower quality. But if the image
1665 // is to heigh too, then we still have to resize it.
1666 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1667 $src_img->setCompressionQuality(75);
1668 $src_img->setImageFormat('jpeg');
1669 $src_img->writeImage($thumb_image);
1671 $src_img->destroy();
1678 // If the image will be rotate because EXIF orientation said so
1679 // 'virtually rotate' the image for further calculations
1680 if($rotate == 90 || $rotate == 270) {
1682 $cur_width = $cur_height;
1686 /* calculates aspect ratio */
1687 $aspect_ratio = $cur_height / $cur_width;
1690 if($aspect_ratio < 1) {
1692 $new_h = abs($new_w * $aspect_ratio);
1694 /* 'virtually' rotate the image and calculate it's ratio */
1695 $tmp_w = $cur_height;
1696 $tmp_h = $cur_width;
1697 /* now get the ratio from the 'rotated' image */
1698 $tmp_ratio = $tmp_h/$tmp_w;
1699 /* now calculate the new dimensions */
1701 $tmp_h = abs($tmp_w * $tmp_ratio);
1703 // now that we know, how high they photo should be, if it
1704 // gets rotated, use this high to scale the image
1706 $new_w = abs($new_h / $aspect_ratio);
1708 // If the image will be rotate because EXIF orientation said so
1709 // now 'virtually rotate' back the image for the image manipulation
1710 if($rotate == 90 || $rotate == 270) {
1721 /* creates new image of that size */
1722 $dst_img = imagecreatetruecolor($new_w, $new_h);
1724 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1726 /* copies resized portion of original image into new image */
1727 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1729 /* needs the image to be flipped horizontal? */
1731 $this->_debug("(FLIP)");
1732 $dst_img = $this->flipImage($dst_img, 'hori');
1734 /* needs the image to be flipped vertical? */
1736 $this->_debug("(FLIP)");
1737 $dst_img = $this->flipImage($dst_img, 'vert');
1741 $this->_debug("(ROTATE)");
1742 $dst_img = $this->rotateImage($dst_img, $rotate);
1745 /* write down new generated file */
1746 $result = imagejpeg($dst_img, $thumb_image, 75);
1748 /* free your mind */
1749 imagedestroy($dst_img);
1750 imagedestroy($src_img);
1752 if($result === false) {
1753 print "Can't write thumbnail ". $thumb_image ."\n";
1763 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1765 /* needs the image to be flipped horizontal? */
1767 $this->_debug("(FLIP)");
1768 $src_img->rotateImage(new ImagickPixel(), 90);
1769 $src_img->flipImage();
1770 $src_img->rotateImage(new ImagickPixel(), -90);
1772 /* needs the image to be flipped vertical? */
1774 $this->_debug("(FLIP)");
1775 $src_img->flipImage();
1779 $this->_debug("(ROTATE)");
1780 $src_img->rotateImage(new ImagickPixel(), $rotate);
1783 $src_img->setCompressionQuality(75);
1784 $src_img->setImageFormat('jpeg');
1786 if(!$src_img->writeImage($thumb_image)) {
1787 print "Can't write thumbnail ". $thumb_image ."\n";
1792 $src_img->destroy();
1799 } // create_thumbnail()
1802 * return all exif meta data from the file
1803 * @param string $file
1806 public function get_meta_informations($file)
1808 return exif_read_data($file);
1810 } // get_meta_informations()
1813 * create phpfspot own sqlite database
1815 * this function creates phpfspots own sqlite database
1816 * if it does not exist yet. this own is used to store
1817 * some necessary informations (md5 sum's, ...).
1819 public function check_config_table()
1821 // if the config table doesn't exist yet, create it
1822 if(!$this->cfg_db->db_check_table_exists("images")) {
1823 $this->cfg_db->db_exec("
1824 CREATE TABLE images (
1825 img_idx int primary key,
1831 } // check_config_table
1834 * Generates a thumbnail from photo idx
1836 * This function will generate JPEG thumbnails from provided F-Spot photo
1839 * 1. Check if all thumbnail generations (width) are already in place and
1841 * 2. Check if the md5sum of the original file has changed
1842 * 3. Generate the thumbnails if needed
1843 * @param integer $idx
1844 * @param integer $force
1845 * @param boolean $overwrite
1847 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1851 $resolutions = Array(
1852 $this->cfg->thumb_width,
1853 $this->cfg->photo_width,
1854 $this->cfg->mini_width,
1858 /* get details from F-Spot's database */
1859 $details = $this->get_photo_details($idx);
1861 /* calculate file MD5 sum */
1862 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1864 if(!file_exists($full_path)) {
1865 $this->_error("File ". $full_path ." does not exist\n");
1869 if(!is_readable($full_path)) {
1870 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1874 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1876 /* If Nikon NEF format, we need to treat it another way */
1877 if(isset($this->cfg->dcraw_bin) &&
1878 file_exists($this->cfg->dcraw_bin) &&
1879 is_executable($this->cfg->dcraw_bin) &&
1880 preg_match('/\.nef$/i', $details['uri'])) {
1882 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1884 /* if PPM file does not exist, let dcraw convert it from NEF */
1885 if(!file_exists($ppm_path)) {
1886 system($this->cfg->dcraw_bin ." -a ". $full_path);
1889 /* for now we handle the PPM instead of the NEF */
1890 $full_path = $ppm_path;
1894 $file_md5 = md5_file($full_path);
1897 foreach($resolutions as $resolution) {
1899 $generate_it = false;
1901 $thumb_sub_path = substr($file_md5, 0, 2);
1902 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1904 /* if thumbnail-subdirectory does not exist yet, create it */
1905 if(!file_exists(dirname($thumb_path))) {
1906 mkdir(dirname($thumb_path), 0755);
1909 /* if the thumbnail file doesn't exist, create it */
1910 if(!file_exists($thumb_path)) {
1911 $generate_it = true;
1913 /* if the file hasn't changed there is no need to regen the thumb */
1914 elseif($file_md5 != $this->getMD5($idx) || $force) {
1915 $generate_it = true;
1918 if($generate_it || $overwrite) {
1920 $this->_debug(" ". $resolution ."px");
1921 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1929 $this->_debug(" already exist");
1932 /* set the new/changed MD5 sum for the current photo */
1934 $this->setMD5($idx, $file_md5);
1937 $this->_debug("\n");
1942 * returns stored md5 sum for a specific photo
1944 * this function queries the phpfspot database for a
1945 * stored MD5 checksum of the specified photo
1946 * @param integer $idx
1947 * @return string|null
1949 public function getMD5($idx)
1951 $result = $this->cfg_db->db_query("
1954 WHERE img_idx='". $idx ."'
1960 $img = $this->cfg_db->db_fetch_object($result);
1961 return $img['img_md5'];
1966 * set MD5 sum for the specific photo
1967 * @param integer $idx
1968 * @param string $md5
1970 private function setMD5($idx, $md5)
1972 $result = $this->cfg_db->db_exec("
1973 REPLACE INTO images (img_idx, img_md5)
1974 VALUES ('". $idx ."', '". $md5 ."')
1980 * store current tag condition
1982 * this function stores the current tag condition
1983 * (AND or OR) in the users session variables
1984 * @param string $mode
1987 public function setTagCondition($mode)
1989 $_SESSION['tag_condition'] = $mode;
1993 } // setTagCondition()
1996 * invoke tag & date search
1998 * this function will return all matching tags and store
1999 * them in the session variable selected_tags. furthermore
2000 * it also handles the date search.
2001 * getPhotoSelection() will then only return the matching
2005 public function startSearch()
2008 if(isset($_POST['from']) && $this->isValidDate($_POST['from'])) {
2009 $from = $_POST['from'];
2011 if(isset($_POST['to']) && $this->isValidDate($_POST['to'])) {
2015 /* tag-name search */
2016 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2017 $searchfor_tag = $_POST['for_tag'];
2018 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2021 unset($_SESSION['searchfor_tag']);
2024 /* file-name search */
2025 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2026 $_SESSION['searchfor_name'] = $_POST['for_name'];
2029 unset($_SESSION['searchfor_name']);
2033 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2035 $_SESSION['rate_from'] = $_POST['rate_from'];
2037 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2038 $_SESSION['rate_to'] = $_POST['rate_to'];
2042 /* delete any previously set value */
2043 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2048 if(isset($from) && !empty($from))
2049 $_SESSION['from_date'] = strtotime($from ." 00:00:00");
2051 unset($_SESSION['from_date']);
2053 if(isset($to) && !empty($to))
2054 $_SESSION['to_date'] = strtotime($to ." 23:59:59");
2056 unset($_SESSION['to_date']);
2058 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2059 /* new search, reset the current selected tags */
2060 $_SESSION['selected_tags'] = Array();
2061 foreach($this->avail_tags as $tag) {
2062 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2063 array_push($_SESSION['selected_tags'], $tag);
2072 * updates sort order in session variable
2074 * this function is invoked by RPC and will sort the requested
2075 * sort order in the session variable.
2076 * @param string $sort_order
2079 public function updateSortOrder($order)
2081 if(isset($this->sort_orders[$order])) {
2082 $_SESSION['sort_order'] = $order;
2086 return "unkown error";
2088 } // updateSortOrder()
2093 * this function rotates the image according the
2095 * @param string $img
2096 * @param integer $degress
2099 private function rotateImage($img, $degrees)
2101 if(function_exists("imagerotate")) {
2102 $img = imagerotate($img, $degrees, 0);
2104 function imagerotate($src_img, $angle)
2106 $src_x = imagesx($src_img);
2107 $src_y = imagesy($src_img);
2108 if ($angle == 180) {
2112 elseif ($src_x <= $src_y) {
2116 elseif ($src_x >= $src_y) {
2121 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2122 imagealphablending($rotate, false);
2127 for ($y = 0; $y < ($src_y); $y++) {
2128 for ($x = 0; $x < ($src_x); $x++) {
2129 $color = imagecolorat($src_img, $x, $y);
2130 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2136 for ($y = 0; $y < ($src_y); $y++) {
2137 for ($x = 0; $x < ($src_x); $x++) {
2138 $color = imagecolorat($src_img, $x, $y);
2139 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2145 for ($y = 0; $y < ($src_y); $y++) {
2146 for ($x = 0; $x < ($src_x); $x++) {
2147 $color = imagecolorat($src_img, $x, $y);
2148 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2162 $img = imagerotate($img, $degrees);
2171 * returns flipped image
2173 * this function will return an either horizontal or
2174 * vertical flipped truecolor image.
2175 * @param string $image
2176 * @param string $mode
2179 private function flipImage($image, $mode)
2181 $w = imagesx($image);
2182 $h = imagesy($image);
2183 $flipped = imagecreatetruecolor($w, $h);
2187 for ($y = 0; $y < $h; $y++) {
2188 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2192 for ($x = 0; $x < $w; $x++) {
2193 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2203 * return all assigned tags for the specified photo
2204 * @param integer $idx
2207 private function get_photo_tags($idx)
2209 $result = $this->db->db_query("
2212 INNER JOIN photo_tags pt
2214 WHERE pt.photo_id='". $idx ."'
2219 while($row = $this->db->db_fetch_object($result)) {
2220 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2222 $tags[$row['id']] = $row['name'];
2227 } // get_photo_tags()
2230 * create on-the-fly images with text within
2231 * @param string $txt
2232 * @param string $color
2233 * @param integer $space
2234 * @param integer $font
2237 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2239 if (strlen($color) != 6)
2242 $int = hexdec($color);
2243 $h = imagefontheight($font);
2244 $fw = imagefontwidth($font);
2245 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2246 $lines = count($txt);
2247 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2248 $bg = imagecolorallocate($im, 255, 255, 255);
2249 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2252 foreach ($txt as $text) {
2253 $x = (($w - ($fw * strlen($text))) / 2);
2254 imagestring($im, $font, $x, $y, $text, $color);
2255 $y += ($h + $space);
2258 Header("Content-type: image/png");
2261 } // showTextImage()
2264 * check if all requirements are met
2267 private function check_requirements()
2269 if(!function_exists("imagecreatefromjpeg")) {
2270 print "PHP GD library extension is missing<br />\n";
2274 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2275 print "PHP SQLite3 library extension is missing<br />\n";
2279 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2280 ini_set('track_errors', 1);
2281 @include_once 'HTML/AJAX/Server.php';
2282 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2283 print "PEAR HTML_AJAX package is missing<br />\n";
2286 @include_once 'Calendar/Calendar.php';
2287 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2288 print "PEAR Calendar package is missing<br />\n";
2291 @include_once 'Console/Getopt.php';
2292 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2293 print "PEAR Console_Getopt package is missing<br />\n";
2296 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2297 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2298 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2301 ini_restore('track_errors');
2308 } // check_requirements()
2310 private function _debug($text)
2312 if(isset($this->fromcmd)) {
2319 * check if specified MIME type is supported
2320 * @param string $mime
2323 public function checkifImageSupported($mime)
2325 $supported_types = Array(
2328 "image/x-portable-pixmap",
2332 if(in_array($mime, $supported_types))
2337 } // checkifImageSupported()
2341 * @param string $text
2343 public function _error($text)
2345 switch($this->cfg->logging) {
2348 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2349 print $text ."<br />\n";
2355 error_log($text, 3, $his->cfg->log_file);
2359 $this->runtime_error = true;
2364 * output calendard input fields
2365 * @param string $mode
2368 private function get_calendar($mode)
2370 $year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2371 $month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2372 $day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2374 $output = "<input type=\"text\" size=\"3\" id=\"". $mode ."year\" value=\"". $year ."\"";
2375 if(!isset($_SESSION[$mode .'_date']))
2376 $output.= " disabled=\"disabled\"";
2378 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."month\" value=\"". $month ."\"";
2379 if(!isset($_SESSION[$mode .'_date']))
2380 $output.= " disabled=\"disabled\"";
2382 $output.= "<input type=\"text\" size=\"1\" id=\"". $mode ."day\" value=\"". $day ."\"";
2383 if(!isset($_SESSION[$mode .'_date']))
2384 $output.= " disabled=\"disabled\"";
2392 * output calendar matrix
2393 * @param integer $year
2394 * @param integer $month
2395 * @param integer $day
2397 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2399 if (!isset($year)) $year = date('Y');
2400 if (!isset($month)) $month = date('m');
2401 if (!isset($day)) $day = date('d');
2406 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2407 require_once CALENDAR_ROOT.'Day.php';
2410 $month = new Calendar_Month_Weekdays($year,$month);
2413 $prevStamp = $month->prevMonth(true);
2414 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2415 $nextStamp = $month->nextMonth(true);
2416 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2418 $selectedDays = array (
2419 new Calendar_Day($year,$month,$day),
2420 new Calendar_Day($year,12,25),
2423 // Build the days in the month
2424 $month->build($selectedDays);
2426 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2427 $this->tmpl->assign('prev_month', $prev);
2428 $this->tmpl->assign('next_month', $next);
2430 while ( $day = $month->fetch() ) {
2432 if(!isset($matrix[$rows]))
2433 $matrix[$rows] = Array();
2437 $dayStamp = $day->thisDay(true);
2438 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2440 // isFirst() to find start of week
2441 if ( $day->isFirst() )
2444 if ( $day->isSelected() ) {
2445 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2446 } else if ( $day->isEmpty() ) {
2447 $string.= "<td> </td>\n";
2449 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2452 // isLast() to find end of week
2453 if ( $day->isLast() )
2454 $string.= "</tr>\n";
2456 $matrix[$rows][$cols] = $string;
2466 $this->tmpl->assign('matrix', $matrix);
2467 $this->tmpl->assign('rows', $rows);
2468 $this->tmpl->show("calendar.tpl");
2470 } // get_calendar_matrix()
2473 * output export page
2474 * @param string $mode
2476 public function getExport($mode)
2478 $pictures = $this->getPhotoSelection();
2479 $current_tags = $this->getCurrentTags();
2481 foreach($pictures as $picture) {
2483 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2484 if($current_tags != "") {
2485 $orig_url.= "&tags=". $current_tags;
2487 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2488 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2491 if($this->is_user_friendly_url()) {
2492 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2495 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2501 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2502 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2506 // "[%pictureurl% %thumbnailurl%]"
2507 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2510 case 'MoinMoinList':
2511 // " * [%pictureurl% %thumbnailurl%]"
2512 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2523 public function getRSSFeed()
2525 Header("Content-type: text/xml; charset=utf-8");
2526 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2529 xmlns:media="http://search.yahoo.com/mrss/"
2530 xmlns:dc="http://purl.org/dc/elements/1.1/"
2533 <title>phpfspot</title>
2534 <description>phpfspot RSS feed</description>
2535 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2536 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2537 <generator>phpfspot</generator>
2540 $pictures = $this->getPhotoSelection();
2541 $current_tags = $this->getCurrentTags();
2543 foreach($pictures as $picture) {
2545 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2546 if($current_tags != "") {
2547 $orig_url.= "&tags=". $current_tags;
2549 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2550 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2553 $details = $this->get_photo_details($picture);
2555 if($this->is_user_friendly_url()) {
2556 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2559 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2562 $thumb_html = htmlspecialchars("
2563 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2565 ". $details['description']);
2567 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2569 /* get EXIF information if JPEG */
2570 if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2571 $meta = $this->get_meta_informations($orig_path);
2574 $meta_date = isset($meta['FileDateTime']) ? $meta['FileDateTime'] : filemtime($orig_path);
2578 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2579 <link><?php print htmlspecialchars($orig_url); ?></link>
2580 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2581 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $meta_date); ?></dc:date.Taken>
2583 <?php print $thumb_html; ?>
2585 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $meta_date); ?></pubDate>
2600 * get all selected tags
2602 * This function will return all selected tags as one string, seperated
2606 private function getCurrentTags()
2609 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2610 foreach($_SESSION['selected_tags'] as $tag)
2611 $current_tags.= $tag .",";
2612 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2614 return $current_tags;
2616 } // getCurrentTags()
2619 * return the current photo
2621 public function getCurrentPhoto()
2623 if(isset($_SESSION['current_photo'])) {
2624 print $_SESSION['current_photo'];
2626 } // getCurrentPhoto()
2629 * tells the client browser what to do
2631 * this function is getting called via AJAX by the
2632 * client browsers. it will tell them what they have
2633 * to do next. This is necessary for directly jumping
2634 * into photo index or single photo view when the are
2635 * requested with specific URLs
2638 public function whatToDo()
2640 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2642 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2643 return "showpi_tags";
2645 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2652 * return the current process-user
2655 private function getuid()
2657 if($uid = posix_getuid()) {
2658 if($user = posix_getpwuid($uid)) {
2659 return $user['name'];
2668 * returns a select-dropdown box to select photo index sort parameters
2669 * @param array $params
2670 * @param smarty $smarty
2673 public function smarty_sort_select_list($params, &$smarty)
2677 foreach($this->sort_orders as $key => $value) {
2678 $output.= "<option value=\"". $key ."\"";
2679 if($key == $_SESSION['sort_order']) {
2680 $output.= " selected=\"selected\"";
2682 $output.= ">". $value ."</option>";
2687 } // smarty_sort_select_list()
2690 * returns the currently selected sort order
2693 private function get_sort_order()
2695 switch($_SESSION['sort_order']) {
2697 return " ORDER BY p.time ASC";
2700 return " ORDER BY p.time DESC";
2703 if($this->dbver < 9) {
2704 return " ORDER BY p.name ASC";
2707 return " ORDER BY basename(p.uri) ASC";
2711 if($this->dbver < 9) {
2712 return " ORDER BY p.name DESC";
2715 return " ORDER BY basename(p.uri) DESC";
2719 return " ORDER BY t.name ASC ,p.time ASC";
2722 return " ORDER BY t.name DESC ,p.time ASC";
2725 return " ORDER BY t.name ASC, p.rating ASC";
2728 return " ORDER BY t.name DESC, p.rating DESC";
2732 } // get_sort_order()
2735 * return the next to be shown slide show image
2737 * this function returns the URL of the next image
2738 * in the slideshow sequence.
2741 public function getNextSlideShowImage()
2743 $all_photos = $this->getPhotoSelection();
2745 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2746 $_SESSION['slideshow_img'] = 0;
2748 $_SESSION['slideshow_img']++;
2750 if($this->is_user_friendly_url()) {
2751 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2754 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2756 } // getNextSlideShowImage()
2759 * return the previous to be shown slide show image
2761 * this function returns the URL of the previous image
2762 * in the slideshow sequence.
2765 public function getPrevSlideShowImage()
2767 $all_photos = $this->getPhotoSelection();
2769 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2770 $_SESSION['slideshow_img'] = 0;
2772 $_SESSION['slideshow_img']--;
2774 if($this->is_user_friendly_url()) {
2775 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2778 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2780 } // getPrevSlideShowImage()
2782 public function resetSlideShow()
2784 if(isset($_SESSION['slideshow_img']))
2785 unset($_SESSION['slideshow_img']);
2787 } // resetSlideShow()
2792 * this function will get all photos from the fspot
2793 * database and randomly return ONE entry
2795 * saddly there is yet no sqlite3 function which returns
2796 * the bulk result in array, so we have to fill up our
2800 public function get_random_photo()
2809 /* if show_tags is set, only return details for photos which
2810 are specified to be shown
2812 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2814 INNER JOIN photo_tags pt
2819 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2822 $result = $this->db->db_query($query_str);
2824 while($row = $this->db->db_fetch_object($result)) {
2825 array_push($all, $row['id']);
2828 return $all[array_rand($all)];
2830 } // get_random_photo()
2833 * get random photo tag photo
2835 * this function will get all photos tagged with the requested
2836 * tag from the fspot database and randomly return ONE entry
2838 * saddly there is yet no sqlite3 function which returns
2839 * the bulk result in array, so we have to fill up our
2843 public function get_random_tag_photo($tagidx)
2850 INNER JOIN photo_tags pt
2854 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2862 pt.tag_id LIKE '". $tagidx ."'
2865 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2868 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2872 $result = $this->db->db_query($query_str);
2874 while($row = $this->db->db_fetch_object($result)) {
2875 array_push($all, $row['id']);
2878 return $all[array_rand($all)];
2880 } // get_random_tag_photo()
2883 * validates provided date
2885 * this function validates if the provided date
2886 * contains a valid date and will return true
2888 * @param string $date_str
2891 public function isValidDate($date_str)
2893 $timestamp = strtotime($date_str);
2895 if(is_numeric($timestamp))
2903 * timestamp to string conversion
2904 * @param integer $timestamp
2907 private function ts2str($timestamp)
2909 if(!empty($timestamp) && is_numeric($timestamp))
2910 return strftime("%Y-%m-%d", $timestamp);
2915 * extract tag-names from $_GET['tags']
2916 * @param string $tags_str
2919 private function extractTags($tags_str)
2921 $not_validated = split(',', $tags_str);
2922 $validated = array();
2924 foreach($not_validated as $tag) {
2925 if(is_numeric($tag))
2926 array_push($validated, $tag);
2934 * returns the full path to a thumbnail
2935 * @param integer $width
2936 * @param integer $photo
2939 public function get_thumb_path($width, $photo)
2941 $md5 = $this->getMD5($photo);
2942 $sub_path = substr($md5, 0, 2);
2943 return $this->cfg->thumb_path
2951 } // get_thumb_path()
2954 * returns server's virtual host name
2957 private function get_server_name()
2959 return $_SERVER['SERVER_NAME'];
2960 } // get_server_name()
2963 * returns type of webprotocol which is currently used
2966 private function get_web_protocol()
2968 if(!isset($_SERVER['HTTPS']))
2972 } // get_web_protocol()
2975 * return url to this phpfspot installation
2978 private function get_phpfspot_url()
2980 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
2982 } // get_phpfspot_url()
2985 * returns the number of photos which are tagged with $tag_id
2986 * @param integer $tag_id
2989 public function get_num_photos($tag_id)
2991 if($result = $this->db->db_fetchSingleRow("
2992 SELECT count(*) as number
2995 tag_id LIKE '". $tag_id ."'")) {
2997 return $result['number'];
3003 } // get_num_photos()
3006 * check file exists and is readable
3008 * returns true, if everything is ok, otherwise false
3009 * if $silent is not set, this function will output and
3011 * @param string $file
3012 * @param boolean $silent
3015 private function check_readable($file, $silent = null)
3017 if(!file_exists($file)) {
3019 print "File \"". $file ."\" does not exist.\n";
3023 if(!is_readable($file)) {
3025 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3031 } // check_readable()
3034 * check if all needed indices are present
3036 * this function checks, if some needed indices are already
3037 * present, or if not, create them on the fly. they are
3038 * necessary to speed up some queries like that one look for
3039 * all tags, when show_tags is specified in the configuration.
3041 private function checkDbIndices()
3043 $result = $this->db->db_exec("
3044 CREATE INDEX IF NOT EXISTS
3051 } // checkDbIndices()
3054 * retrive F-Spot database version
3056 * this function will return the F-Spot database version number
3057 * It is stored within the sqlite3 database in the table meta
3058 * @return string|null
3060 public function getFspotDBVersion()
3062 if($result = $this->db->db_fetchSingleRow("
3063 SELECT data as version
3066 name LIKE 'F-Spot Database Version'
3068 return $result['version'];
3072 } // getFspotDBVersion()
3075 * parse the provided URI and will returned the requested chunk
3076 * @param string $uri
3077 * @param string $mode
3080 public function parse_uri($uri, $mode)
3082 if(($components = parse_url($uri)) !== false) {
3086 return basename($components['path']);
3089 return dirname($components['path']);
3092 return $components['path'];
3102 * validate config options
3104 * this function checks if all necessary configuration options are
3105 * specified and set.
3108 private function check_config_options()
3110 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3111 $this->_error("Please set \$page_title in phpfspot_cfg");
3113 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3114 $this->_error("Please set \$base_path in phpfspot_cfg");
3116 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3117 $this->_error("Please set \$web_path in phpfspot_cfg");
3119 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3120 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3122 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3123 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3125 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3126 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3128 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3129 $this->_error("Please set \$db_access in phpfspot_cfg");
3131 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3132 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3134 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3135 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3137 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3138 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3140 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3141 $this->_error("Please set \$photo_width in phpfspot_cfg");
3143 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3144 $this->_error("Please set \$mini_width in phpfspot_cfg");
3146 if(!isset($this->cfg->thumbs_per_page))
3147 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3149 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3150 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3152 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3153 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3155 if(!isset($this->cfg->hide_tags))
3156 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3158 if(!isset($this->cfg->theme_name))
3159 $this->_error("Please set \$theme_name in phpfspot_cfg");
3161 if(!isset($this->cfg->logging))
3162 $this->_error("Please set \$logging in phpfspot_cfg");
3164 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3166 if(!isset($this->cfg->log_file))
3167 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3169 if(!is_writeable($this->cfg->log_file))
3170 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3174 /* remove trailing slash, if set */
3175 if($this->cfg->web_path == "/")
3176 $this->cfg->web_path = "";
3177 elseif(preg_match('/\/$/', $this->cfg->web_path))
3178 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3180 return $this->runtime_error;
3182 } // check_config_options()
3185 * cleanup phpfspot own database
3187 * When photos are getting delete from F-Spot, there will remain
3188 * remain some residues in phpfspot own database. This function
3189 * will try to wipe them out.
3191 public function cleanup_phpfspot_db()
3193 $to_delete = Array();
3195 $result = $this->cfg_db->db_query("
3198 ORDER BY img_idx ASC
3201 while($row = $this->cfg_db->db_fetch_object($result)) {
3202 if(!$this->db->db_fetchSingleRow("
3205 WHERE id='". $row['img_idx'] ."'")) {
3207 array_push($to_delete, $row['img_idx'], ',');
3211 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3213 $this->cfg_db->db_exec("
3215 WHERE img_idx IN (". implode($to_delete) .")
3218 } // cleanup_phpfspot_db()
3221 * return first image of the page, the $current photo
3224 * this function is used to find out the first photo of the
3225 * current page, in which the $current photo lies. this is
3226 * used to display the correct photo, when calling showPhotoIndex()
3228 * @param integer $current
3229 * @param integer $max
3232 private function getCurrentPage($current, $max)
3234 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3235 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3236 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3242 } // getCurrentPage()
3247 * this function tries to find out the correct mime-type
3248 * for the provided file.
3249 * @param string $file
3252 public function get_mime_info($file)
3254 $details = getimagesize($file);
3256 /* if getimagesize() returns empty, try at least to find out the
3259 if(empty($details) && function_exists('mime_content_type')) {
3261 // mime_content_type is marked as deprecated in the documentation,
3262 // but is it really necessary to force users to install a PECL
3264 $details['mime'] = mime_content_type($file);
3267 return $details['mime'];
3269 } // get_mime_info()
3272 * return tag-name by tag-idx
3274 * this function returns the tag-name for the requested
3275 * tag specified by tag-idx.
3276 * @param integer $idx
3279 public function get_tag_name($idx)
3281 if($result = $this->db->db_fetchSingleRow("
3285 id LIKE '". $idx ."'")) {
3287 return $result['name'];
3296 * parse user friendly url which got rewritten by the websever
3297 * @param string $request_uri
3300 private function parse_user_friendly_url($request_uri)
3302 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3304 $options = explode('/', $request_uri);
3306 switch($options[1]) {
3308 if(is_numeric($options[2])) {
3309 $this->session_cleanup();
3310 //unset($_SESSION['start_action']);
3311 //unset($_SESSION['selected_tags']);
3312 $_GET['mode'] = 'showp';
3313 return $this->showPhoto($options[2]);
3317 if(is_numeric($options[2])) {
3318 require_once "phpfspot_img.php";
3319 $img = new PHPFSPOT_IMG;
3320 if(isset($options[3]) && is_numeric($options[3]))
3321 $img->showImg($options[2], $options[3]);
3323 $img->showImg($options[2]);
3328 if(is_numeric($options[2])) {
3329 $this->session_cleanup();
3330 $_GET['tags'] = $options[2];
3331 $_SESSION['selected_tags'] = Array($options[2]);
3332 if(isset($options[3]) && is_numeric($options[3]))
3333 $_SESSION['begin_with'] = $options[3];
3334 return $this->showPhotoIndex();
3340 } // parse_user_friendly_url()
3343 * check if user-friendly-urls are enabled
3345 * this function will return true, if the config option
3346 * $user_friendly_url has been set. Otherwise false.
3349 private function is_user_friendly_url()
3351 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3356 } // is_user_friendly_url()
3361 * this function will cleanup user's session information
3363 private function session_cleanup()
3365 unset($_SESSION['begin_with']);
3366 $this->resetDateSearch();
3367 $this->resetPhotoView();
3368 $this->resetTagSearch();
3369 $this->resetNameSearch();
3370 $this->resetDateSearch();
3373 } // session_cleanup()