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.6";
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 "Error: ". $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() .".\n";
146 print "Please fix permissions so phpfspot can create indices within the F-Spot database to"
147 ." speed up some database operations.\n";
151 /* open the database */
152 $this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
154 /* change sqlite temp directory, if requested */
155 if(isset($this->cfg->sqlite_temp_dir)) {
158 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
162 /* get F-Spot database version */
163 $this->dbver = $this->getFspotDBVersion();
165 if(!is_writeable($this->cfg->base_path ."/templates_c")) {
166 print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
170 if(!is_writeable($this->cfg->thumb_path)) {
171 print $this->cfg->thumb_path .": directory is not writeable for user ". $this->getuid() ."\n";
175 /******* Opening phpfspot's sqlite database *********/
177 /* Check if directory where the database file is stored is writeable */
178 if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
179 print "Error: ". dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() .".\n";
180 print "Please fix permissions so phpfspot can create its own sqlite database to store some settings.\n";
184 /* Check if database file is writeable */
185 if(!is_writeable($this->cfg->phpfspot_db)) {
186 print "Error: ". $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() .".\n";
187 print "Please fix permissions so phpfspot can create its own sqlite database to store some settings.\n";
191 /* open the database */
192 $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
194 /* change sqlite temp directory, if requested */
195 if(isset($this->cfg->sqlite_temp_dir)) {
196 $this->cfg_db->db_exec("
198 temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
202 /* Check if some tables need to be created */
203 $this->check_config_table();
205 /* overload Smarty class with our own template handler */
206 require_once "phpfspot_tmpl.php";
207 $this->tmpl = new PHPFSPOT_TMPL();
209 $this->tmpl->assign('web_path', $this->cfg->web_path);
211 /* Starting with F-Spot 0.4.2, the rating-feature was available */
212 if($this->dbver > 10) {
213 $this->tmpl->assign('has_rating', true);
214 $this->sort_orders = array_merge($this->sort_orders, array(
215 'rate_asc' => 'Rate ↑',
216 'rate_desc' => 'Rate ↓',
220 /* check if all necessary indices exist */
221 $this->checkDbIndices();
223 /* if session is not yet started, do it now */
224 if(session_id() == "")
227 if(!isset($_SESSION['tag_condition']))
228 $_SESSION['tag_condition'] = 'or';
230 /* if sort-order has not been set yet, get the one specified in the config */
231 if(!isset($_SESSION['sort_order']))
232 $_SESSION['sort_order'] = $this->cfg->sort_order;
234 if(!isset($_SESSION['searchfor_tag']))
235 $_SESSION['searchfor_tag'] = '';
237 // if begin_with is still set but thumbs_per_page is now 0, unset it
238 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
239 unset($_SESSION['begin_with']);
241 // if user-friendly-url's are enabled, set also a flag for the template handler
242 if($this->is_user_friendly_url()) {
243 $this->tmpl->assign('user_friendly_url', 'true');
248 public function __destruct()
254 * show - generate html output
256 * this function can be called after the constructor has
257 * prepared everyhing. it will load the index.tpl smarty
258 * template. if necessary it will registere pre-selects
259 * (photo index, photo, tag search, date search) into
262 public function show()
264 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
265 $this->tmpl->assign('page_title', $this->cfg->page_title);
266 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
267 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
270 if($this->is_user_friendly_url()) {
271 $content = $this->parse_user_friendly_url($_SERVER['REQUEST_URI']);
274 if(isset($_GET['mode'])) {
276 $_SESSION['start_action'] = $_GET['mode'];
278 switch($_GET['mode']) {
280 if(isset($_GET['tags'])) {
281 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
283 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
284 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
286 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
287 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
291 if(isset($_GET['tags'])) {
292 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
293 $_SESSION['start_action'] = 'showp';
295 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
296 $_SESSION['current_photo'] = $_GET['id'];
297 $_SESSION['start_action'] = 'showp';
299 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
300 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
302 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
303 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
307 /* fetch export template */
308 print $this->tmpl->fetch("export.tpl");
309 /* no further execution necessary. */
313 /* fetch slideshow template */
314 print $this->tmpl->show("slideshow.tpl");
315 /* no further execution necessary. */
319 if(isset($_GET['tags'])) {
320 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
322 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
323 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
325 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
326 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
334 /* if date-search variables are registered in the session, set the check
335 for "consider date-range" in the html output
337 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
338 $this->tmpl->assign('date_search_enabled', true);
340 /* if rate-search variables are registered in the session, set the check
341 for "consider rate-range" in the html output
343 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
344 $this->tmpl->assign('rate_search_enabled', true);
347 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
348 $this->tmpl->assign('search_from_date', $this->get_date_text_field('from'));
349 $this->tmpl->assign('search_to_date', $this->get_date_text_field('to'));
351 $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags());
352 $this->tmpl->assign('preset_available_tags', $this->getAvailableTags());
353 $this->tmpl->assign('rate_search', $this->get_rate_search());
355 /* if no site-content has been set yet... */
356 if(!isset($content)) {
357 /* if tags are already selected, we can immediately display photo-index */
358 if((isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']) &&
359 isset($_SESSION['start_action']) && $_SESSION['start_action'] != 'showp') ||
360 (isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi'))
361 $this->tmpl->assign('initial_content', $this->showPhotoIndex());
363 /* if a photo is already selected, we can immediately display single-photo */
364 if(isset($_SESSION['current_photo']) && !empty($_SESSION['current_photo']))
365 $this->tmpl->assign('initial_content', $this->showPhoto($_SESSION['current_photo']));
367 /* ok, then let us show the welcome page... */
368 $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl'));
373 $this->tmpl->assign('initial_content', $content);
375 $this->tmpl->show("index.tpl");
380 * get_tags - grab all tags of f-spot's database
382 * this function will get all available tags from
383 * the f-spot database and store them within two
384 * arrays within this class for later usage. in
385 * fact, if the user requests (hide_tags) it will
386 * opt-out some of them.
388 * this function is getting called once by show()
390 private function get_tags()
392 $this->avail_tags = Array();
395 /* if show_tags has been set in the configuration (only show photos
396 which are tagged by these tags) they following will take care,
397 that only these other tags are displayed where the photo is also
398 tagged with one of show_tags.
400 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
403 DISTINCT t1.id as id, t1.name as name
406 INNER JOIN photo_tags
407 pt2 ON pt1.photo_id=pt2.photo_id
413 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
415 t1.sort_priority ASC";
417 $result = $this->db->db_query($query_str);
421 $result = $this->db->db_query("
424 ORDER BY sort_priority ASC
428 while($row = $this->db->db_fetch_object($result)) {
430 $tag_id = $row['id'];
431 $tag_name = $row['name'];
433 /* if the user has specified to ignore this tag in phpfspot's
434 configuration, ignore it here so it does not get added to
437 if(in_array($row['name'], $this->cfg->hide_tags))
440 /* if you include the following if-clause and the user has specified
441 to only show certain tags which are specified in phpfspot's
442 configuration, ignore all others so they will not be added to the
444 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
445 !in_array($row['name'], $this->cfg->show_tags))
449 $this->tags[$tag_id] = $tag_name;
450 $this->avail_tags[$count] = $tag_id;
458 * extract all photo details
460 * retrieve all available details from f-spot's
461 * database and return them as object
462 * @param integer $idx
463 * @return object|null
465 public function get_photo_details($idx)
467 /* ~ F-Spot version 0.3.x */
468 if($this->dbver < 9) {
470 SELECT p.id, p.name, p.time, p.directory_path, p.description
475 /* till F-Spot version 0.4.1 */
476 if($this->dbver < 11) {
478 SELECT p.id, p.uri, p.time, p.description
484 SELECT p.id, p.uri, p.time, p.description, p.rating
490 /* if show_tags is set, only return details of photos which are
491 tagged with a tag that has been specified to be shown.
493 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
495 INNER JOIN photo_tags pt
499 WHERE p.id='". $idx ."'
500 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
504 WHERE p.id='". $idx ."'
508 if($result = $this->db->db_query($query_str)) {
510 $row = $this->db->db_fetch_object($result);
512 /* before F-Spot db version 9 there was no uri column but seperated
513 columns for directory_path and name (= filename).
515 if($this->dbver < 9) {
516 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
525 } // get_photo_details
528 * returns aligned photo names
530 * this function returns aligned (length) names for a specific photo.
531 * If the length of the name exceeds $limit the name will bei
534 * @param integer $idx
535 * @param integer $limit
536 * @return string|null
538 public function getPhotoName($idx, $limit = 0)
540 if($details = $this->get_photo_details($idx)) {
541 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
542 $name = $this->shrink_text($long_name, $limit);
552 * get photo rating level
554 * this function will return the integer-based rating level of a
555 * photo. This can only be done, if the F-Spot database is at a
556 * specific version. If rating value can not be found, zero will
557 * be returned indicating no rating value is available.
562 public function get_photo_rating($idx)
564 if($detail = $this->get_photo_details($idx)) {
565 if(isset($detail['rating']))
566 return $detail['rating'];
571 } // get_photo_rating()
574 * get rate-search bars
576 * this function will return the rating-bars for the search field.
580 public function get_rate_search()
584 for($i = 1; $i <= 5; $i++) {
586 $bar.= "<img id=\"rate_from_". $i ."\" src=\"";
588 if(isset($_SESSION['rate_from']) && $i <= $_SESSION['rate_from'])
589 $bar.= $this->cfg->web_path ."/resources/star.png";
591 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
594 onmouseover=\"show_rate('from', ". $i .");\"
595 onmouseout=\"reset_rate('from');\"
596 onclick=\"set_rate('from', ". $i .")\" />";
601 for($i = 1; $i <= 5; $i++) {
603 $bar.= "<img id=\"rate_to_". $i ."\" src=\"";
605 if(isset($_SESSION['rate_to']) && $i <= $_SESSION['rate_to'])
606 $bar.= $this->cfg->web_path ."/resources/star.png";
608 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
611 onmouseover=\"show_rate('to', ". $i .");\"
612 onmouseout=\"reset_rate('to');\"
613 onclick=\"set_rate('to', ". $i .");\" />";
618 } // get_rate_search()
621 * shrink text according provided limit
623 * If the length of the name exceeds $limit, text will be shortend
624 * and inner content will be replaced with "...".
627 * @param integer $limit
630 private function shrink_text($text, $limit)
632 if($limit != 0 && strlen($text) > $limit) {
633 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
641 * translate f-spoth photo path
643 * as the full-qualified path recorded in the f-spot database
644 * is usally not the same as on the webserver, this function
645 * will replace the path with that one specified in the cfg
646 * @param string $path
649 public function translate_path($path)
651 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
656 * control HTML ouput for a single photo
658 * this function provides all the necessary information
659 * for the single photo template.
660 * @param integer photo
662 public function showPhoto($photo)
664 /* get all photos from the current photo selection */
665 $all_photos = $this->getPhotoSelection();
666 $count = count($all_photos);
668 for($i = 0; $i < $count; $i++) {
670 // $get_next will be set, when the photo which has to
671 // be displayed has been found - this means that the
672 // next available is in fact the NEXT image (for the
674 if(isset($get_next)) {
675 $next_img = $all_photos[$i];
679 /* the next photo is our NEXT photo */
680 if($all_photos[$i] == $photo) {
684 $previous_img = $all_photos[$i];
687 if($photo == $all_photos[$i]) {
692 $details = $this->get_photo_details($photo);
699 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
700 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
702 if(!file_exists($orig_path)) {
703 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
707 if(!is_readable($orig_path)) {
708 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
712 /* If the thumbnail doesn't exist yet, try to create it */
713 if(!file_exists($thumb_path)) {
714 $this->gen_thumb($photo, true);
715 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
718 /* get mime-type, height and width from the original photo */
719 $info = getimagesize($orig_path);
721 /* get EXIF information if JPEG */
722 if(isset($info['mime']) && $info['mime'] == "image/jpeg") {
723 $meta = $this->get_meta_informations($orig_path);
726 /* If EXIF data are available, use them */
727 if(isset($meta['ExifImageWidth'])) {
728 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
730 $meta_res = $info[0] ."x". $info[1];
733 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
734 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
736 $extern_link = "index.php?mode=showp&id=". $photo;
737 $current_tags = $this->getCurrentTags();
738 if($current_tags != "") {
739 $extern_link.= "&tags=". $current_tags;
741 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
742 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
745 $this->tmpl->assign('extern_link', $extern_link);
747 if(!file_exists($thumb_path)) {
748 $this->_error("Can't open file ". $thumb_path ."\n");
752 $info_thumb = getimagesize($thumb_path);
754 $this->tmpl->assign('description', $details['description']);
755 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
756 $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
758 $this->tmpl->assign('width', $info_thumb[0]);
759 $this->tmpl->assign('height', $info_thumb[1]);
760 $this->tmpl->assign('ExifMadeOn', strftime("%a %x %X", $details['time']));
761 $this->tmpl->assign('ExifMadeWith', $meta_make);
762 $this->tmpl->assign('ExifOrigResolution', $meta_res);
763 $this->tmpl->assign('ExifFileSize', $meta_size);
765 if($this->is_user_friendly_url()) {
766 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
767 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
770 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
771 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
774 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
776 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
777 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
778 $this->tmpl->assign('current_img', $photo);
780 if(isset($previous_img)) {
781 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
782 $this->tmpl->assign('prev_img', $previous_img);
785 if(isset($next_img)) {
786 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
787 $this->tmpl->assign('next_img', $next_img);
790 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
791 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
792 $this->tmpl->assign('photo_number', $i);
793 $this->tmpl->assign('photo_count', count($all_photos));
795 return $this->tmpl->fetch("single_photo.tpl");
800 * all available tags and tag cloud
802 * this function outputs all available tags (time ordered)
803 * and in addition output them as tag cloud (tags which have
804 * many photos will appears more then others)
806 public function getAvailableTags()
808 /* retrive tags from database */
813 $result = $this->db->db_query("
814 SELECT tag_id as id, count(tag_id) as quantity
824 while($row = $this->db->db_fetch_object($result)) {
825 $tags[$row['id']] = $row['quantity'];
828 // change these font sizes if you will
829 $max_size = 125; // max font size in %
830 $min_size = 75; // min font size in %
833 $max_sat = hexdec('cc');
834 $min_sat = hexdec('44');
836 // get the largest and smallest array values
837 $max_qty = max(array_values($tags));
838 $min_qty = min(array_values($tags));
840 // find the range of values
841 $spread = $max_qty - $min_qty;
842 if (0 == $spread) { // we don't want to divide by zero
846 // determine the font-size increment
847 // this is the increase per tag quantity (times used)
848 $step = ($max_size - $min_size)/($spread);
849 $step_sat = ($max_sat - $min_sat)/($spread);
851 // loop through our tag array
852 foreach ($tags as $key => $value) {
854 /* has the currently processed tag already been added to
855 the selected tag list? if so, ignore it here...
857 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
860 // calculate CSS font-size
861 // find the $value in excess of $min_qty
862 // multiply by the font-size increment ($size)
863 // and add the $min_size set above
864 $size = $min_size + (($value - $min_qty) * $step);
865 // uncomment if you want sizes in whole %:
868 $color = $min_sat + ($value - $min_qty) * $step_sat;
874 if(isset($this->tags[$key])) {
875 if($this->is_user_friendly_url()) {
876 $output.= "<a href=\"". $this->cfg->web_path ."/tag/". $key ."\"
877 onclick=\"Tags('add', ". $key ."); return false;\"
879 style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\"
880 title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] ."</a>, ";
883 $output.= "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi\"
884 onclick=\"Tags('add', ". $key ."); return false;\"
886 style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\"
887 title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] ."</a>, ";
892 $output = substr($output, 0, strlen($output)-2);
895 } // getAvailableTags()
898 * output all selected tags
900 * this function output all tags which have been selected
901 * by the user. the selected tags are stored in the
902 * session-variable $_SESSION['selected_tags']
905 public function getSelectedTags($type = 'link')
907 /* retrive tags from database */
912 foreach($this->avail_tags as $tag)
914 // return all selected tags
915 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
920 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
924 <div class=\"tagresulttag\">
925 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
926 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
936 $output = substr($output, 0, strlen($output)-2);
940 return "no tags selected";
943 } // getSelectedTags()
946 * add tag to users session variable
948 * this function will add the specified to users current
949 * tag selection. if a date search has been made before
950 * it will be now cleared
953 public function addTag($tag)
955 if(!isset($_SESSION['selected_tags']))
956 $_SESSION['selected_tags'] = Array();
958 if(isset($_SESSION['searchfor_tag']))
959 unset($_SESSION['searchfor_tag']);
961 // has the user requested to hide this tag, and still someone,
962 // somehow tries to add it, don't allow this.
963 if(!isset($this->cfg->hide_tags) &&
964 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
967 if(!in_array($tag, $_SESSION['selected_tags']))
968 array_push($_SESSION['selected_tags'], $tag);
975 * remove tag to users session variable
977 * this function removes the specified tag from
978 * users current tag selection
982 public function delTag($tag)
984 if(isset($_SESSION['searchfor_tag']))
985 unset($_SESSION['searchfor_tag']);
987 if(isset($_SESSION['selected_tags'])) {
988 $key = array_search($tag, $_SESSION['selected_tags']);
989 unset($_SESSION['selected_tags'][$key]);
990 sort($_SESSION['selected_tags']);
998 * reset tag selection
1000 * if there is any tag selection, it will be
1003 public function resetTags()
1005 if(isset($_SESSION['selected_tags']))
1006 unset($_SESSION['selected_tags']);
1011 * returns the value for the autocomplete tag-search
1014 public function get_xml_tag_list()
1016 if(!isset($_GET['search']) || !is_string($_GET['search']))
1017 $_GET['search'] = '';
1022 /* retrive tags from database */
1025 $matched_tags = Array();
1027 header("Content-Type: text/xml");
1029 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
1030 $string.= "<results>\n";
1032 foreach($this->avail_tags as $tag)
1034 if(!empty($_GET['search']) &&
1035 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
1036 count($matched_tags) < $length) {
1038 $count = $this->get_num_photos($tag);
1041 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1044 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1050 /* if we have collected enough items, break out */
1051 if(count($matched_tags) >= $length)
1055 $string.= "</results>\n";
1059 } // get_xml_tag_list()
1063 * reset single photo
1065 * if a specific photo was requested (external link)
1066 * unset the session variable now
1068 public function resetPhotoView()
1070 if(isset($_SESSION['current_photo']))
1071 unset($_SESSION['current_photo']);
1073 } // resetPhotoView();
1078 * if any tag search has taken place, reset it now
1080 public function resetTagSearch()
1082 if(isset($_SESSION['searchfor_tag']))
1083 unset($_SESSION['searchfor_tag']);
1085 } // resetTagSearch()
1090 * if any name search has taken place, reset it now
1092 public function resetNameSearch()
1094 if(isset($_SESSION['searchfor_name']))
1095 unset($_SESSION['searchfor_name']);
1097 } // resetNameSearch()
1102 * if any date search has taken place, reset it now.
1104 public function resetDateSearch()
1106 if(isset($_SESSION['from_date']))
1107 unset($_SESSION['from_date']);
1108 if(isset($_SESSION['to_date']))
1109 unset($_SESSION['to_date']);
1111 } // resetDateSearch();
1116 * if any rate search has taken place, reset it now.
1118 public function resetRateSearch()
1120 if(isset($_SESSION['rate_from']))
1121 unset($_SESSION['rate_from']);
1122 if(isset($_SESSION['rate_to']))
1123 unset($_SESSION['rate_to']);
1125 } // resetRateSearch();
1128 * return all photo according selection
1130 * this function returns all photos based on
1131 * the tag-selection, tag- or date-search.
1132 * the tag-search also has to take care of AND
1133 * and OR conjunctions
1136 public function getPhotoSelection()
1138 $matched_photos = Array();
1139 $additional_where_cond = "";
1141 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1142 $from_date = $_SESSION['from_date'];
1143 $to_date = $_SESSION['to_date'];
1144 $additional_where_cond.= "
1145 p.time>='". $from_date ."'
1147 p.time<='". $to_date ."'
1151 if(isset($_SESSION['searchfor_name'])) {
1153 /* check for previous conditions. if so add 'AND' */
1154 if(!empty($additional_where_cond)) {
1155 $additional_where_cond.= " AND ";
1158 if($this->dbver < 9) {
1159 $additional_where_cond.= "
1161 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1163 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1168 $additional_where_cond.= "
1170 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1172 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1178 /* limit result based on rate-search */
1179 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1181 if($this->dbver > 10) {
1183 /* check for previous conditions. if so add 'AND' */
1184 if(!empty($additional_where_cond)) {
1185 $additional_where_cond.= " AND ";
1188 $additional_where_cond.= "
1189 p.rating >= ". $_SESSION['rate_from'] ."
1191 p.rating <= ". $_SESSION['rate_to'] ."
1196 if(isset($_SESSION['sort_order'])) {
1197 $order_str = $this->get_sort_order();
1200 /* return a search result */
1201 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1203 SELECT DISTINCT pt1.photo_id
1205 INNER JOIN photo_tags pt2
1206 ON pt1.photo_id=pt2.photo_id
1210 ON pt1.photo_id=p.id
1213 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1215 if(!empty($additional_where_cond))
1216 $query_str.= "AND ". $additional_where_cond ." ";
1218 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1219 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1222 if(isset($order_str))
1223 $query_str.= $order_str;
1225 $result = $this->db->db_query($query_str);
1226 while($row = $this->db->db_fetch_object($result)) {
1227 array_push($matched_photos, $row['photo_id']);
1229 return $matched_photos;
1232 /* return according the selected tags */
1233 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1235 foreach($_SESSION['selected_tags'] as $tag)
1236 $selected.= $tag .",";
1237 $selected = substr($selected, 0, strlen($selected)-1);
1239 /* photo has to match at least on of the selected tags */
1240 if($_SESSION['tag_condition'] == 'or') {
1242 SELECT DISTINCT pt1.photo_id
1244 INNER JOIN photo_tags pt2
1245 ON pt1.photo_id=pt2.photo_id
1249 ON pt1.photo_id=p.id
1250 WHERE pt1.tag_id IN (". $selected .")
1252 if(!empty($additional_where_cond))
1253 $query_str.= "AND ". $additional_where_cond ." ";
1255 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1256 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1259 if(isset($order_str))
1260 $query_str.= $order_str;
1262 /* photo has to match all selected tags */
1263 elseif($_SESSION['tag_condition'] == 'and') {
1265 if(count($_SESSION['selected_tags']) >= 32) {
1266 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1267 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1271 /* Join together a table looking like
1273 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1275 so the query can quickly return all images matching the
1276 selected tags in an AND condition
1281 SELECT DISTINCT pt1.photo_id
1285 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1292 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1294 INNER JOIN photo_tags pt". ($i+2) ."
1295 ON pt1.photo_id=pt". ($i+2) .".photo_id
1300 ON pt1.photo_id=p.id
1302 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1303 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1305 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1308 if(!empty($additional_where_cond))
1309 $query_str.= "AND ". $additional_where_cond;
1311 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1312 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1315 if(isset($order_str))
1316 $query_str.= $order_str;
1320 $result = $this->db->db_query($query_str);
1321 while($row = $this->db->db_fetch_object($result)) {
1322 array_push($matched_photos, $row['photo_id']);
1324 return $matched_photos;
1327 /* return all available photos */
1329 SELECT DISTINCT p.id
1331 LEFT JOIN photo_tags pt
1337 if(!empty($additional_where_cond))
1338 $query_str.= "WHERE ". $additional_where_cond ." ";
1340 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1341 if(!empty($additional_where_cond))
1342 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1344 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1347 if(isset($order_str))
1348 $query_str.= $order_str;
1350 $result = $this->db->db_query($query_str);
1351 while($row = $this->db->db_fetch_object($result)) {
1352 array_push($matched_photos, $row['id']);
1354 return $matched_photos;
1356 } // getPhotoSelection()
1359 * control HTML ouput for photo index
1361 * this function provides all the necessary information
1362 * for the photo index template.
1365 public function showPhotoIndex()
1367 $photos = $this->getPhotoSelection();
1368 $current_tags = $this->getCurrentTags();
1370 $count = count($photos);
1372 /* if all thumbnails should be shown on one page */
1373 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1377 /* thumbnails should be splitted up in several pages */
1378 elseif($this->cfg->thumbs_per_page > 0) {
1380 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1384 $begin_with = $_SESSION['begin_with'];
1387 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1391 $images[$thumbs] = Array();
1392 $img_height[$thumbs] = Array();
1393 $img_width[$thumbs] = Array();
1394 $img_id[$thumbs] = Array();
1395 $img_name[$thumbs] = Array();
1396 $img_fullname[$thumbs] = Array();
1397 $img_title = Array();
1398 $img_rating = Array();
1400 for($i = $begin_with; $i < $end_with; $i++) {
1402 if(isset($photos[$i])) {
1404 $images[$thumbs] = $photos[$i];
1405 $img_id[$thumbs] = $i;
1406 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1407 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1408 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1409 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1411 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1413 if(file_exists($thumb_path)) {
1414 $info = getimagesize($thumb_path);
1415 $img_width[$thumbs] = $info[0];
1416 $img_height[$thumbs] = $info[1];
1422 // +1 for for smarty's selection iteration
1425 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1426 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1428 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1429 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1430 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1433 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1434 $this->tmpl->assign('tag_result', 1);
1437 /* do we have to display the page selector ? */
1438 if($this->cfg->thumbs_per_page != 0) {
1442 /* calculate the page switchers */
1443 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1444 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1446 if($begin_with != 0)
1447 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1448 if($end_with < $count)
1449 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1451 $photo_per_page = $this->cfg->thumbs_per_page;
1452 $last_page = ceil($count / $photo_per_page);
1454 /* get the current selected page */
1455 if($begin_with == 0) {
1459 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1466 for($i = 1; $i <= $last_page; $i++) {
1468 if($current_page == $i)
1469 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1470 elseif($current_page-1 == $i || $current_page+1 == $i)
1471 $style = "style=\"font-size: 105%;\"";
1472 elseif(($current_page-5 >= $i) && ($i != 1) ||
1473 ($current_page+5 <= $i) && ($i != $last_page))
1474 $style = "style=\"font-size: 75%;\"";
1478 $start_with = ($i*$photo_per_page)-$photo_per_page;
1480 if($this->is_user_friendly_url()) {
1481 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1484 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1486 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1490 $select.= ">". $i ."</a> ";
1492 // until 9 pages we show the selector from 1-9
1493 if($last_page <= 9) {
1494 $page_select.= $select;
1497 if($i == 1 /* first page */ ||
1498 $i == $last_page /* last page */ ||
1499 $i == $current_page /* current page */ ||
1500 $i == ceil($last_page * 0.25) /* first quater */ ||
1501 $i == ceil($last_page * 0.5) /* half */ ||
1502 $i == ceil($last_page * 0.75) /* third quater */ ||
1503 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1504 (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 */ ||
1505 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1506 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1508 $page_select.= $select;
1516 $page_select.= "......... ";
1521 /* only show the page selector if we have more then one page */
1523 $this->tmpl->assign('page_selector', $page_select);
1526 $extern_link = "index.php?mode=showpi";
1527 $rss_link = "index.php?mode=rss";
1528 if($current_tags != "") {
1529 $extern_link.= "&tags=". $current_tags;
1530 $rss_link.= "&tags=". $current_tags;
1532 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1533 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1534 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1537 $export_link = "index.php?mode=export";
1538 $slideshow_link = "index.php?mode=slideshow";
1540 $this->tmpl->assign('extern_link', $extern_link);
1541 $this->tmpl->assign('slideshow_link', $slideshow_link);
1542 $this->tmpl->assign('export_link', $export_link);
1543 $this->tmpl->assign('rss_link', $rss_link);
1544 $this->tmpl->assign('count', $count);
1545 $this->tmpl->assign('width', $this->cfg->thumb_width);
1546 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1547 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1548 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1549 $this->tmpl->assign('images', $images);
1550 $this->tmpl->assign('img_width', $img_width);
1551 $this->tmpl->assign('img_height', $img_height);
1552 $this->tmpl->assign('img_id', $img_id);
1553 $this->tmpl->assign('img_name', $img_name);
1554 $this->tmpl->assign('img_fullname', $img_fullname);
1555 $this->tmpl->assign('img_title', $img_title);
1556 $this->tmpl->assign('img_rating', $img_rating);
1557 $this->tmpl->assign('thumbs', $thumbs);
1558 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1560 $result = $this->tmpl->fetch("photo_index.tpl");
1562 /* if we are returning to photo index from an photo-view,
1563 scroll the window to the last shown photo-thumbnail.
1564 after this, unset the last_photo session variable.
1566 if(isset($_SESSION['last_photo'])) {
1567 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1568 unset($_SESSION['last_photo']);
1573 } // showPhotoIndex()
1576 * show credit template
1578 public function showCredits()
1580 $this->tmpl->assign('version', $this->cfg->version);
1581 $this->tmpl->assign('product', $this->cfg->product);
1582 $this->tmpl->assign('db_version', $this->dbver);
1583 $this->tmpl->show("credits.tpl");
1588 * create thumbnails for the requested width
1590 * this function creates image thumbnails of $orig_image
1591 * stored as $thumb_image. It will check if the image is
1592 * in a supported format, if necessary rotate the image
1593 * (based on EXIF orientation meta headers) and re-sizing.
1594 * @param string $orig_image
1595 * @param string $thumb_image
1596 * @param integer $width
1599 public function create_thumbnail($orig_image, $thumb_image, $width)
1601 if(!file_exists($orig_image)) {
1605 $mime = $this->get_mime_info($orig_image);
1607 /* check if original photo is a support image type */
1608 if(!$this->checkifImageSupported($mime))
1615 $meta = $this->get_meta_informations($orig_image);
1621 if(isset($meta['Orientation'])) {
1622 switch($meta['Orientation']) {
1623 case 1: /* top, left */
1624 /* nothing to do */ break;
1625 case 2: /* top, right */
1626 $rotate = 0; $flip_hori = true; break;
1627 case 3: /* bottom, left */
1628 $rotate = 180; break;
1629 case 4: /* bottom, right */
1630 $flip_vert = true; break;
1631 case 5: /* left side, top */
1632 $rotate = 90; $flip_vert = true; break;
1633 case 6: /* right side, top */
1634 $rotate = 90; break;
1635 case 7: /* left side, bottom */
1636 $rotate = 270; $flip_vert = true; break;
1637 case 8: /* right side, bottom */
1638 $rotate = 270; break;
1642 $src_img = @imagecreatefromjpeg($orig_image);
1648 $src_img = @imagecreatefrompng($orig_image);
1652 case 'image/x-portable-pixmap':
1654 $src_img = new Imagick($orig_image);
1655 $handler = "imagick";
1660 if(!isset($src_img) || empty($src_img)) {
1661 print "Can't load image from ". $orig_image ."\n";
1669 /* grabs the height and width */
1670 $cur_width = imagesx($src_img);
1671 $cur_height = imagesy($src_img);
1673 // If requested width is more then the actual image width,
1674 // do not generate a thumbnail, instead safe the original
1675 // as thumbnail but with lower quality. But if the image
1676 // is to heigh too, then we still have to resize it.
1677 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1678 $result = imagejpeg($src_img, $thumb_image, 75);
1679 imagedestroy($src_img);
1686 $cur_width = $src_img->getImageWidth();
1687 $cur_height = $src_img->getImageHeight();
1689 // If requested width is more then the actual image width,
1690 // do not generate a thumbnail, instead safe the original
1691 // as thumbnail but with lower quality. But if the image
1692 // is to heigh too, then we still have to resize it.
1693 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1694 $src_img->setCompressionQuality(75);
1695 $src_img->setImageFormat('jpeg');
1696 $src_img->writeImage($thumb_image);
1698 $src_img->destroy();
1705 // If the image will be rotate because EXIF orientation said so
1706 // 'virtually rotate' the image for further calculations
1707 if($rotate == 90 || $rotate == 270) {
1709 $cur_width = $cur_height;
1713 /* calculates aspect ratio */
1714 $aspect_ratio = $cur_height / $cur_width;
1717 if($aspect_ratio < 1) {
1719 $new_h = abs($new_w * $aspect_ratio);
1721 /* 'virtually' rotate the image and calculate it's ratio */
1722 $tmp_w = $cur_height;
1723 $tmp_h = $cur_width;
1724 /* now get the ratio from the 'rotated' image */
1725 $tmp_ratio = $tmp_h/$tmp_w;
1726 /* now calculate the new dimensions */
1728 $tmp_h = abs($tmp_w * $tmp_ratio);
1730 // now that we know, how high they photo should be, if it
1731 // gets rotated, use this high to scale the image
1733 $new_w = abs($new_h / $aspect_ratio);
1735 // If the image will be rotate because EXIF orientation said so
1736 // now 'virtually rotate' back the image for the image manipulation
1737 if($rotate == 90 || $rotate == 270) {
1748 /* creates new image of that size */
1749 $dst_img = imagecreatetruecolor($new_w, $new_h);
1751 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1753 /* copies resized portion of original image into new image */
1754 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1756 /* needs the image to be flipped horizontal? */
1758 $this->_debug("(FLIP)");
1759 $dst_img = $this->flipImage($dst_img, 'hori');
1761 /* needs the image to be flipped vertical? */
1763 $this->_debug("(FLIP)");
1764 $dst_img = $this->flipImage($dst_img, 'vert');
1768 $this->_debug("(ROTATE)");
1769 $dst_img = $this->rotateImage($dst_img, $rotate);
1772 /* write down new generated file */
1773 $result = imagejpeg($dst_img, $thumb_image, 75);
1775 /* free your mind */
1776 imagedestroy($dst_img);
1777 imagedestroy($src_img);
1779 if($result === false) {
1780 print "Can't write thumbnail ". $thumb_image ."\n";
1790 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1792 /* needs the image to be flipped horizontal? */
1794 $this->_debug("(FLIP)");
1795 $src_img->rotateImage(new ImagickPixel(), 90);
1796 $src_img->flipImage();
1797 $src_img->rotateImage(new ImagickPixel(), -90);
1799 /* needs the image to be flipped vertical? */
1801 $this->_debug("(FLIP)");
1802 $src_img->flipImage();
1806 $this->_debug("(ROTATE)");
1807 $src_img->rotateImage(new ImagickPixel(), $rotate);
1810 $src_img->setCompressionQuality(75);
1811 $src_img->setImageFormat('jpeg');
1813 if(!$src_img->writeImage($thumb_image)) {
1814 print "Can't write thumbnail ". $thumb_image ."\n";
1819 $src_img->destroy();
1826 } // create_thumbnail()
1829 * return all exif meta data from the file
1830 * @param string $file
1833 public function get_meta_informations($file)
1835 return exif_read_data($file);
1837 } // get_meta_informations()
1840 * create phpfspot own sqlite database
1842 * this function creates phpfspots own sqlite database
1843 * if it does not exist yet. this own is used to store
1844 * some necessary informations (md5 sum's, ...).
1846 public function check_config_table()
1848 // if the config table doesn't exist yet, create it
1849 if(!$this->cfg_db->db_check_table_exists("images")) {
1850 $this->cfg_db->db_exec("
1851 CREATE TABLE images (
1852 img_idx int primary key,
1858 } // check_config_table
1861 * Generates a thumbnail from photo idx
1863 * This function will generate JPEG thumbnails from provided F-Spot photo
1866 * 1. Check if all thumbnail generations (width) are already in place and
1868 * 2. Check if the md5sum of the original file has changed
1869 * 3. Generate the thumbnails if needed
1870 * @param integer $idx
1871 * @param integer $force
1872 * @param boolean $overwrite
1874 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1878 $resolutions = Array(
1879 $this->cfg->thumb_width,
1880 $this->cfg->photo_width,
1881 $this->cfg->mini_width,
1885 /* get details from F-Spot's database */
1886 $details = $this->get_photo_details($idx);
1888 /* calculate file MD5 sum */
1889 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1891 if(!file_exists($full_path)) {
1892 $this->_error("File ". $full_path ." does not exist\n");
1896 if(!is_readable($full_path)) {
1897 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1901 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1903 /* If Nikon NEF format, we need to treat it another way */
1904 if(isset($this->cfg->dcraw_bin) &&
1905 file_exists($this->cfg->dcraw_bin) &&
1906 is_executable($this->cfg->dcraw_bin) &&
1907 preg_match('/\.nef$/i', $details['uri'])) {
1909 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1911 /* if PPM file does not exist, let dcraw convert it from NEF */
1912 if(!file_exists($ppm_path)) {
1913 system($this->cfg->dcraw_bin ." -a ". $full_path);
1916 /* for now we handle the PPM instead of the NEF */
1917 $full_path = $ppm_path;
1921 $file_md5 = md5_file($full_path);
1924 foreach($resolutions as $resolution) {
1926 $generate_it = false;
1928 $thumb_sub_path = substr($file_md5, 0, 2);
1929 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1931 /* if thumbnail-subdirectory does not exist yet, create it */
1932 if(!file_exists(dirname($thumb_path))) {
1933 mkdir(dirname($thumb_path), 0755);
1936 /* if the thumbnail file doesn't exist, create it */
1937 if(!file_exists($thumb_path)) {
1938 $generate_it = true;
1940 /* if the file hasn't changed there is no need to regen the thumb */
1941 elseif($file_md5 != $this->getMD5($idx) || $force) {
1942 $generate_it = true;
1945 if($generate_it || $overwrite) {
1947 $this->_debug(" ". $resolution ."px");
1948 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1956 $this->_debug(" already exist");
1959 /* set the new/changed MD5 sum for the current photo */
1961 $this->setMD5($idx, $file_md5);
1964 $this->_debug("\n");
1969 * returns stored md5 sum for a specific photo
1971 * this function queries the phpfspot database for a
1972 * stored MD5 checksum of the specified photo
1973 * @param integer $idx
1974 * @return string|null
1976 public function getMD5($idx)
1978 $result = $this->cfg_db->db_query("
1981 WHERE img_idx='". $idx ."'
1987 $img = $this->cfg_db->db_fetch_object($result);
1988 return $img['img_md5'];
1993 * set MD5 sum for the specific photo
1994 * @param integer $idx
1995 * @param string $md5
1997 private function setMD5($idx, $md5)
1999 $result = $this->cfg_db->db_exec("
2000 REPLACE INTO images (img_idx, img_md5)
2001 VALUES ('". $idx ."', '". $md5 ."')
2007 * store current tag condition
2009 * this function stores the current tag condition
2010 * (AND or OR) in the users session variables
2011 * @param string $mode
2014 public function setTagCondition($mode)
2016 $_SESSION['tag_condition'] = $mode;
2020 } // setTagCondition()
2023 * invoke tag & date search
2025 * this function will return all matching tags and store
2026 * them in the session variable selected_tags. furthermore
2027 * it also handles the date search.
2028 * getPhotoSelection() will then only return the matching
2032 public function startSearch()
2035 if(isset($_POST['date_from']) && $this->isValidDate($_POST['date_from'])) {
2036 $date_from = $_POST['date_from'];
2038 if(isset($_POST['date_to']) && $this->isValidDate($_POST['date_to'])) {
2039 $date_to = $_POST['date_to'];
2042 /* tag-name search */
2043 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2044 $searchfor_tag = $_POST['for_tag'];
2045 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2048 unset($_SESSION['searchfor_tag']);
2051 /* file-name search */
2052 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2053 $_SESSION['searchfor_name'] = $_POST['for_name'];
2056 unset($_SESSION['searchfor_name']);
2060 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2062 $_SESSION['rate_from'] = $_POST['rate_from'];
2064 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2065 $_SESSION['rate_to'] = $_POST['rate_to'];
2069 /* delete any previously set value */
2070 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2075 if(isset($date_from) && !empty($date_from))
2076 $_SESSION['from_date'] = strtotime($date_from ." 00:00:00");
2078 unset($_SESSION['from_date']);
2080 if(isset($date_to) && !empty($date_to))
2081 $_SESSION['to_date'] = strtotime($date_to ." 23:59:59");
2083 unset($_SESSION['to_date']);
2085 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2086 /* new search, reset the current selected tags */
2087 $_SESSION['selected_tags'] = Array();
2088 foreach($this->avail_tags as $tag) {
2089 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2090 array_push($_SESSION['selected_tags'], $tag);
2099 * updates sort order in session variable
2101 * this function is invoked by RPC and will sort the requested
2102 * sort order in the session variable.
2103 * @param string $sort_order
2106 public function updateSortOrder($order)
2108 if(isset($this->sort_orders[$order])) {
2109 $_SESSION['sort_order'] = $order;
2113 return "unkown error";
2115 } // updateSortOrder()
2120 * this function rotates the image according the
2122 * @param string $img
2123 * @param integer $degress
2126 private function rotateImage($img, $degrees)
2128 if(function_exists("imagerotate")) {
2129 $img = imagerotate($img, $degrees, 0);
2131 function imagerotate($src_img, $angle)
2133 $src_x = imagesx($src_img);
2134 $src_y = imagesy($src_img);
2135 if ($angle == 180) {
2139 elseif ($src_x <= $src_y) {
2143 elseif ($src_x >= $src_y) {
2148 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2149 imagealphablending($rotate, false);
2154 for ($y = 0; $y < ($src_y); $y++) {
2155 for ($x = 0; $x < ($src_x); $x++) {
2156 $color = imagecolorat($src_img, $x, $y);
2157 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2163 for ($y = 0; $y < ($src_y); $y++) {
2164 for ($x = 0; $x < ($src_x); $x++) {
2165 $color = imagecolorat($src_img, $x, $y);
2166 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2172 for ($y = 0; $y < ($src_y); $y++) {
2173 for ($x = 0; $x < ($src_x); $x++) {
2174 $color = imagecolorat($src_img, $x, $y);
2175 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2189 $img = imagerotate($img, $degrees);
2198 * returns flipped image
2200 * this function will return an either horizontal or
2201 * vertical flipped truecolor image.
2202 * @param string $image
2203 * @param string $mode
2206 private function flipImage($image, $mode)
2208 $w = imagesx($image);
2209 $h = imagesy($image);
2210 $flipped = imagecreatetruecolor($w, $h);
2214 for ($y = 0; $y < $h; $y++) {
2215 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2219 for ($x = 0; $x < $w; $x++) {
2220 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2230 * return all assigned tags for the specified photo
2231 * @param integer $idx
2234 private function get_photo_tags($idx)
2236 $result = $this->db->db_query("
2239 INNER JOIN photo_tags pt
2241 WHERE pt.photo_id='". $idx ."'
2246 while($row = $this->db->db_fetch_object($result)) {
2247 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2249 $tags[$row['id']] = $row['name'];
2254 } // get_photo_tags()
2257 * create on-the-fly images with text within
2258 * @param string $txt
2259 * @param string $color
2260 * @param integer $space
2261 * @param integer $font
2264 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2266 if (strlen($color) != 6)
2269 $int = hexdec($color);
2270 $h = imagefontheight($font);
2271 $fw = imagefontwidth($font);
2272 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2273 $lines = count($txt);
2274 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2275 $bg = imagecolorallocate($im, 255, 255, 255);
2276 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2279 foreach ($txt as $text) {
2280 $x = (($w - ($fw * strlen($text))) / 2);
2281 imagestring($im, $font, $x, $y, $text, $color);
2282 $y += ($h + $space);
2285 Header("Content-type: image/png");
2288 } // showTextImage()
2291 * check if all requirements are met
2294 private function check_requirements()
2296 if(!function_exists("imagecreatefromjpeg")) {
2297 print "PHP GD library extension is missing<br />\n";
2301 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2302 print "PHP SQLite3 library extension is missing<br />\n";
2306 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2307 ini_set('track_errors', 1);
2308 @include_once 'HTML/AJAX/Server.php';
2309 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2310 print "PEAR HTML_AJAX package is missing<br />\n";
2313 @include_once 'Calendar/Calendar.php';
2314 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2315 print "PEAR Calendar package is missing<br />\n";
2318 @include_once 'Console/Getopt.php';
2319 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2320 print "PEAR Console_Getopt package is missing<br />\n";
2323 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2324 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2325 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2328 ini_restore('track_errors');
2335 } // check_requirements()
2337 private function _debug($text)
2339 if(isset($this->fromcmd)) {
2346 * check if specified MIME type is supported
2347 * @param string $mime
2350 public function checkifImageSupported($mime)
2352 $supported_types = Array(
2355 "image/x-portable-pixmap",
2359 if(in_array($mime, $supported_types))
2364 } // checkifImageSupported()
2368 * @param string $text
2370 public function _error($text)
2372 switch($this->cfg->logging) {
2375 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2376 print $text ."<br />\n";
2382 error_log($text, 3, $his->cfg->log_file);
2386 $this->runtime_error = true;
2391 * get calendar input-text fields
2393 * this function returns a text-field used for the data selection.
2394 * Either it will be filled with the current date or, if available,
2395 * filled with the date user entered previously.
2397 * @param string $mode
2400 private function get_date_text_field($mode)
2402 $date = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2404 $date.= isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2406 $date.= isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2408 $output = "<input type=\"text\" size=\"15\" id=\"date_". $mode ."\" value=\"". $date ."\"";
2409 if(!isset($_SESSION[$mode .'_date']))
2410 $output.= " disabled=\"disabled\"";
2415 } // get_date_text_field()
2418 * output calendar matrix
2419 * @param integer $year
2420 * @param integer $month
2421 * @param integer $day
2423 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2425 if (!isset($year)) $year = date('Y');
2426 if (!isset($month)) $month = date('m');
2427 if (!isset($day)) $day = date('d');
2432 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2433 require_once CALENDAR_ROOT.'Day.php';
2436 $month = new Calendar_Month_Weekdays($year,$month);
2439 $prevStamp = $month->prevMonth(true);
2440 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2441 $nextStamp = $month->nextMonth(true);
2442 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2444 $selectedDays = array (
2445 new Calendar_Day($year,$month,$day),
2446 new Calendar_Day($year,12,25),
2449 // Build the days in the month
2450 $month->build($selectedDays);
2452 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2453 $this->tmpl->assign('prev_month', $prev);
2454 $this->tmpl->assign('next_month', $next);
2456 while ( $day = $month->fetch() ) {
2458 if(!isset($matrix[$rows]))
2459 $matrix[$rows] = Array();
2463 $dayStamp = $day->thisDay(true);
2464 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2466 // isFirst() to find start of week
2467 if ( $day->isFirst() )
2470 if ( $day->isSelected() ) {
2471 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2472 } else if ( $day->isEmpty() ) {
2473 $string.= "<td> </td>\n";
2475 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2478 // isLast() to find end of week
2479 if ( $day->isLast() )
2480 $string.= "</tr>\n";
2482 $matrix[$rows][$cols] = $string;
2492 $this->tmpl->assign('matrix', $matrix);
2493 $this->tmpl->assign('rows', $rows);
2494 $this->tmpl->show("calendar.tpl");
2496 } // get_calendar_matrix()
2499 * output export page
2500 * @param string $mode
2502 public function getExport($mode)
2504 $pictures = $this->getPhotoSelection();
2505 $current_tags = $this->getCurrentTags();
2507 foreach($pictures as $picture) {
2509 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2510 if($current_tags != "") {
2511 $orig_url.= "&tags=". $current_tags;
2513 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2514 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2517 if($this->is_user_friendly_url()) {
2518 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2521 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2527 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2528 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2532 // "[%pictureurl% %thumbnailurl%]"
2533 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2536 case 'MoinMoinList':
2537 // " * [%pictureurl% %thumbnailurl%]"
2538 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2549 public function getRSSFeed()
2551 Header("Content-type: text/xml; charset=utf-8");
2552 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2555 xmlns:media="http://search.yahoo.com/mrss/"
2556 xmlns:dc="http://purl.org/dc/elements/1.1/"
2559 <title>phpfspot</title>
2560 <description>phpfspot RSS feed</description>
2561 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2562 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2563 <generator>phpfspot</generator>
2566 $pictures = $this->getPhotoSelection();
2567 $current_tags = $this->getCurrentTags();
2569 foreach($pictures as $picture) {
2571 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2572 if($current_tags != "") {
2573 $orig_url.= "&tags=". $current_tags;
2575 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2576 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2579 $details = $this->get_photo_details($picture);
2581 if($this->is_user_friendly_url()) {
2582 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2585 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2588 $thumb_html = htmlspecialchars("
2589 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2591 ". $details['description']);
2593 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2595 /* get EXIF information if JPEG */
2596 if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2597 $meta = $this->get_meta_informations($orig_path);
2602 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2603 <link><?php print htmlspecialchars($orig_url); ?></link>
2604 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2605 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $details['time']); ?></dc:date.Taken>
2607 <?php print $thumb_html; ?>
2609 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $details['time']); ?></pubDate>
2624 * get all selected tags
2626 * This function will return all selected tags as one string, seperated
2630 private function getCurrentTags()
2633 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2634 foreach($_SESSION['selected_tags'] as $tag)
2635 $current_tags.= $tag .",";
2636 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2638 return $current_tags;
2640 } // getCurrentTags()
2643 * return the current photo
2645 public function getCurrentPhoto()
2647 if(isset($_SESSION['current_photo'])) {
2648 print $_SESSION['current_photo'];
2650 } // getCurrentPhoto()
2653 * tells the client browser what to do
2655 * this function is getting called via AJAX by the
2656 * client browsers. it will tell them what they have
2657 * to do next. This is necessary for directly jumping
2658 * into photo index or single photo view when the are
2659 * requested with specific URLs
2662 public function whatToDo()
2664 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2666 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2667 return "showpi_tags";
2669 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2676 * return the current process-user
2679 private function getuid()
2681 if($uid = posix_getuid()) {
2682 if($user = posix_getpwuid($uid)) {
2683 return $user['name'];
2692 * returns a select-dropdown box to select photo index sort parameters
2693 * @param array $params
2694 * @param smarty $smarty
2697 public function smarty_sort_select_list($params, &$smarty)
2701 foreach($this->sort_orders as $key => $value) {
2702 $output.= "<option value=\"". $key ."\"";
2703 if($key == $_SESSION['sort_order']) {
2704 $output.= " selected=\"selected\"";
2706 $output.= ">". $value ."</option>";
2711 } // smarty_sort_select_list()
2714 * returns the currently selected sort order
2717 private function get_sort_order()
2719 switch($_SESSION['sort_order']) {
2721 return " ORDER BY p.time ASC";
2724 return " ORDER BY p.time DESC";
2727 if($this->dbver < 9) {
2728 return " ORDER BY p.name ASC";
2731 return " ORDER BY basename(p.uri) ASC";
2735 if($this->dbver < 9) {
2736 return " ORDER BY p.name DESC";
2739 return " ORDER BY basename(p.uri) DESC";
2743 return " ORDER BY t.name ASC ,p.time ASC";
2746 return " ORDER BY t.name DESC ,p.time ASC";
2749 return " ORDER BY p.rating ASC, t.name ASC";
2752 return " ORDER BY p.rating DESC, t.name DESC";
2756 } // get_sort_order()
2759 * return the next to be shown slide show image
2761 * this function returns the URL of the next image
2762 * in the slideshow sequence.
2765 public function getNextSlideShowImage()
2767 $all_photos = $this->getPhotoSelection();
2769 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
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 } // getNextSlideShowImage()
2783 * return the previous to be shown slide show image
2785 * this function returns the URL of the previous image
2786 * in the slideshow sequence.
2789 public function getPrevSlideShowImage()
2791 $all_photos = $this->getPhotoSelection();
2793 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2794 $_SESSION['slideshow_img'] = 0;
2796 $_SESSION['slideshow_img']--;
2798 if($this->is_user_friendly_url()) {
2799 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2802 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2804 } // getPrevSlideShowImage()
2806 public function resetSlideShow()
2808 if(isset($_SESSION['slideshow_img']))
2809 unset($_SESSION['slideshow_img']);
2811 } // resetSlideShow()
2816 * this function will get all photos from the fspot
2817 * database and randomly return ONE entry
2819 * saddly there is yet no sqlite3 function which returns
2820 * the bulk result in array, so we have to fill up our
2824 public function get_random_photo()
2833 /* if show_tags is set, only return details for photos which
2834 are specified to be shown
2836 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2838 INNER JOIN photo_tags pt
2843 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2846 $result = $this->db->db_query($query_str);
2848 while($row = $this->db->db_fetch_object($result)) {
2849 array_push($all, $row['id']);
2852 return $all[array_rand($all)];
2854 } // get_random_photo()
2857 * get random photo tag photo
2859 * this function will get all photos tagged with the requested
2860 * tag from the fspot database and randomly return ONE entry
2862 * saddly there is yet no sqlite3 function which returns
2863 * the bulk result in array, so we have to fill up our
2867 public function get_random_tag_photo($tagidx)
2874 INNER JOIN photo_tags pt
2878 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2886 pt.tag_id LIKE '". $tagidx ."'
2889 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2892 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2896 $result = $this->db->db_query($query_str);
2898 while($row = $this->db->db_fetch_object($result)) {
2899 array_push($all, $row['id']);
2902 return $all[array_rand($all)];
2904 } // get_random_tag_photo()
2907 * validates provided date
2909 * this function validates if the provided date
2910 * contains a valid date and will return true
2912 * @param string $date_str
2915 public function isValidDate($date_str)
2917 $timestamp = strtotime($date_str);
2919 if(is_numeric($timestamp))
2927 * timestamp to string conversion
2928 * @param integer $timestamp
2931 private function ts2str($timestamp)
2933 if(!empty($timestamp) && is_numeric($timestamp))
2934 return strftime("%Y-%m-%d", $timestamp);
2939 * extract tag-names from $_GET['tags']
2940 * @param string $tags_str
2943 private function extractTags($tags_str)
2945 $not_validated = split(',', $tags_str);
2946 $validated = array();
2948 foreach($not_validated as $tag) {
2949 if(is_numeric($tag))
2950 array_push($validated, $tag);
2958 * returns the full path to a thumbnail
2959 * @param integer $width
2960 * @param integer $photo
2963 public function get_thumb_path($width, $photo)
2965 $md5 = $this->getMD5($photo);
2966 $sub_path = substr($md5, 0, 2);
2967 return $this->cfg->thumb_path
2975 } // get_thumb_path()
2978 * returns server's virtual host name
2981 private function get_server_name()
2983 return $_SERVER['SERVER_NAME'];
2984 } // get_server_name()
2987 * returns type of webprotocol which is currently used
2990 private function get_web_protocol()
2992 if(!isset($_SERVER['HTTPS']))
2996 } // get_web_protocol()
2999 * return url to this phpfspot installation
3002 private function get_phpfspot_url()
3004 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
3006 } // get_phpfspot_url()
3009 * returns the number of photos which are tagged with $tag_id
3010 * @param integer $tag_id
3013 public function get_num_photos($tag_id)
3015 if($result = $this->db->db_fetchSingleRow("
3016 SELECT count(*) as number
3019 tag_id LIKE '". $tag_id ."'")) {
3021 return $result['number'];
3027 } // get_num_photos()
3030 * check file exists and is readable
3032 * returns true, if everything is ok, otherwise false
3033 * if $silent is not set, this function will output and
3035 * @param string $file
3036 * @param boolean $silent
3039 private function check_readable($file, $silent = null)
3041 if(!file_exists($file)) {
3043 print "File \"". $file ."\" does not exist.\n";
3047 if(!is_readable($file)) {
3049 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3055 } // check_readable()
3058 * check if all needed indices are present
3060 * this function checks, if some needed indices are already
3061 * present, or if not, create them on the fly. they are
3062 * necessary to speed up some queries like that one look for
3063 * all tags, when show_tags is specified in the configuration.
3065 private function checkDbIndices()
3067 $result = $this->db->db_exec("
3068 CREATE INDEX IF NOT EXISTS
3075 } // checkDbIndices()
3078 * retrive F-Spot database version
3080 * this function will return the F-Spot database version number
3081 * It is stored within the sqlite3 database in the table meta
3082 * @return string|null
3084 public function getFspotDBVersion()
3086 if($result = $this->db->db_fetchSingleRow("
3087 SELECT data as version
3090 name LIKE 'F-Spot Database Version'
3092 return $result['version'];
3096 } // getFspotDBVersion()
3099 * parse the provided URI and will returned the requested chunk
3100 * @param string $uri
3101 * @param string $mode
3104 public function parse_uri($uri, $mode)
3106 if(($components = parse_url($uri)) !== false) {
3110 return basename($components['path']);
3113 return dirname($components['path']);
3116 return $components['path'];
3126 * validate config options
3128 * this function checks if all necessary configuration options are
3129 * specified and set.
3132 private function check_config_options()
3134 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3135 $this->_error("Please set \$page_title in phpfspot_cfg");
3137 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3138 $this->_error("Please set \$base_path in phpfspot_cfg");
3140 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3141 $this->_error("Please set \$web_path in phpfspot_cfg");
3143 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3144 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3146 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3147 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3149 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3150 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3152 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3153 $this->_error("Please set \$db_access in phpfspot_cfg");
3155 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3156 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3158 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3159 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3161 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3162 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3164 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3165 $this->_error("Please set \$photo_width in phpfspot_cfg");
3167 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3168 $this->_error("Please set \$mini_width in phpfspot_cfg");
3170 if(!isset($this->cfg->thumbs_per_page))
3171 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3173 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3174 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3176 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3177 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3179 if(!isset($this->cfg->hide_tags))
3180 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3182 if(!isset($this->cfg->theme_name))
3183 $this->_error("Please set \$theme_name in phpfspot_cfg");
3185 if(!isset($this->cfg->logging))
3186 $this->_error("Please set \$logging in phpfspot_cfg");
3188 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3190 if(!isset($this->cfg->log_file))
3191 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3193 if(!is_writeable($this->cfg->log_file))
3194 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3198 /* remove trailing slash, if set */
3199 if($this->cfg->web_path == "/")
3200 $this->cfg->web_path = "";
3201 elseif(preg_match('/\/$/', $this->cfg->web_path))
3202 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3204 return $this->runtime_error;
3206 } // check_config_options()
3209 * cleanup phpfspot own database
3211 * When photos are getting delete from F-Spot, there will remain
3212 * remain some residues in phpfspot own database. This function
3213 * will try to wipe them out.
3215 public function cleanup_phpfspot_db()
3217 $to_delete = Array();
3219 $result = $this->cfg_db->db_query("
3222 ORDER BY img_idx ASC
3225 while($row = $this->cfg_db->db_fetch_object($result)) {
3226 if(!$this->db->db_fetchSingleRow("
3229 WHERE id='". $row['img_idx'] ."'")) {
3231 array_push($to_delete, $row['img_idx'], ',');
3235 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3237 $this->cfg_db->db_exec("
3239 WHERE img_idx IN (". implode($to_delete) .")
3242 } // cleanup_phpfspot_db()
3245 * return first image of the page, the $current photo
3248 * this function is used to find out the first photo of the
3249 * current page, in which the $current photo lies. this is
3250 * used to display the correct photo, when calling showPhotoIndex()
3252 * @param integer $current
3253 * @param integer $max
3256 private function getCurrentPage($current, $max)
3258 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3259 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3260 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3266 } // getCurrentPage()
3271 * this function tries to find out the correct mime-type
3272 * for the provided file.
3273 * @param string $file
3276 public function get_mime_info($file)
3278 $details = getimagesize($file);
3280 /* if getimagesize() returns empty, try at least to find out the
3283 if(empty($details) && function_exists('mime_content_type')) {
3285 // mime_content_type is marked as deprecated in the documentation,
3286 // but is it really necessary to force users to install a PECL
3288 $details['mime'] = mime_content_type($file);
3291 return $details['mime'];
3293 } // get_mime_info()
3296 * return tag-name by tag-idx
3298 * this function returns the tag-name for the requested
3299 * tag specified by tag-idx.
3300 * @param integer $idx
3303 public function get_tag_name($idx)
3305 if($result = $this->db->db_fetchSingleRow("
3309 id LIKE '". $idx ."'")) {
3311 return $result['name'];
3320 * parse user friendly url which got rewritten by the websever
3321 * @param string $request_uri
3324 private function parse_user_friendly_url($request_uri)
3326 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3328 $options = explode('/', $request_uri);
3330 switch($options[1]) {
3332 if(is_numeric($options[2])) {
3333 $this->session_cleanup();
3334 //unset($_SESSION['start_action']);
3335 //unset($_SESSION['selected_tags']);
3336 $_GET['mode'] = 'showp';
3337 return $this->showPhoto($options[2]);
3341 if(is_numeric($options[2])) {
3342 require_once "phpfspot_img.php";
3343 $img = new PHPFSPOT_IMG;
3344 if(isset($options[3]) && is_numeric($options[3]))
3345 $img->showImg($options[2], $options[3]);
3347 $img->showImg($options[2]);
3352 if(is_numeric($options[2])) {
3353 $this->session_cleanup();
3354 $_GET['tags'] = $options[2];
3355 $_SESSION['selected_tags'] = Array($options[2]);
3356 if(isset($options[3]) && is_numeric($options[3]))
3357 $_SESSION['begin_with'] = $options[3];
3358 return $this->showPhotoIndex();
3364 } // parse_user_friendly_url()
3367 * check if user-friendly-urls are enabled
3369 * this function will return true, if the config option
3370 * $user_friendly_url has been set. Otherwise false.
3373 private function is_user_friendly_url()
3375 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3380 } // is_user_friendly_url()
3385 * this function will cleanup user's session information
3387 private function session_cleanup()
3389 unset($_SESSION['begin_with']);
3390 $this->resetDateSearch();
3391 $this->resetPhotoView();
3392 $this->resetTagSearch();
3393 $this->resetNameSearch();
3394 $this->resetDateSearch();
3397 } // session_cleanup()