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(!isset($_SESSION['sort_order']))
231 $_SESSION['sort_order'] = 'date_desc';
233 if(!isset($_SESSION['searchfor_tag']))
234 $_SESSION['searchfor_tag'] = '';
236 // if begin_with is still set but thumbs_per_page is now 0, unset it
237 if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
238 unset($_SESSION['begin_with']);
240 // if user-friendly-url's are enabled, set also a flag for the template handler
241 if($this->is_user_friendly_url()) {
242 $this->tmpl->assign('user_friendly_url', 'true');
247 public function __destruct()
253 * show - generate html output
255 * this function can be called after the constructor has
256 * prepared everyhing. it will load the index.tpl smarty
257 * template. if necessary it will registere pre-selects
258 * (photo index, photo, tag search, date search) into
261 public function show()
263 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
264 $this->tmpl->assign('page_title', $this->cfg->page_title);
265 $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
266 $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
269 if($this->is_user_friendly_url()) {
270 $content = $this->parse_user_friendly_url($_SERVER['REQUEST_URI']);
273 if(isset($_GET['mode'])) {
275 $_SESSION['start_action'] = $_GET['mode'];
277 switch($_GET['mode']) {
279 if(isset($_GET['tags'])) {
280 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
282 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
283 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
285 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
286 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
290 if(isset($_GET['tags'])) {
291 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
292 $_SESSION['start_action'] = 'showp';
294 if(isset($_GET['id']) && is_numeric($_GET['id'])) {
295 $_SESSION['current_photo'] = $_GET['id'];
296 $_SESSION['start_action'] = 'showp';
298 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
299 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
301 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
302 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
306 /* fetch export template */
307 print $this->tmpl->fetch("export.tpl");
308 /* no further execution necessary. */
312 /* fetch slideshow template */
313 print $this->tmpl->show("slideshow.tpl");
314 /* no further execution necessary. */
318 if(isset($_GET['tags'])) {
319 $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
321 if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
322 $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
324 if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
325 $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
333 /* if date-search variables are registered in the session, set the check
334 for "consider date-range" in the html output
336 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
337 $this->tmpl->assign('date_search_enabled', true);
339 /* if rate-search variables are registered in the session, set the check
340 for "consider rate-range" in the html output
342 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
343 $this->tmpl->assign('rate_search_enabled', true);
346 $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
347 $this->tmpl->assign('search_from_date', $this->get_date_text_field('from'));
348 $this->tmpl->assign('search_to_date', $this->get_date_text_field('to'));
350 $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags());
351 $this->tmpl->assign('preset_available_tags', $this->getAvailableTags());
352 $this->tmpl->assign('rate_search', $this->get_rate_search());
354 /* if no site-content has been set yet... */
355 if(!isset($content)) {
356 /* if tags are already selected, we can immediately display photo-index */
357 if((isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']) &&
358 isset($_SESSION['start_action']) && $_SESSION['start_action'] != 'showp') ||
359 (isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi'))
360 $this->tmpl->assign('initial_content', $this->showPhotoIndex());
362 /* if a photo is already selected, we can immediately display single-photo */
363 if(isset($_SESSION['current_photo']) && !empty($_SESSION['current_photo']))
364 $this->tmpl->assign('initial_content', $this->showPhoto($_SESSION['current_photo']));
366 /* ok, then let us show the welcome page... */
367 $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl'));
372 $this->tmpl->assign('initial_content', $content);
374 $this->tmpl->show("index.tpl");
379 * get_tags - grab all tags of f-spot's database
381 * this function will get all available tags from
382 * the f-spot database and store them within two
383 * arrays within this class for later usage. in
384 * fact, if the user requests (hide_tags) it will
385 * opt-out some of them.
387 * this function is getting called once by show()
389 private function get_tags()
391 $this->avail_tags = Array();
394 /* if show_tags has been set in the configuration (only show photos
395 which are tagged by these tags) they following will take care,
396 that only these other tags are displayed where the photo is also
397 tagged with one of show_tags.
399 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
402 DISTINCT t1.id as id, t1.name as name
405 INNER JOIN photo_tags
406 pt2 ON pt1.photo_id=pt2.photo_id
412 t2.name IN ('".implode("','",$this->cfg->show_tags)."')
414 t1.sort_priority ASC";
416 $result = $this->db->db_query($query_str);
420 $result = $this->db->db_query("
423 ORDER BY sort_priority ASC
427 while($row = $this->db->db_fetch_object($result)) {
429 $tag_id = $row['id'];
430 $tag_name = $row['name'];
432 /* if the user has specified to ignore this tag in phpfspot's
433 configuration, ignore it here so it does not get added to
436 if(in_array($row['name'], $this->cfg->hide_tags))
439 /* if you include the following if-clause and the user has specified
440 to only show certain tags which are specified in phpfspot's
441 configuration, ignore all others so they will not be added to the
443 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
444 !in_array($row['name'], $this->cfg->show_tags))
448 $this->tags[$tag_id] = $tag_name;
449 $this->avail_tags[$count] = $tag_id;
457 * extract all photo details
459 * retrieve all available details from f-spot's
460 * database and return them as object
461 * @param integer $idx
462 * @return object|null
464 public function get_photo_details($idx)
466 /* ~ F-Spot version 0.3.x */
467 if($this->dbver < 9) {
469 SELECT p.id, p.name, p.time, p.directory_path, p.description
474 /* till F-Spot version 0.4.1 */
475 if($this->dbver < 11) {
477 SELECT p.id, p.uri, p.time, p.description
483 SELECT p.id, p.uri, p.time, p.description, p.rating
489 /* if show_tags is set, only return details of photos which are
490 tagged with a tag that has been specified to be shown.
492 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
494 INNER JOIN photo_tags pt
498 WHERE p.id='". $idx ."'
499 AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
503 WHERE p.id='". $idx ."'
507 if($result = $this->db->db_query($query_str)) {
509 $row = $this->db->db_fetch_object($result);
511 /* before F-Spot db version 9 there was no uri column but seperated
512 columns for directory_path and name (= filename).
514 if($this->dbver < 9) {
515 $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
524 } // get_photo_details
527 * returns aligned photo names
529 * this function returns aligned (length) names for a specific photo.
530 * If the length of the name exceeds $limit the name will bei
533 * @param integer $idx
534 * @param integer $limit
535 * @return string|null
537 public function getPhotoName($idx, $limit = 0)
539 if($details = $this->get_photo_details($idx)) {
540 if($long_name = $this->parse_uri($details['uri'], 'filename')) {
541 $name = $this->shrink_text($long_name, $limit);
551 * get photo rating level
553 * this function will return the integer-based rating level of a
554 * photo. This can only be done, if the F-Spot database is at a
555 * specific version. If rating value can not be found, zero will
556 * be returned indicating no rating value is available.
561 public function get_photo_rating($idx)
563 if($detail = $this->get_photo_details($idx)) {
564 if(isset($detail['rating']))
565 return $detail['rating'];
570 } // get_photo_rating()
573 * get rate-search bars
575 * this function will return the rating-bars for the search field.
579 public function get_rate_search()
583 for($i = 1; $i <= 5; $i++) {
585 $bar.= "<img id=\"rate_from_". $i ."\" src=\"";
587 if(isset($_SESSION['rate_from']) && $i <= $_SESSION['rate_from'])
588 $bar.= $this->cfg->web_path ."/resources/star.png";
590 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
593 onmouseover=\"show_rate('from', ". $i .");\"
594 onmouseout=\"reset_rate('from');\"
595 onclick=\"set_rate('from', ". $i .")\" />";
600 for($i = 1; $i <= 5; $i++) {
602 $bar.= "<img id=\"rate_to_". $i ."\" src=\"";
604 if(isset($_SESSION['rate_to']) && $i <= $_SESSION['rate_to'])
605 $bar.= $this->cfg->web_path ."/resources/star.png";
607 $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
610 onmouseover=\"show_rate('to', ". $i .");\"
611 onmouseout=\"reset_rate('to');\"
612 onclick=\"set_rate('to', ". $i .");\" />";
617 } // get_rate_search()
620 * shrink text according provided limit
622 * If the length of the name exceeds $limit, text will be shortend
623 * and inner content will be replaced with "...".
626 * @param integer $limit
629 private function shrink_text($text, $limit)
631 if($limit != 0 && strlen($text) > $limit) {
632 $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
640 * translate f-spoth photo path
642 * as the full-qualified path recorded in the f-spot database
643 * is usally not the same as on the webserver, this function
644 * will replace the path with that one specified in the cfg
645 * @param string $path
648 public function translate_path($path)
650 return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
655 * control HTML ouput for a single photo
657 * this function provides all the necessary information
658 * for the single photo template.
659 * @param integer photo
661 public function showPhoto($photo)
663 /* get all photos from the current photo selection */
664 $all_photos = $this->getPhotoSelection();
665 $count = count($all_photos);
667 for($i = 0; $i < $count; $i++) {
669 // $get_next will be set, when the photo which has to
670 // be displayed has been found - this means that the
671 // next available is in fact the NEXT image (for the
673 if(isset($get_next)) {
674 $next_img = $all_photos[$i];
678 /* the next photo is our NEXT photo */
679 if($all_photos[$i] == $photo) {
683 $previous_img = $all_photos[$i];
686 if($photo == $all_photos[$i]) {
691 $details = $this->get_photo_details($photo);
698 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
699 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
701 if(!file_exists($orig_path)) {
702 $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
706 if(!is_readable($orig_path)) {
707 $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
711 /* If the thumbnail doesn't exist yet, try to create it */
712 if(!file_exists($thumb_path)) {
713 $this->gen_thumb($photo, true);
714 $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
717 /* get mime-type, height and width from the original photo */
718 $info = getimagesize($orig_path);
720 /* get EXIF information if JPEG */
721 if(isset($info['mime']) && $info['mime'] == "image/jpeg") {
722 $meta = $this->get_meta_informations($orig_path);
725 /* If EXIF data are available, use them */
726 if(isset($meta['ExifImageWidth'])) {
727 $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
729 $meta_res = $info[0] ."x". $info[1];
732 $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
733 $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
735 $extern_link = "index.php?mode=showp&id=". $photo;
736 $current_tags = $this->getCurrentTags();
737 if($current_tags != "") {
738 $extern_link.= "&tags=". $current_tags;
740 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
741 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
744 $this->tmpl->assign('extern_link', $extern_link);
746 if(!file_exists($thumb_path)) {
747 $this->_error("Can't open file ". $thumb_path ."\n");
751 $info_thumb = getimagesize($thumb_path);
753 $this->tmpl->assign('description', $details['description']);
754 $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
755 $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
757 $this->tmpl->assign('width', $info_thumb[0]);
758 $this->tmpl->assign('height', $info_thumb[1]);
759 $this->tmpl->assign('ExifMadeOn', strftime("%a %x %X", $details['time']));
760 $this->tmpl->assign('ExifMadeWith', $meta_make);
761 $this->tmpl->assign('ExifOrigResolution', $meta_res);
762 $this->tmpl->assign('ExifFileSize', $meta_size);
764 if($this->is_user_friendly_url()) {
765 $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width);
766 $this->tmpl->assign('image_url_full', '/photo/'. $photo);
769 $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
770 $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
773 $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
775 $this->tmpl->assign('tags', $this->get_photo_tags($photo));
776 $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
777 $this->tmpl->assign('current_img', $photo);
779 if(isset($previous_img)) {
780 $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
781 $this->tmpl->assign('prev_img', $previous_img);
784 if(isset($next_img)) {
785 $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
786 $this->tmpl->assign('next_img', $next_img);
789 $this->tmpl->assign('mini_width', $this->cfg->mini_width);
790 $this->tmpl->assign('photo_width', $this->cfg->photo_width);
791 $this->tmpl->assign('photo_number', $i);
792 $this->tmpl->assign('photo_count', count($all_photos));
794 return $this->tmpl->fetch("single_photo.tpl");
799 * all available tags and tag cloud
801 * this function outputs all available tags (time ordered)
802 * and in addition output them as tag cloud (tags which have
803 * many photos will appears more then others)
805 public function getAvailableTags()
807 /* retrive tags from database */
812 $result = $this->db->db_query("
813 SELECT tag_id as id, count(tag_id) as quantity
823 while($row = $this->db->db_fetch_object($result)) {
824 $tags[$row['id']] = $row['quantity'];
827 // change these font sizes if you will
828 $max_size = 125; // max font size in %
829 $min_size = 75; // min font size in %
832 $max_sat = hexdec('cc');
833 $min_sat = hexdec('44');
835 // get the largest and smallest array values
836 $max_qty = max(array_values($tags));
837 $min_qty = min(array_values($tags));
839 // find the range of values
840 $spread = $max_qty - $min_qty;
841 if (0 == $spread) { // we don't want to divide by zero
845 // determine the font-size increment
846 // this is the increase per tag quantity (times used)
847 $step = ($max_size - $min_size)/($spread);
848 $step_sat = ($max_sat - $min_sat)/($spread);
850 // loop through our tag array
851 foreach ($tags as $key => $value) {
853 /* has the currently processed tag already been added to
854 the selected tag list? if so, ignore it here...
856 if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
859 // calculate CSS font-size
860 // find the $value in excess of $min_qty
861 // multiply by the font-size increment ($size)
862 // and add the $min_size set above
863 $size = $min_size + (($value - $min_qty) * $step);
864 // uncomment if you want sizes in whole %:
867 $color = $min_sat + ($value - $min_qty) * $step_sat;
873 if(isset($this->tags[$key])) {
874 if($this->is_user_friendly_url()) {
875 $output.= "<a href=\"". $this->cfg->web_path ."/tag/". $key ."\"
876 onclick=\"Tags('add', ". $key ."); return false;\"
878 style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\"
879 title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] ."</a>, ";
882 $output.= "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi\"
883 onclick=\"Tags('add', ". $key ."); return false;\"
885 style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\"
886 title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] ."</a>, ";
891 $output = substr($output, 0, strlen($output)-2);
894 } // getAvailableTags()
897 * output all selected tags
899 * this function output all tags which have been selected
900 * by the user. the selected tags are stored in the
901 * session-variable $_SESSION['selected_tags']
904 public function getSelectedTags($type = 'link')
906 /* retrive tags from database */
911 foreach($this->avail_tags as $tag)
913 // return all selected tags
914 if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
919 $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
923 <div class=\"tagresulttag\">
924 <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
925 <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
935 $output = substr($output, 0, strlen($output)-2);
939 return "no tags selected";
942 } // getSelectedTags()
945 * add tag to users session variable
947 * this function will add the specified to users current
948 * tag selection. if a date search has been made before
949 * it will be now cleared
952 public function addTag($tag)
954 if(!isset($_SESSION['selected_tags']))
955 $_SESSION['selected_tags'] = Array();
957 if(isset($_SESSION['searchfor_tag']))
958 unset($_SESSION['searchfor_tag']);
960 // has the user requested to hide this tag, and still someone,
961 // somehow tries to add it, don't allow this.
962 if(!isset($this->cfg->hide_tags) &&
963 in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
966 if(!in_array($tag, $_SESSION['selected_tags']))
967 array_push($_SESSION['selected_tags'], $tag);
974 * remove tag to users session variable
976 * this function removes the specified tag from
977 * users current tag selection
981 public function delTag($tag)
983 if(isset($_SESSION['searchfor_tag']))
984 unset($_SESSION['searchfor_tag']);
986 if(isset($_SESSION['selected_tags'])) {
987 $key = array_search($tag, $_SESSION['selected_tags']);
988 unset($_SESSION['selected_tags'][$key]);
989 sort($_SESSION['selected_tags']);
997 * reset tag selection
999 * if there is any tag selection, it will be
1002 public function resetTags()
1004 if(isset($_SESSION['selected_tags']))
1005 unset($_SESSION['selected_tags']);
1010 * returns the value for the autocomplete tag-search
1013 public function get_xml_tag_list()
1015 if(!isset($_GET['search']) || !is_string($_GET['search']))
1016 $_GET['search'] = '';
1021 /* retrive tags from database */
1024 $matched_tags = Array();
1026 header("Content-Type: text/xml");
1028 $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
1029 $string.= "<results>\n";
1031 foreach($this->avail_tags as $tag)
1033 if(!empty($_GET['search']) &&
1034 preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
1035 count($matched_tags) < $length) {
1037 $count = $this->get_num_photos($tag);
1040 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1043 $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1049 /* if we have collected enough items, break out */
1050 if(count($matched_tags) >= $length)
1054 $string.= "</results>\n";
1058 } // get_xml_tag_list()
1062 * reset single photo
1064 * if a specific photo was requested (external link)
1065 * unset the session variable now
1067 public function resetPhotoView()
1069 if(isset($_SESSION['current_photo']))
1070 unset($_SESSION['current_photo']);
1072 } // resetPhotoView();
1077 * if any tag search has taken place, reset it now
1079 public function resetTagSearch()
1081 if(isset($_SESSION['searchfor_tag']))
1082 unset($_SESSION['searchfor_tag']);
1084 } // resetTagSearch()
1089 * if any name search has taken place, reset it now
1091 public function resetNameSearch()
1093 if(isset($_SESSION['searchfor_name']))
1094 unset($_SESSION['searchfor_name']);
1096 } // resetNameSearch()
1101 * if any date search has taken place, reset it now.
1103 public function resetDateSearch()
1105 if(isset($_SESSION['from_date']))
1106 unset($_SESSION['from_date']);
1107 if(isset($_SESSION['to_date']))
1108 unset($_SESSION['to_date']);
1110 } // resetDateSearch();
1115 * if any rate search has taken place, reset it now.
1117 public function resetRateSearch()
1119 if(isset($_SESSION['rate_from']))
1120 unset($_SESSION['rate_from']);
1121 if(isset($_SESSION['rate_to']))
1122 unset($_SESSION['rate_to']);
1124 } // resetRateSearch();
1127 * return all photo according selection
1129 * this function returns all photos based on
1130 * the tag-selection, tag- or date-search.
1131 * the tag-search also has to take care of AND
1132 * and OR conjunctions
1135 public function getPhotoSelection()
1137 $matched_photos = Array();
1138 $additional_where_cond = "";
1140 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1141 $from_date = $_SESSION['from_date'];
1142 $to_date = $_SESSION['to_date'];
1143 $additional_where_cond.= "
1144 p.time>='". $from_date ."'
1146 p.time<='". $to_date ."'
1150 if(isset($_SESSION['searchfor_name'])) {
1152 /* check for previous conditions. if so add 'AND' */
1153 if(!empty($additional_where_cond)) {
1154 $additional_where_cond.= " AND ";
1157 if($this->dbver < 9) {
1158 $additional_where_cond.= "
1160 p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1162 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1167 $additional_where_cond.= "
1169 basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1171 p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1177 /* limit result based on rate-search */
1178 if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1180 if($this->dbver > 10) {
1182 /* check for previous conditions. if so add 'AND' */
1183 if(!empty($additional_where_cond)) {
1184 $additional_where_cond.= " AND ";
1187 $additional_where_cond.= "
1188 p.rating >= ". $_SESSION['rate_from'] ."
1190 p.rating <= ". $_SESSION['rate_to'] ."
1195 if(isset($_SESSION['sort_order'])) {
1196 $order_str = $this->get_sort_order();
1199 /* return a search result */
1200 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1202 SELECT DISTINCT pt1.photo_id
1204 INNER JOIN photo_tags pt2
1205 ON pt1.photo_id=pt2.photo_id
1209 ON pt1.photo_id=p.id
1212 WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1214 if(!empty($additional_where_cond))
1215 $query_str.= "AND ". $additional_where_cond ." ";
1217 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1218 $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1221 if(isset($order_str))
1222 $query_str.= $order_str;
1224 $result = $this->db->db_query($query_str);
1225 while($row = $this->db->db_fetch_object($result)) {
1226 array_push($matched_photos, $row['photo_id']);
1228 return $matched_photos;
1231 /* return according the selected tags */
1232 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1234 foreach($_SESSION['selected_tags'] as $tag)
1235 $selected.= $tag .",";
1236 $selected = substr($selected, 0, strlen($selected)-1);
1238 /* photo has to match at least on of the selected tags */
1239 if($_SESSION['tag_condition'] == 'or') {
1241 SELECT DISTINCT pt1.photo_id
1243 INNER JOIN photo_tags pt2
1244 ON pt1.photo_id=pt2.photo_id
1248 ON pt1.photo_id=p.id
1249 WHERE pt1.tag_id IN (". $selected .")
1251 if(!empty($additional_where_cond))
1252 $query_str.= "AND ". $additional_where_cond ." ";
1254 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1255 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1258 if(isset($order_str))
1259 $query_str.= $order_str;
1261 /* photo has to match all selected tags */
1262 elseif($_SESSION['tag_condition'] == 'and') {
1264 if(count($_SESSION['selected_tags']) >= 32) {
1265 print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1266 print "evaluate your tag selection. Please remove some tags from your selection.\n";
1270 /* Join together a table looking like
1272 pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1274 so the query can quickly return all images matching the
1275 selected tags in an AND condition
1280 SELECT DISTINCT pt1.photo_id
1284 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1291 for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1293 INNER JOIN photo_tags pt". ($i+2) ."
1294 ON pt1.photo_id=pt". ($i+2) .".photo_id
1299 ON pt1.photo_id=p.id
1301 $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1302 for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1304 AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1307 if(!empty($additional_where_cond))
1308 $query_str.= "AND ". $additional_where_cond;
1310 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1311 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1314 if(isset($order_str))
1315 $query_str.= $order_str;
1319 $result = $this->db->db_query($query_str);
1320 while($row = $this->db->db_fetch_object($result)) {
1321 array_push($matched_photos, $row['photo_id']);
1323 return $matched_photos;
1326 /* return all available photos */
1328 SELECT DISTINCT p.id
1330 LEFT JOIN photo_tags pt
1336 if(!empty($additional_where_cond))
1337 $query_str.= "WHERE ". $additional_where_cond ." ";
1339 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1340 if(!empty($additional_where_cond))
1341 $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1343 $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1346 if(isset($order_str))
1347 $query_str.= $order_str;
1349 $result = $this->db->db_query($query_str);
1350 while($row = $this->db->db_fetch_object($result)) {
1351 array_push($matched_photos, $row['id']);
1353 return $matched_photos;
1355 } // getPhotoSelection()
1358 * control HTML ouput for photo index
1360 * this function provides all the necessary information
1361 * for the photo index template.
1364 public function showPhotoIndex()
1366 $photos = $this->getPhotoSelection();
1367 $current_tags = $this->getCurrentTags();
1369 $count = count($photos);
1371 /* if all thumbnails should be shown on one page */
1372 if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1376 /* thumbnails should be splitted up in several pages */
1377 elseif($this->cfg->thumbs_per_page > 0) {
1379 if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1383 $begin_with = $_SESSION['begin_with'];
1386 $end_with = $begin_with + $this->cfg->thumbs_per_page;
1390 $images[$thumbs] = Array();
1391 $img_height[$thumbs] = Array();
1392 $img_width[$thumbs] = Array();
1393 $img_id[$thumbs] = Array();
1394 $img_name[$thumbs] = Array();
1395 $img_fullname[$thumbs] = Array();
1396 $img_title = Array();
1397 $img_rating = Array();
1399 for($i = $begin_with; $i < $end_with; $i++) {
1401 if(isset($photos[$i])) {
1403 $images[$thumbs] = $photos[$i];
1404 $img_id[$thumbs] = $i;
1405 $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1406 $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1407 $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1408 $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1410 $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
1412 if(file_exists($thumb_path)) {
1413 $info = getimagesize($thumb_path);
1414 $img_width[$thumbs] = $info[0];
1415 $img_height[$thumbs] = $info[1];
1421 // +1 for for smarty's selection iteration
1424 if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1425 $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1427 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1428 $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1429 $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1432 if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1433 $this->tmpl->assign('tag_result', 1);
1436 /* do we have to display the page selector ? */
1437 if($this->cfg->thumbs_per_page != 0) {
1441 /* calculate the page switchers */
1442 $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1443 $next_start = $begin_with + $this->cfg->thumbs_per_page;
1445 if($begin_with != 0)
1446 $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
1447 if($end_with < $count)
1448 $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
1450 $photo_per_page = $this->cfg->thumbs_per_page;
1451 $last_page = ceil($count / $photo_per_page);
1453 /* get the current selected page */
1454 if($begin_with == 0) {
1458 for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1465 for($i = 1; $i <= $last_page; $i++) {
1467 if($current_page == $i)
1468 $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1469 elseif($current_page-1 == $i || $current_page+1 == $i)
1470 $style = "style=\"font-size: 105%;\"";
1471 elseif(($current_page-5 >= $i) && ($i != 1) ||
1472 ($current_page+5 <= $i) && ($i != $last_page))
1473 $style = "style=\"font-size: 75%;\"";
1477 $start_with = ($i*$photo_per_page)-$photo_per_page;
1479 if($this->is_user_friendly_url()) {
1480 $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1483 $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi tags=". $current_tags ." begin_with=". $begin_with ."\"";
1485 $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1489 $select.= ">". $i ."</a> ";
1491 // until 9 pages we show the selector from 1-9
1492 if($last_page <= 9) {
1493 $page_select.= $select;
1496 if($i == 1 /* first page */ ||
1497 $i == $last_page /* last page */ ||
1498 $i == $current_page /* current page */ ||
1499 $i == ceil($last_page * 0.25) /* first quater */ ||
1500 $i == ceil($last_page * 0.5) /* half */ ||
1501 $i == ceil($last_page * 0.75) /* third quater */ ||
1502 (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1503 (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 */ ||
1504 $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1505 $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1507 $page_select.= $select;
1515 $page_select.= "......... ";
1520 /* only show the page selector if we have more then one page */
1522 $this->tmpl->assign('page_selector', $page_select);
1525 $extern_link = "index.php?mode=showpi";
1526 $rss_link = "index.php?mode=rss";
1527 if($current_tags != "") {
1528 $extern_link.= "&tags=". $current_tags;
1529 $rss_link.= "&tags=". $current_tags;
1531 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1532 $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1533 $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1536 $export_link = "index.php?mode=export";
1537 $slideshow_link = "index.php?mode=slideshow";
1539 $this->tmpl->assign('extern_link', $extern_link);
1540 $this->tmpl->assign('slideshow_link', $slideshow_link);
1541 $this->tmpl->assign('export_link', $export_link);
1542 $this->tmpl->assign('rss_link', $rss_link);
1543 $this->tmpl->assign('count', $count);
1544 $this->tmpl->assign('width', $this->cfg->thumb_width);
1545 $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1546 $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1547 $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1548 $this->tmpl->assign('images', $images);
1549 $this->tmpl->assign('img_width', $img_width);
1550 $this->tmpl->assign('img_height', $img_height);
1551 $this->tmpl->assign('img_id', $img_id);
1552 $this->tmpl->assign('img_name', $img_name);
1553 $this->tmpl->assign('img_fullname', $img_fullname);
1554 $this->tmpl->assign('img_title', $img_title);
1555 $this->tmpl->assign('img_rating', $img_rating);
1556 $this->tmpl->assign('thumbs', $thumbs);
1557 $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1559 $result = $this->tmpl->fetch("photo_index.tpl");
1561 /* if we are returning to photo index from an photo-view,
1562 scroll the window to the last shown photo-thumbnail.
1563 after this, unset the last_photo session variable.
1565 if(isset($_SESSION['last_photo'])) {
1566 $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1567 unset($_SESSION['last_photo']);
1572 } // showPhotoIndex()
1575 * show credit template
1577 public function showCredits()
1579 $this->tmpl->assign('version', $this->cfg->version);
1580 $this->tmpl->assign('product', $this->cfg->product);
1581 $this->tmpl->assign('db_version', $this->dbver);
1582 $this->tmpl->show("credits.tpl");
1587 * create thumbnails for the requested width
1589 * this function creates image thumbnails of $orig_image
1590 * stored as $thumb_image. It will check if the image is
1591 * in a supported format, if necessary rotate the image
1592 * (based on EXIF orientation meta headers) and re-sizing.
1593 * @param string $orig_image
1594 * @param string $thumb_image
1595 * @param integer $width
1598 public function create_thumbnail($orig_image, $thumb_image, $width)
1600 if(!file_exists($orig_image)) {
1604 $mime = $this->get_mime_info($orig_image);
1606 /* check if original photo is a support image type */
1607 if(!$this->checkifImageSupported($mime))
1614 $meta = $this->get_meta_informations($orig_image);
1620 if(isset($meta['Orientation'])) {
1621 switch($meta['Orientation']) {
1622 case 1: /* top, left */
1623 /* nothing to do */ break;
1624 case 2: /* top, right */
1625 $rotate = 0; $flip_hori = true; break;
1626 case 3: /* bottom, left */
1627 $rotate = 180; break;
1628 case 4: /* bottom, right */
1629 $flip_vert = true; break;
1630 case 5: /* left side, top */
1631 $rotate = 90; $flip_vert = true; break;
1632 case 6: /* right side, top */
1633 $rotate = 90; break;
1634 case 7: /* left side, bottom */
1635 $rotate = 270; $flip_vert = true; break;
1636 case 8: /* right side, bottom */
1637 $rotate = 270; break;
1641 $src_img = @imagecreatefromjpeg($orig_image);
1647 $src_img = @imagecreatefrompng($orig_image);
1651 case 'image/x-portable-pixmap':
1653 $src_img = new Imagick($orig_image);
1654 $handler = "imagick";
1659 if(!isset($src_img) || empty($src_img)) {
1660 print "Can't load image from ". $orig_image ."\n";
1668 /* grabs the height and width */
1669 $cur_width = imagesx($src_img);
1670 $cur_height = imagesy($src_img);
1672 // If requested width is more then the actual image width,
1673 // do not generate a thumbnail, instead safe the original
1674 // as thumbnail but with lower quality. But if the image
1675 // is to heigh too, then we still have to resize it.
1676 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1677 $result = imagejpeg($src_img, $thumb_image, 75);
1678 imagedestroy($src_img);
1685 $cur_width = $src_img->getImageWidth();
1686 $cur_height = $src_img->getImageHeight();
1688 // If requested width is more then the actual image width,
1689 // do not generate a thumbnail, instead safe the original
1690 // as thumbnail but with lower quality. But if the image
1691 // is to heigh too, then we still have to resize it.
1692 if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1693 $src_img->setCompressionQuality(75);
1694 $src_img->setImageFormat('jpeg');
1695 $src_img->writeImage($thumb_image);
1697 $src_img->destroy();
1704 // If the image will be rotate because EXIF orientation said so
1705 // 'virtually rotate' the image for further calculations
1706 if($rotate == 90 || $rotate == 270) {
1708 $cur_width = $cur_height;
1712 /* calculates aspect ratio */
1713 $aspect_ratio = $cur_height / $cur_width;
1716 if($aspect_ratio < 1) {
1718 $new_h = abs($new_w * $aspect_ratio);
1720 /* 'virtually' rotate the image and calculate it's ratio */
1721 $tmp_w = $cur_height;
1722 $tmp_h = $cur_width;
1723 /* now get the ratio from the 'rotated' image */
1724 $tmp_ratio = $tmp_h/$tmp_w;
1725 /* now calculate the new dimensions */
1727 $tmp_h = abs($tmp_w * $tmp_ratio);
1729 // now that we know, how high they photo should be, if it
1730 // gets rotated, use this high to scale the image
1732 $new_w = abs($new_h / $aspect_ratio);
1734 // If the image will be rotate because EXIF orientation said so
1735 // now 'virtually rotate' back the image for the image manipulation
1736 if($rotate == 90 || $rotate == 270) {
1747 /* creates new image of that size */
1748 $dst_img = imagecreatetruecolor($new_w, $new_h);
1750 imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1752 /* copies resized portion of original image into new image */
1753 imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1755 /* needs the image to be flipped horizontal? */
1757 $this->_debug("(FLIP)");
1758 $dst_img = $this->flipImage($dst_img, 'hori');
1760 /* needs the image to be flipped vertical? */
1762 $this->_debug("(FLIP)");
1763 $dst_img = $this->flipImage($dst_img, 'vert');
1767 $this->_debug("(ROTATE)");
1768 $dst_img = $this->rotateImage($dst_img, $rotate);
1771 /* write down new generated file */
1772 $result = imagejpeg($dst_img, $thumb_image, 75);
1774 /* free your mind */
1775 imagedestroy($dst_img);
1776 imagedestroy($src_img);
1778 if($result === false) {
1779 print "Can't write thumbnail ". $thumb_image ."\n";
1789 $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1791 /* needs the image to be flipped horizontal? */
1793 $this->_debug("(FLIP)");
1794 $src_img->rotateImage(new ImagickPixel(), 90);
1795 $src_img->flipImage();
1796 $src_img->rotateImage(new ImagickPixel(), -90);
1798 /* needs the image to be flipped vertical? */
1800 $this->_debug("(FLIP)");
1801 $src_img->flipImage();
1805 $this->_debug("(ROTATE)");
1806 $src_img->rotateImage(new ImagickPixel(), $rotate);
1809 $src_img->setCompressionQuality(75);
1810 $src_img->setImageFormat('jpeg');
1812 if(!$src_img->writeImage($thumb_image)) {
1813 print "Can't write thumbnail ". $thumb_image ."\n";
1818 $src_img->destroy();
1825 } // create_thumbnail()
1828 * return all exif meta data from the file
1829 * @param string $file
1832 public function get_meta_informations($file)
1834 return exif_read_data($file);
1836 } // get_meta_informations()
1839 * create phpfspot own sqlite database
1841 * this function creates phpfspots own sqlite database
1842 * if it does not exist yet. this own is used to store
1843 * some necessary informations (md5 sum's, ...).
1845 public function check_config_table()
1847 // if the config table doesn't exist yet, create it
1848 if(!$this->cfg_db->db_check_table_exists("images")) {
1849 $this->cfg_db->db_exec("
1850 CREATE TABLE images (
1851 img_idx int primary key,
1857 } // check_config_table
1860 * Generates a thumbnail from photo idx
1862 * This function will generate JPEG thumbnails from provided F-Spot photo
1865 * 1. Check if all thumbnail generations (width) are already in place and
1867 * 2. Check if the md5sum of the original file has changed
1868 * 3. Generate the thumbnails if needed
1869 * @param integer $idx
1870 * @param integer $force
1871 * @param boolean $overwrite
1873 public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
1877 $resolutions = Array(
1878 $this->cfg->thumb_width,
1879 $this->cfg->photo_width,
1880 $this->cfg->mini_width,
1884 /* get details from F-Spot's database */
1885 $details = $this->get_photo_details($idx);
1887 /* calculate file MD5 sum */
1888 $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
1890 if(!file_exists($full_path)) {
1891 $this->_error("File ". $full_path ." does not exist\n");
1895 if(!is_readable($full_path)) {
1896 $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
1900 $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
1902 /* If Nikon NEF format, we need to treat it another way */
1903 if(isset($this->cfg->dcraw_bin) &&
1904 file_exists($this->cfg->dcraw_bin) &&
1905 is_executable($this->cfg->dcraw_bin) &&
1906 preg_match('/\.nef$/i', $details['uri'])) {
1908 $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
1910 /* if PPM file does not exist, let dcraw convert it from NEF */
1911 if(!file_exists($ppm_path)) {
1912 system($this->cfg->dcraw_bin ." -a ". $full_path);
1915 /* for now we handle the PPM instead of the NEF */
1916 $full_path = $ppm_path;
1920 $file_md5 = md5_file($full_path);
1923 foreach($resolutions as $resolution) {
1925 $generate_it = false;
1927 $thumb_sub_path = substr($file_md5, 0, 2);
1928 $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
1930 /* if thumbnail-subdirectory does not exist yet, create it */
1931 if(!file_exists(dirname($thumb_path))) {
1932 mkdir(dirname($thumb_path), 0755);
1935 /* if the thumbnail file doesn't exist, create it */
1936 if(!file_exists($thumb_path)) {
1937 $generate_it = true;
1939 /* if the file hasn't changed there is no need to regen the thumb */
1940 elseif($file_md5 != $this->getMD5($idx) || $force) {
1941 $generate_it = true;
1944 if($generate_it || $overwrite) {
1946 $this->_debug(" ". $resolution ."px");
1947 if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
1955 $this->_debug(" already exist");
1958 /* set the new/changed MD5 sum for the current photo */
1960 $this->setMD5($idx, $file_md5);
1963 $this->_debug("\n");
1968 * returns stored md5 sum for a specific photo
1970 * this function queries the phpfspot database for a
1971 * stored MD5 checksum of the specified photo
1972 * @param integer $idx
1973 * @return string|null
1975 public function getMD5($idx)
1977 $result = $this->cfg_db->db_query("
1980 WHERE img_idx='". $idx ."'
1986 $img = $this->cfg_db->db_fetch_object($result);
1987 return $img['img_md5'];
1992 * set MD5 sum for the specific photo
1993 * @param integer $idx
1994 * @param string $md5
1996 private function setMD5($idx, $md5)
1998 $result = $this->cfg_db->db_exec("
1999 REPLACE INTO images (img_idx, img_md5)
2000 VALUES ('". $idx ."', '". $md5 ."')
2006 * store current tag condition
2008 * this function stores the current tag condition
2009 * (AND or OR) in the users session variables
2010 * @param string $mode
2013 public function setTagCondition($mode)
2015 $_SESSION['tag_condition'] = $mode;
2019 } // setTagCondition()
2022 * invoke tag & date search
2024 * this function will return all matching tags and store
2025 * them in the session variable selected_tags. furthermore
2026 * it also handles the date search.
2027 * getPhotoSelection() will then only return the matching
2031 public function startSearch()
2034 if(isset($_POST['date_from']) && $this->isValidDate($_POST['date_from'])) {
2035 $date_from = $_POST['date_from'];
2037 if(isset($_POST['date_to']) && $this->isValidDate($_POST['date_to'])) {
2038 $date_to = $_POST['date_to'];
2041 /* tag-name search */
2042 if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2043 $searchfor_tag = $_POST['for_tag'];
2044 $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2047 unset($_SESSION['searchfor_tag']);
2050 /* file-name search */
2051 if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2052 $_SESSION['searchfor_name'] = $_POST['for_name'];
2055 unset($_SESSION['searchfor_name']);
2059 if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2061 $_SESSION['rate_from'] = $_POST['rate_from'];
2063 if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2064 $_SESSION['rate_to'] = $_POST['rate_to'];
2068 /* delete any previously set value */
2069 unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2074 if(isset($date_from) && !empty($date_from))
2075 $_SESSION['from_date'] = strtotime($date_from ." 00:00:00");
2077 unset($_SESSION['from_date']);
2079 if(isset($date_to) && !empty($date_to))
2080 $_SESSION['to_date'] = strtotime($date_to ." 23:59:59");
2082 unset($_SESSION['to_date']);
2084 if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2085 /* new search, reset the current selected tags */
2086 $_SESSION['selected_tags'] = Array();
2087 foreach($this->avail_tags as $tag) {
2088 if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2089 array_push($_SESSION['selected_tags'], $tag);
2098 * updates sort order in session variable
2100 * this function is invoked by RPC and will sort the requested
2101 * sort order in the session variable.
2102 * @param string $sort_order
2105 public function updateSortOrder($order)
2107 if(isset($this->sort_orders[$order])) {
2108 $_SESSION['sort_order'] = $order;
2112 return "unkown error";
2114 } // updateSortOrder()
2119 * this function rotates the image according the
2121 * @param string $img
2122 * @param integer $degress
2125 private function rotateImage($img, $degrees)
2127 if(function_exists("imagerotate")) {
2128 $img = imagerotate($img, $degrees, 0);
2130 function imagerotate($src_img, $angle)
2132 $src_x = imagesx($src_img);
2133 $src_y = imagesy($src_img);
2134 if ($angle == 180) {
2138 elseif ($src_x <= $src_y) {
2142 elseif ($src_x >= $src_y) {
2147 $rotate=imagecreatetruecolor($dest_x,$dest_y);
2148 imagealphablending($rotate, false);
2153 for ($y = 0; $y < ($src_y); $y++) {
2154 for ($x = 0; $x < ($src_x); $x++) {
2155 $color = imagecolorat($src_img, $x, $y);
2156 imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2162 for ($y = 0; $y < ($src_y); $y++) {
2163 for ($x = 0; $x < ($src_x); $x++) {
2164 $color = imagecolorat($src_img, $x, $y);
2165 imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2171 for ($y = 0; $y < ($src_y); $y++) {
2172 for ($x = 0; $x < ($src_x); $x++) {
2173 $color = imagecolorat($src_img, $x, $y);
2174 imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2188 $img = imagerotate($img, $degrees);
2197 * returns flipped image
2199 * this function will return an either horizontal or
2200 * vertical flipped truecolor image.
2201 * @param string $image
2202 * @param string $mode
2205 private function flipImage($image, $mode)
2207 $w = imagesx($image);
2208 $h = imagesy($image);
2209 $flipped = imagecreatetruecolor($w, $h);
2213 for ($y = 0; $y < $h; $y++) {
2214 imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2218 for ($x = 0; $x < $w; $x++) {
2219 imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2229 * return all assigned tags for the specified photo
2230 * @param integer $idx
2233 private function get_photo_tags($idx)
2235 $result = $this->db->db_query("
2238 INNER JOIN photo_tags pt
2240 WHERE pt.photo_id='". $idx ."'
2245 while($row = $this->db->db_fetch_object($result)) {
2246 if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2248 $tags[$row['id']] = $row['name'];
2253 } // get_photo_tags()
2256 * create on-the-fly images with text within
2257 * @param string $txt
2258 * @param string $color
2259 * @param integer $space
2260 * @param integer $font
2263 public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2265 if (strlen($color) != 6)
2268 $int = hexdec($color);
2269 $h = imagefontheight($font);
2270 $fw = imagefontwidth($font);
2271 $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2272 $lines = count($txt);
2273 $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2274 $bg = imagecolorallocate($im, 255, 255, 255);
2275 $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2278 foreach ($txt as $text) {
2279 $x = (($w - ($fw * strlen($text))) / 2);
2280 imagestring($im, $font, $x, $y, $text, $color);
2281 $y += ($h + $space);
2284 Header("Content-type: image/png");
2287 } // showTextImage()
2290 * check if all requirements are met
2293 private function check_requirements()
2295 if(!function_exists("imagecreatefromjpeg")) {
2296 print "PHP GD library extension is missing<br />\n";
2300 if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2301 print "PHP SQLite3 library extension is missing<br />\n";
2305 /* Check for HTML_AJAX PEAR package, lent from Horde project */
2306 ini_set('track_errors', 1);
2307 @include_once 'HTML/AJAX/Server.php';
2308 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2309 print "PEAR HTML_AJAX package is missing<br />\n";
2312 @include_once 'Calendar/Calendar.php';
2313 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2314 print "PEAR Calendar package is missing<br />\n";
2317 @include_once 'Console/Getopt.php';
2318 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2319 print "PEAR Console_Getopt package is missing<br />\n";
2322 @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2323 if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2324 print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2327 ini_restore('track_errors');
2334 } // check_requirements()
2336 private function _debug($text)
2338 if(isset($this->fromcmd)) {
2345 * check if specified MIME type is supported
2346 * @param string $mime
2349 public function checkifImageSupported($mime)
2351 $supported_types = Array(
2354 "image/x-portable-pixmap",
2358 if(in_array($mime, $supported_types))
2363 } // checkifImageSupported()
2367 * @param string $text
2369 public function _error($text)
2371 switch($this->cfg->logging) {
2374 print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2375 print $text ."<br />\n";
2381 error_log($text, 3, $his->cfg->log_file);
2385 $this->runtime_error = true;
2390 * get calendar input-text fields
2392 * this function returns a text-field used for the data selection.
2393 * Either it will be filled with the current date or, if available,
2394 * filled with the date user entered previously.
2396 * @param string $mode
2399 private function get_date_text_field($mode)
2401 $date = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2403 $date.= isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2405 $date.= isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2407 $output = "<input type=\"text\" size=\"15\" id=\"date_". $mode ."\" value=\"". $date ."\"";
2408 if(!isset($_SESSION[$mode .'_date']))
2409 $output.= " disabled=\"disabled\"";
2414 } // get_date_text_field()
2417 * output calendar matrix
2418 * @param integer $year
2419 * @param integer $month
2420 * @param integer $day
2422 public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
2424 if (!isset($year)) $year = date('Y');
2425 if (!isset($month)) $month = date('m');
2426 if (!isset($day)) $day = date('d');
2431 require_once CALENDAR_ROOT.'Month/Weekdays.php';
2432 require_once CALENDAR_ROOT.'Day.php';
2435 $month = new Calendar_Month_Weekdays($year,$month);
2438 $prevStamp = $month->prevMonth(true);
2439 $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2440 $nextStamp = $month->nextMonth(true);
2441 $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2443 $selectedDays = array (
2444 new Calendar_Day($year,$month,$day),
2445 new Calendar_Day($year,12,25),
2448 // Build the days in the month
2449 $month->build($selectedDays);
2451 $this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
2452 $this->tmpl->assign('prev_month', $prev);
2453 $this->tmpl->assign('next_month', $next);
2455 while ( $day = $month->fetch() ) {
2457 if(!isset($matrix[$rows]))
2458 $matrix[$rows] = Array();
2462 $dayStamp = $day->thisDay(true);
2463 $link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
2465 // isFirst() to find start of week
2466 if ( $day->isFirst() )
2469 if ( $day->isSelected() ) {
2470 $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2471 } else if ( $day->isEmpty() ) {
2472 $string.= "<td> </td>\n";
2474 $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2477 // isLast() to find end of week
2478 if ( $day->isLast() )
2479 $string.= "</tr>\n";
2481 $matrix[$rows][$cols] = $string;
2491 $this->tmpl->assign('matrix', $matrix);
2492 $this->tmpl->assign('rows', $rows);
2493 $this->tmpl->show("calendar.tpl");
2495 } // get_calendar_matrix()
2498 * output export page
2499 * @param string $mode
2501 public function getExport($mode)
2503 $pictures = $this->getPhotoSelection();
2504 $current_tags = $this->getCurrentTags();
2506 foreach($pictures as $picture) {
2508 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2509 if($current_tags != "") {
2510 $orig_url.= "&tags=". $current_tags;
2512 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2513 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2516 if($this->is_user_friendly_url()) {
2517 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2520 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2526 // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2527 print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2531 // "[%pictureurl% %thumbnailurl%]"
2532 print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2535 case 'MoinMoinList':
2536 // " * [%pictureurl% %thumbnailurl%]"
2537 print " " . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2548 public function getRSSFeed()
2550 Header("Content-type: text/xml; charset=utf-8");
2551 print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2554 xmlns:media="http://search.yahoo.com/mrss/"
2555 xmlns:dc="http://purl.org/dc/elements/1.1/"
2558 <title>phpfspot</title>
2559 <description>phpfspot RSS feed</description>
2560 <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2561 <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2562 <generator>phpfspot</generator>
2565 $pictures = $this->getPhotoSelection();
2566 $current_tags = $this->getCurrentTags();
2568 foreach($pictures as $picture) {
2570 $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2571 if($current_tags != "") {
2572 $orig_url.= "&tags=". $current_tags;
2574 if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2575 $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2578 $details = $this->get_photo_details($picture);
2580 if($this->is_user_friendly_url()) {
2581 $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2584 $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2587 $thumb_html = htmlspecialchars("
2588 <a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2590 ". $details['description']);
2592 $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2594 /* get EXIF information if JPEG */
2595 if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2596 $meta = $this->get_meta_informations($orig_path);
2601 <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2602 <link><?php print htmlspecialchars($orig_url); ?></link>
2603 <guid><?php print htmlspecialchars($orig_url); ?></guid>
2604 <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $details['time']); ?></dc:date.Taken>
2606 <?php print $thumb_html; ?>
2608 <pubDate><?php print strftime("%a, %d %b %Y %T %z", $details['time']); ?></pubDate>
2623 * get all selected tags
2625 * This function will return all selected tags as one string, seperated
2629 private function getCurrentTags()
2632 if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2633 foreach($_SESSION['selected_tags'] as $tag)
2634 $current_tags.= $tag .",";
2635 $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2637 return $current_tags;
2639 } // getCurrentTags()
2642 * return the current photo
2644 public function getCurrentPhoto()
2646 if(isset($_SESSION['current_photo'])) {
2647 print $_SESSION['current_photo'];
2649 } // getCurrentPhoto()
2652 * tells the client browser what to do
2654 * this function is getting called via AJAX by the
2655 * client browsers. it will tell them what they have
2656 * to do next. This is necessary for directly jumping
2657 * into photo index or single photo view when the are
2658 * requested with specific URLs
2661 public function whatToDo()
2663 if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
2665 elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
2666 return "showpi_tags";
2668 elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
2675 * return the current process-user
2678 private function getuid()
2680 if($uid = posix_getuid()) {
2681 if($user = posix_getpwuid($uid)) {
2682 return $user['name'];
2691 * returns a select-dropdown box to select photo index sort parameters
2692 * @param array $params
2693 * @param smarty $smarty
2696 public function smarty_sort_select_list($params, &$smarty)
2700 foreach($this->sort_orders as $key => $value) {
2701 $output.= "<option value=\"". $key ."\"";
2702 if($key == $_SESSION['sort_order']) {
2703 $output.= " selected=\"selected\"";
2705 $output.= ">". $value ."</option>";
2710 } // smarty_sort_select_list()
2713 * returns the currently selected sort order
2716 private function get_sort_order()
2718 switch($_SESSION['sort_order']) {
2720 return " ORDER BY p.time ASC";
2723 return " ORDER BY p.time DESC";
2726 if($this->dbver < 9) {
2727 return " ORDER BY p.name ASC";
2730 return " ORDER BY basename(p.uri) ASC";
2734 if($this->dbver < 9) {
2735 return " ORDER BY p.name DESC";
2738 return " ORDER BY basename(p.uri) DESC";
2742 return " ORDER BY t.name ASC ,p.time ASC";
2745 return " ORDER BY t.name DESC ,p.time ASC";
2748 return " ORDER BY p.rating ASC, t.name ASC";
2751 return " ORDER BY p.rating DESC, t.name ASC";
2755 } // get_sort_order()
2758 * return the next to be shown slide show image
2760 * this function returns the URL of the next image
2761 * in the slideshow sequence.
2764 public function getNextSlideShowImage()
2766 $all_photos = $this->getPhotoSelection();
2768 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1)
2769 $_SESSION['slideshow_img'] = 0;
2771 $_SESSION['slideshow_img']++;
2773 if($this->is_user_friendly_url()) {
2774 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2777 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2779 } // getNextSlideShowImage()
2782 * return the previous to be shown slide show image
2784 * this function returns the URL of the previous image
2785 * in the slideshow sequence.
2788 public function getPrevSlideShowImage()
2790 $all_photos = $this->getPhotoSelection();
2792 if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
2793 $_SESSION['slideshow_img'] = 0;
2795 $_SESSION['slideshow_img']--;
2797 if($this->is_user_friendly_url()) {
2798 return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
2801 return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
2803 } // getPrevSlideShowImage()
2805 public function resetSlideShow()
2807 if(isset($_SESSION['slideshow_img']))
2808 unset($_SESSION['slideshow_img']);
2810 } // resetSlideShow()
2815 * this function will get all photos from the fspot
2816 * database and randomly return ONE entry
2818 * saddly there is yet no sqlite3 function which returns
2819 * the bulk result in array, so we have to fill up our
2823 public function get_random_photo()
2832 /* if show_tags is set, only return details for photos which
2833 are specified to be shown
2835 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2837 INNER JOIN photo_tags pt
2842 t.name IN ('".implode("','",$this->cfg->show_tags)."')";
2845 $result = $this->db->db_query($query_str);
2847 while($row = $this->db->db_fetch_object($result)) {
2848 array_push($all, $row['id']);
2851 return $all[array_rand($all)];
2853 } // get_random_photo()
2856 * get random photo tag photo
2858 * this function will get all photos tagged with the requested
2859 * tag from the fspot database and randomly return ONE entry
2861 * saddly there is yet no sqlite3 function which returns
2862 * the bulk result in array, so we have to fill up our
2866 public function get_random_tag_photo($tagidx)
2873 INNER JOIN photo_tags pt
2877 if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2885 pt.tag_id LIKE '". $tagidx ."'
2888 /*if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
2891 t.name IN ('".implode("','",$this->cfg->show_tags)."')
2895 $result = $this->db->db_query($query_str);
2897 while($row = $this->db->db_fetch_object($result)) {
2898 array_push($all, $row['id']);
2901 return $all[array_rand($all)];
2903 } // get_random_tag_photo()
2906 * validates provided date
2908 * this function validates if the provided date
2909 * contains a valid date and will return true
2911 * @param string $date_str
2914 public function isValidDate($date_str)
2916 $timestamp = strtotime($date_str);
2918 if(is_numeric($timestamp))
2926 * timestamp to string conversion
2927 * @param integer $timestamp
2930 private function ts2str($timestamp)
2932 if(!empty($timestamp) && is_numeric($timestamp))
2933 return strftime("%Y-%m-%d", $timestamp);
2938 * extract tag-names from $_GET['tags']
2939 * @param string $tags_str
2942 private function extractTags($tags_str)
2944 $not_validated = split(',', $tags_str);
2945 $validated = array();
2947 foreach($not_validated as $tag) {
2948 if(is_numeric($tag))
2949 array_push($validated, $tag);
2957 * returns the full path to a thumbnail
2958 * @param integer $width
2959 * @param integer $photo
2962 public function get_thumb_path($width, $photo)
2964 $md5 = $this->getMD5($photo);
2965 $sub_path = substr($md5, 0, 2);
2966 return $this->cfg->thumb_path
2974 } // get_thumb_path()
2977 * returns server's virtual host name
2980 private function get_server_name()
2982 return $_SERVER['SERVER_NAME'];
2983 } // get_server_name()
2986 * returns type of webprotocol which is currently used
2989 private function get_web_protocol()
2991 if(!isset($_SERVER['HTTPS']))
2995 } // get_web_protocol()
2998 * return url to this phpfspot installation
3001 private function get_phpfspot_url()
3003 return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
3005 } // get_phpfspot_url()
3008 * returns the number of photos which are tagged with $tag_id
3009 * @param integer $tag_id
3012 public function get_num_photos($tag_id)
3014 if($result = $this->db->db_fetchSingleRow("
3015 SELECT count(*) as number
3018 tag_id LIKE '". $tag_id ."'")) {
3020 return $result['number'];
3026 } // get_num_photos()
3029 * check file exists and is readable
3031 * returns true, if everything is ok, otherwise false
3032 * if $silent is not set, this function will output and
3034 * @param string $file
3035 * @param boolean $silent
3038 private function check_readable($file, $silent = null)
3040 if(!file_exists($file)) {
3042 print "File \"". $file ."\" does not exist.\n";
3046 if(!is_readable($file)) {
3048 print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3054 } // check_readable()
3057 * check if all needed indices are present
3059 * this function checks, if some needed indices are already
3060 * present, or if not, create them on the fly. they are
3061 * necessary to speed up some queries like that one look for
3062 * all tags, when show_tags is specified in the configuration.
3064 private function checkDbIndices()
3066 $result = $this->db->db_exec("
3067 CREATE INDEX IF NOT EXISTS
3074 } // checkDbIndices()
3077 * retrive F-Spot database version
3079 * this function will return the F-Spot database version number
3080 * It is stored within the sqlite3 database in the table meta
3081 * @return string|null
3083 public function getFspotDBVersion()
3085 if($result = $this->db->db_fetchSingleRow("
3086 SELECT data as version
3089 name LIKE 'F-Spot Database Version'
3091 return $result['version'];
3095 } // getFspotDBVersion()
3098 * parse the provided URI and will returned the requested chunk
3099 * @param string $uri
3100 * @param string $mode
3103 public function parse_uri($uri, $mode)
3105 if(($components = parse_url($uri)) !== false) {
3109 return basename($components['path']);
3112 return dirname($components['path']);
3115 return $components['path'];
3125 * validate config options
3127 * this function checks if all necessary configuration options are
3128 * specified and set.
3131 private function check_config_options()
3133 if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3134 $this->_error("Please set \$page_title in phpfspot_cfg");
3136 if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3137 $this->_error("Please set \$base_path in phpfspot_cfg");
3139 if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3140 $this->_error("Please set \$web_path in phpfspot_cfg");
3142 if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3143 $this->_error("Please set \$thumb_path in phpfspot_cfg");
3145 if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3146 $this->_error("Please set \$smarty_path in phpfspot_cfg");
3148 if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3149 $this->_error("Please set \$fspot_db in phpfspot_cfg");
3151 if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3152 $this->_error("Please set \$db_access in phpfspot_cfg");
3154 if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3155 $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3157 if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3158 $this->_error("Please set \$thumb_width in phpfspot_cfg");
3160 if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3161 $this->_error("Please set \$thumb_height in phpfspot_cfg");
3163 if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3164 $this->_error("Please set \$photo_width in phpfspot_cfg");
3166 if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3167 $this->_error("Please set \$mini_width in phpfspot_cfg");
3169 if(!isset($this->cfg->thumbs_per_page))
3170 $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3172 if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3173 $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3175 if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3176 $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3178 if(!isset($this->cfg->hide_tags))
3179 $this->_error("Please set \$hide_tags in phpfspot_cfg");
3181 if(!isset($this->cfg->theme_name))
3182 $this->_error("Please set \$theme_name in phpfspot_cfg");
3184 if(!isset($this->cfg->logging))
3185 $this->_error("Please set \$logging in phpfspot_cfg");
3187 if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3189 if(!isset($this->cfg->log_file))
3190 $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3192 if(!is_writeable($this->cfg->log_file))
3193 $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3197 /* remove trailing slash, if set */
3198 if($this->cfg->web_path == "/")
3199 $this->cfg->web_path = "";
3200 elseif(preg_match('/\/$/', $this->cfg->web_path))
3201 $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3203 return $this->runtime_error;
3205 } // check_config_options()
3208 * cleanup phpfspot own database
3210 * When photos are getting delete from F-Spot, there will remain
3211 * remain some residues in phpfspot own database. This function
3212 * will try to wipe them out.
3214 public function cleanup_phpfspot_db()
3216 $to_delete = Array();
3218 $result = $this->cfg_db->db_query("
3221 ORDER BY img_idx ASC
3224 while($row = $this->cfg_db->db_fetch_object($result)) {
3225 if(!$this->db->db_fetchSingleRow("
3228 WHERE id='". $row['img_idx'] ."'")) {
3230 array_push($to_delete, $row['img_idx'], ',');
3234 print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3236 $this->cfg_db->db_exec("
3238 WHERE img_idx IN (". implode($to_delete) .")
3241 } // cleanup_phpfspot_db()
3244 * return first image of the page, the $current photo
3247 * this function is used to find out the first photo of the
3248 * current page, in which the $current photo lies. this is
3249 * used to display the correct photo, when calling showPhotoIndex()
3251 * @param integer $current
3252 * @param integer $max
3255 private function getCurrentPage($current, $max)
3257 if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3258 for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3259 if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3265 } // getCurrentPage()
3270 * this function tries to find out the correct mime-type
3271 * for the provided file.
3272 * @param string $file
3275 public function get_mime_info($file)
3277 $details = getimagesize($file);
3279 /* if getimagesize() returns empty, try at least to find out the
3282 if(empty($details) && function_exists('mime_content_type')) {
3284 // mime_content_type is marked as deprecated in the documentation,
3285 // but is it really necessary to force users to install a PECL
3287 $details['mime'] = mime_content_type($file);
3290 return $details['mime'];
3292 } // get_mime_info()
3295 * return tag-name by tag-idx
3297 * this function returns the tag-name for the requested
3298 * tag specified by tag-idx.
3299 * @param integer $idx
3302 public function get_tag_name($idx)
3304 if($result = $this->db->db_fetchSingleRow("
3308 id LIKE '". $idx ."'")) {
3310 return $result['name'];
3319 * parse user friendly url which got rewritten by the websever
3320 * @param string $request_uri
3323 private function parse_user_friendly_url($request_uri)
3325 if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3327 $options = explode('/', $request_uri);
3329 switch($options[1]) {
3331 if(is_numeric($options[2])) {
3332 $this->session_cleanup();
3333 //unset($_SESSION['start_action']);
3334 //unset($_SESSION['selected_tags']);
3335 $_GET['mode'] = 'showp';
3336 return $this->showPhoto($options[2]);
3340 if(is_numeric($options[2])) {
3341 require_once "phpfspot_img.php";
3342 $img = new PHPFSPOT_IMG;
3343 if(isset($options[3]) && is_numeric($options[3]))
3344 $img->showImg($options[2], $options[3]);
3346 $img->showImg($options[2]);
3351 if(is_numeric($options[2])) {
3352 $this->session_cleanup();
3353 $_GET['tags'] = $options[2];
3354 $_SESSION['selected_tags'] = Array($options[2]);
3355 if(isset($options[3]) && is_numeric($options[3]))
3356 $_SESSION['begin_with'] = $options[3];
3357 return $this->showPhotoIndex();
3363 } // parse_user_friendly_url()
3366 * check if user-friendly-urls are enabled
3368 * this function will return true, if the config option
3369 * $user_friendly_url has been set. Otherwise false.
3372 private function is_user_friendly_url()
3374 if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3379 } // is_user_friendly_url()
3384 * this function will cleanup user's session information
3386 private function session_cleanup()
3388 unset($_SESSION['begin_with']);
3389 $this->resetDateSearch();
3390 $this->resetPhotoView();
3391 $this->resetTagSearch();
3392 $this->resetNameSearch();
3393 $this->resetDateSearch();
3396 } // session_cleanup()