cfg = new PHPFSPOT_CFG;
/* set application name and version information */
$this->cfg->product = "phpfspot";
$this->cfg->version = "1.3";
$this->sort_orders= array(
'date_asc' => 'Date ↑',
'date_desc' => 'Date ↓',
'name_asc' => 'Name ↑',
'name_desc' => 'Name ↓'
);
/* Check necessary requirements */
if(!$this->checkRequirements()) {
exit(1);
}
$this->db = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
if(!is_writeable($this->cfg->fspot_db)) {
print $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() ."\n";
exit(1);
}
$this->dbver = $this->getFspotDBVersion();
if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
print dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() ."\n";
exit(1);
}
if(!is_writeable($this->cfg->base_path ."/templates_c")) {
print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
exit(1);
}
$this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
if(!is_writeable($this->cfg->phpfspot_db)) {
print $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() ."\n";
exit(1);
}
$this->check_config_table();
/* include Smarty template engine */
if(!$this->check_readable($this->cfg->smarty_path .'/libs/Smarty.class.php')) {
exit(1);
}
require $this->cfg->smarty_path .'/libs/Smarty.class.php';
/* overload Smarty class if our own template handler */
require_once "phpfspot_tmpl.php";
$this->tmpl = new PHPFSPOT_TMPL($this);
/* check if all necessary indices exist */
$this->checkDbIndices();
/* if session is not yet started, do it now */
if(session_id() == "")
session_start();
if(!isset($_SESSION['tag_condition']))
$_SESSION['tag_condition'] = 'or';
if(!isset($_SESSION['sort_order']))
$_SESSION['sort_order'] = 'date_asc';
if(!isset($_SESSION['searchfor']))
$_SESSION['searchfor'] = '';
// if begin_with is still set but thumbs_per_page is now 0, unset it
if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
unset($_SESSION['begin_with']);
} // __construct()
public function __destruct()
{
} // __destruct()
/**
* show - generate html output
*
* this function can be called after the constructor has
* prepared everyhing. it will load the index.tpl smarty
* template. if necessary it will registere pre-selects
* (photo index, photo, tag search, date search) into
* users session.
*/
public function show()
{
$this->tmpl->assign('searchfor', $_SESSION['searchfor']);
$this->tmpl->assign('page_title', $this->cfg->page_title);
$this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
$this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
if(isset($_GET['mode'])) {
$_SESSION['start_action'] = $_GET['mode'];
switch($_GET['mode']) {
case 'showpi':
if(isset($_GET['tags'])) {
$_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
}
if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
$_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
}
if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
$_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
}
break;
case 'showp':
if(isset($_GET['tags'])) {
$_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
$_SESSION['start_action'] = 'showp';
}
if(isset($_GET['id']) && is_numeric($_GET['id'])) {
$_SESSION['current_photo'] = $_GET['id'];
$_SESSION['start_action'] = 'showp';
}
if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
$_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
}
if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
$_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
}
break;
case 'export':
$this->tmpl->show("export.tpl");
return;
break;
case 'slideshow':
$this->tmpl->show("slideshow.tpl");
return;
break;
case 'rss':
if(isset($_GET['tags'])) {
$_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
}
if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
$_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
}
if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
$_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
}
$this->getRSSFeed();
return;
break;
}
}
if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
$this->tmpl->assign('date_search_enabled', true);
$this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
$this->tmpl->assign('from_date', $this->get_calendar('from'));
$this->tmpl->assign('to_date', $this->get_calendar('to'));
$this->tmpl->assign('content_page', 'welcome.tpl');
$this->tmpl->show("index.tpl");
} // show()
/**
* get_tags - grab all tags of f-spot's database
*
* this function will get all available tags from
* the f-spot database and store them within two
* arrays within this class for later usage. in
* fact, if the user requests (hide_tags) it will
* opt-out some of them.
*
* this function is getting called once by show()
*/
private function get_tags()
{
$this->avail_tags = Array();
$count = 0;
if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
$query_str="
SELECT
DISTINCT t1.id as id, t1.name as name
FROM
photo_tags pt1
INNER JOIN photo_tags
pt2 ON pt1.photo_id=pt2.photo_id
INNER JOIN tags t1
ON t1.id=pt1.tag_id
INNER JOIN tags t2
ON t2.id=pt2.tag_id
WHERE
t2.name IN ('".implode("','",$this->cfg->show_tags)."')
ORDER BY
t1.sort_priority ASC";
$result = $this->db->db_query($query_str);
}
else
{
$result = $this->db->db_query("
SELECT id,name
FROM tags
ORDER BY sort_priority ASC
");
}
while($row = $this->db->db_fetch_object($result)) {
$tag_id = $row['id'];
$tag_name = $row['name'];
/* if the user has specified to ignore this tag in phpfspot's
configuration, ignore it here so it does not get added to
the tag list.
*/
if(in_array($row['name'], $this->cfg->hide_tags))
continue;
/* if you include the following if-clause and the user has specified
to only show certain tags which are specified in phpfspot's
configuration, ignore all others so they will not be added to the
tag list.
if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
!in_array($row['name'], $this->cfg->show_tags))
continue;
*/
$this->tags[$tag_id] = $tag_name;
$this->avail_tags[$count] = $tag_id;
$count++;
}
} // get_tags()
/**
* extract all photo details
*
* retrieve all available details from f-spot's
* database and return them as object
*/
public function get_photo_details($idx)
{
if($this->dbver < 9) {
$query_str = "
SELECT p.id, p.name, p.time, p.directory_path, p.description
FROM photos p
";
}
else {
$query_str = "
SELECT p.id, p.uri, p.time, p.description
FROM photos p
";
}
/* if show_tags is set, only return details for photos which
are specified to be shown
*/
if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
$query_str.= "
INNER JOIN photo_tags pt
ON p.id=pt.photo_id
INNER JOIN tags t
ON pt.tag_id=t.id
WHERE p.id='". $idx ."'
AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
}
else {
$query_str.= "
WHERE p.id='". $idx ."'
";
}
if($result = $this->db->db_query($query_str)) {
$row = $this->db->db_fetch_object($result);
if($this->dbver < 9) {
$row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
}
return $row;
}
return null;
} // get_photo_details
/**
* returns aligned photo names
*
* this function returns aligned (length) names for
* an specific photo. If the length of the name exceeds
* $limit the name will be shrinked (...)
*/
public function getPhotoName($idx, $limit = 0)
{
if($details = $this->get_photo_details($idx)) {
if($long_name = $this->parse_uri($details['uri'], 'filename')) {
$name = $this->shrink_text($long_name, $limit);
return $name;
}
}
return null;
} // getPhotoName()
/**
* shrink text according provided limit
*
* If the length of the name exceeds $limit the
* text will be shortend and some content in between
* will be replaced with "..."
*/
private function shrink_text($text, $limit)
{
if($limit != 0 && strlen($text) > $limit) {
$text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
}
return $text;
} // shrink_text();
/**
* translate f-spoth photo path
*
* as the full-qualified path recorded in the f-spot database
* is usally not the same as on the webserver, this function
* will replace the path with that one specified in the cfg
*/
public function translate_path($path, $width = 0)
{
return str_replace($this->cfg->path_replace_from, $this->cfg->path_replace_to, $path);
} // translate_path
/**
* control HTML ouput for a single photo
*
* this function provides all the necessary information
* for the single photo template.
*/
public function showPhoto($photo)
{
/* get all photos from the current photo selection */
$all_photos = $this->getPhotoSelection();
$count = count($all_photos);
for($i = 0; $i < $count; $i++) {
// $get_next will be set, when the photo which has to
// be displayed has been found - this means that the
// next available is in fact the NEXT image (for the
// navigation icons)
if(isset($get_next)) {
$next_img = $all_photos[$i];
break;
}
/* the next photo is our NEXT photo */
if($all_photos[$i] == $photo) {
$get_next = 1;
}
else {
$previous_img = $all_photos[$i];
}
if($photo == $all_photos[$i]) {
$current = $i;
}
}
$details = $this->get_photo_details($photo);
if(!$details) {
print "error";
return;
}
$orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
$thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
if(!file_exists($orig_path)) {
$this->_error("Photo ". $orig_path ." does not exist!
\n");
return;
}
if(!is_readable($orig_path)) {
$this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."
\n");
return;
}
/* If the thumbnail doesn't exist yet, try to create it */
if(!file_exists($thumb_path)) {
$this->gen_thumb($photo, true);
$thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo);
}
/* get f-spot database meta information */
$meta = $this->get_meta_informations($orig_path);
/* If EXIF data are available, use them */
if(isset($meta['ExifImageWidth'])) {
$meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
} else {
$info = getimagesize($orig_path);
$meta_res = $info[0] ."x". $info[1];
}
$meta_date = isset($meta['FileDateTime']) ? strftime("%a %x %X", $meta['FileDateTime']) : "n/a";
$meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
$meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
$extern_link = "index.php?mode=showp&id=". $photo;
$current_tags = $this->getCurrentTags();
if($current_tags != "") {
$extern_link.= "&tags=". $current_tags;
}
if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
$extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
}
$this->tmpl->assign('extern_link', $extern_link);
if(!file_exists($thumb_path)) {
$this->_error("Can't open file ". $thumb_path ."\n");
return;
}
$info = getimagesize($thumb_path);
$this->tmpl->assign('description', $details['description']);
$this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
$this->tmpl->assign('width', $info[0]);
$this->tmpl->assign('height', $info[1]);
$this->tmpl->assign('ExifMadeOn', $meta_date);
$this->tmpl->assign('ExifMadeWith', $meta_make);
$this->tmpl->assign('ExifOrigResolution', $meta_res);
$this->tmpl->assign('ExifFileSize', $meta_size);
$this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&width=". $this->cfg->photo_width);
$this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
$this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
$this->tmpl->assign('tags', $this->get_photo_tags($photo));
$this->tmpl->assign('current', $current);
if($previous_img) {
$this->tmpl->assign('previous_url', "javascript:showImage(". $previous_img .");");
$this->tmpl->assign('prev_img', $previous_img);
}
if($next_img) {
$this->tmpl->assign('next_url', "javascript:showImage(". $next_img .");");
$this->tmpl->assign('next_img', $next_img);
}
$this->tmpl->assign('mini_width', $this->cfg->mini_width);
$this->tmpl->assign('photo_number', $i);
$this->tmpl->assign('photo_count', count($all_photos));
$this->tmpl->show("single_photo.tpl");
} // showPhoto()
/**
* all available tags and tag cloud
*
* this function outputs all available tags (time ordered)
* and in addition output them as tag cloud (tags which have
* many photos will appears more then others)
*/
public function getAvailableTags()
{
/* retrive tags from database */
$this->get_tags();
$output = "";
$result = $this->db->db_query("
SELECT tag_id as id, count(tag_id) as quantity
FROM photo_tags
INNER JOIN tags t
ON t.id = tag_id
GROUP BY tag_id
ORDER BY t.name ASC
");
$tags = Array();
while($row = $this->db->db_fetch_object($result)) {
$tags[$row['id']] = $row['quantity'];
}
// change these font sizes if you will
$max_size = 125; // max font size in %
$min_size = 75; // min font size in %
// get the largest and smallest array values
$max_qty = max(array_values($tags));
$min_qty = min(array_values($tags));
// find the range of values
$spread = $max_qty - $min_qty;
if (0 == $spread) { // we don't want to divide by zero
$spread = 1;
}
// determine the font-size increment
// this is the increase per tag quantity (times used)
$step = ($max_size - $min_size)/($spread);
// loop through our tag array
foreach ($tags as $key => $value) {
if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
continue;
// calculate CSS font-size
// find the $value in excess of $min_qty
// multiply by the font-size increment ($size)
// and add the $min_size set above
$size = $min_size + (($value - $min_qty) * $step);
// uncomment if you want sizes in whole %:
$size = ceil($size);
if(isset($this->tags[$key])) {
$output.= "". $this->tags[$key] .", ";
}
}
$output = substr($output, 0, strlen($output)-2);
print $output;
} // getAvailableTags()
/**
* output all selected tags
*
* this function output all tags which have been selected
* by the user. the selected tags are stored in the
* session-variable $_SESSION['selected_tags']
*/
public function getSelectedTags()
{
/* retrive tags from database */
$this->get_tags();
$output = "";
foreach($this->avail_tags as $tag)
{
// return all selected tags
if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
$output.= "". $this->tags[$tag] .", ";
}
}
if($output != "") {
$output = substr($output, 0, strlen($output)-2);
return $output;
}
else {
return "no tags selected";
}
} // getSelectedTags()
/**
* add tag to users session variable
*
* this function will add the specified to users current
* tag selection. if a date search has been made before
* it will be now cleared
*/
public function addTag($tag)
{
if(!isset($_SESSION['selected_tags']))
$_SESSION['selected_tags'] = Array();
if(isset($_SESSION['searchfor']))
unset($_SESSION['searchfor']);
if(!in_array($tag, $_SESSION['selected_tags']))
array_push($_SESSION['selected_tags'], $tag);
} // addTag()
/**
* remove tag to users session variable
*
* this function removes the specified tag from
* users current tag selection
*/
public function delTag($tag)
{
if(isset($_SESSION['searchfor']))
unset($_SESSION['searchfor']);
if(isset($_SESSION['selected_tags'])) {
$key = array_search($tag, $_SESSION['selected_tags']);
unset($_SESSION['selected_tags'][$key]);
sort($_SESSION['selected_tags']);
}
} // delTag()
/**
* reset tag selection
*
* if there is any tag selection, it will be
* deleted now
*/
public function resetTags()
{
if(isset($_SESSION['selected_tags']))
unset($_SESSION['selected_tags']);
} // resetTags()
/**
* reset single photo
*
* if a specific photo was requested (external link)
* unset the session variable now
*/
public function resetPhotoView()
{
if(isset($_SESSION['current_photo']))
unset($_SESSION['current_photo']);
} // resetPhotoView();
/**
* reset tag search
*
* if any tag search has taken place, reset
* it now
*/
public function resetTagSearch()
{
if(isset($_SESSION['searchfor']))
unset($_SESSION['searchfor']);
} // resetTagSearch()
/**
* reset date search
*
* if any date search has taken place, reset
* it now
*/
public function resetDateSearch()
{
if(isset($_SESSION['from_date']))
unset($_SESSION['from_date']);
if(isset($_SESSION['to_date']))
unset($_SESSION['to_date']);
} // resetDateSearch();
/**
* return all photo according selection
*
* this function returns all photos based on
* the tag-selection, tag- or date-search.
* the tag-search also has to take care of AND
* and OR conjunctions
*/
public function getPhotoSelection()
{
$matched_photos = Array();
if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
$from_date = $_SESSION['from_date'];
$to_date = $_SESSION['to_date'];
$additional_where_cond = "
p.time>='". $from_date ."'
AND
p.time<='". $to_date ."'
";
}
if(isset($_SESSION['sort_order'])) {
$order_str = $this->get_sort_order();
}
/* return a search result */
if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '') {
$query_str = "
SELECT DISTINCT pt1.photo_id
FROM photo_tags pt1
INNER JOIN photo_tags pt2
ON pt1.photo_id=pt2.photo_id
INNER JOIN tags t1
ON pt1.tag_id=t1.id
INNER JOIN photos p
ON pt1.photo_id=p.id
INNER JOIN tags t2
ON pt2.tag_id=t2.id
WHERE t1.name LIKE '%". $_SESSION['searchfor'] ."%' ";
if(isset($additional_where_cond))
$query_str.= "AND ". $additional_where_cond ." ";
if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
$query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
}
if(isset($order_str))
$query_str.= $order_str;
$result = $this->db->db_query($query_str);
while($row = $this->db->db_fetch_object($result)) {
array_push($matched_photos, $row['photo_id']);
}
return $matched_photos;
}
/* return according the selected tags */
if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
$selected = "";
foreach($_SESSION['selected_tags'] as $tag)
$selected.= $tag .",";
$selected = substr($selected, 0, strlen($selected)-1);
/* photo has to match at least on of the selected tags */
if($_SESSION['tag_condition'] == 'or') {
$query_str = "
SELECT DISTINCT pt1.photo_id
FROM photo_tags pt1
INNER JOIN photo_tags pt2
ON pt1.photo_id=pt2.photo_id
INNER JOIN tags t
ON pt2.tag_id=t.id
INNER JOIN photos p
ON pt1.photo_id=p.id
WHERE pt1.tag_id IN (". $selected .")
";
if(isset($additional_where_cond))
$query_str.= "AND ". $additional_where_cond ." ";
if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
$query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
}
if(isset($order_str))
$query_str.= $order_str;
}
/* photo has to match all selected tags */
elseif($_SESSION['tag_condition'] == 'and') {
if(count($_SESSION['selected_tags']) >= 32) {
print "A SQLite limit of 32 tables within a JOIN SELECT avoids to
\n";
print "evaluate your tag selection. Please remove some tags from your selection.\n";
return Array();
}
/* Join together a table looking like
pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
so the query can quickly return all images matching the
selected tags in an AND condition
*/
$query_str = "
SELECT DISTINCT pt1.photo_id
FROM photo_tags pt1
";
if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
$query_str.= "
INNER JOIN tags t
ON pt1.tag_id=t.id
";
}
for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
$query_str.= "
INNER JOIN photo_tags pt". ($i+2) ."
ON pt1.photo_id=pt". ($i+2) .".photo_id
";
}
$query_str.= "
INNER JOIN photos p
ON pt1.photo_id=p.id
";
$query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
$query_str.= "
AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
";
}
if(isset($additional_where_cond))
$query_str.= "AND ". $additional_where_cond;
if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
$query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
}
if(isset($order_str))
$query_str.= $order_str;
}
$result = $this->db->db_query($query_str);
while($row = $this->db->db_fetch_object($result)) {
array_push($matched_photos, $row['photo_id']);
}
return $matched_photos;
}
/* return all available photos */
$query_str = "
SELECT p.id
FROM photos p
LEFT JOIN photo_tags pt
ON p.id=pt.photo_id
LEFT JOIN tags t
ON pt.tag_id=t.id
";
if(isset($additional_where_cond))
$query_str.= "WHERE ". $additional_where_cond ." ";
if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
if(isset($additional_where_cond))
$query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
else
$query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
}
if(isset($order_str))
$query_str.= $order_str;
$result = $this->db->db_query($query_str);
while($row = $this->db->db_fetch_object($result)) {
array_push($matched_photos, $row['id']);
}
return $matched_photos;
} // getPhotoSelection()
/**
* control HTML ouput for photo index
*
* this function provides all the necessary information
* for the photo index template.
*/
public function showPhotoIndex()
{
$photos = $this->getPhotoSelection();
$count = count($photos);
if(isset($_SESSION['begin_with']) && $_SESSION['begin_with'] != "")
$anchor = $_SESSION['begin_with'];
if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
$begin_with = 0;
$end_with = $count;
}
elseif($this->cfg->thumbs_per_page > 0) {
if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
$begin_with = 0;
}
else {
$begin_with = $_SESSION['begin_with'];
}
$end_with = $begin_with + $this->cfg->thumbs_per_page;
}
$thumbs = 0;
$images[$thumbs] = Array();
$img_height[$thumbs] = Array();
$img_width[$thumbs] = Array();
$img_id[$thumbs] = Array();
$img_name[$thumbs] = Array();
$img_title = Array();
for($i = $begin_with; $i < $end_with; $i++) {
if(isset($photos[$i])) {
$images[$thumbs] = $photos[$i];
$img_id[$thumbs] = $i;
$img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
$img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
$thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i]);
if(file_exists($thumb_path)) {
$info = getimagesize($thumb_path);
$img_width[$thumbs] = $info[0];
$img_height[$thumbs] = $info[1];
}
$thumbs++;
}
}
// +1 for for smarty's selection iteration
$thumbs++;
if(isset($_SESSION['searchfor']) && $_SESSION['searchfor'] != '')
$this->tmpl->assign('searchfor', $_SESSION['searchfor']);
if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
$this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
$this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
}
if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
$this->tmpl->assign('tag_result', 1);
}
/* do we have to display the page selector ? */
if($this->cfg->thumbs_per_page != 0) {
$page_select = "";
/* calculate the page switchers */
$previous_start = $begin_with - $this->cfg->thumbs_per_page;
$next_start = $begin_with + $this->cfg->thumbs_per_page;
if($begin_with != 0)
$this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");");
if($end_with < $count)
$this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");");
$photo_per_page = $this->cfg->thumbs_per_page;
$last_page = ceil($count / $photo_per_page);
/* get the current selected page */
if($begin_with == 0) {
$current_page = 1;
} else {
$current_page = 0;
for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
$current_page++;
}
}
$dotdot_made = 0;
for($i = 1; $i <= $last_page; $i++) {
if($current_page == $i)
$style = "style=\"font-size: 125%; text-decoration: underline;\"";
elseif($current_page-1 == $i || $current_page+1 == $i)
$style = "style=\"font-size: 105%;\"";
elseif(($current_page-5 >= $i) && ($i != 1) ||
($current_page+5 <= $i) && ($i != $last_page))
$style = "style=\"font-size: 75%;\"";
else
$style = "";
$select = "". $i ." ";
// until 9 pages we show the selector from 1-9
if($last_page <= 9) {
$page_select.= $select;
continue;
} else {
if($i == 1 /* first page */ ||
$i == $last_page /* last page */ ||
$i == $current_page /* current page */ ||
$i == ceil($last_page * 0.25) /* first quater */ ||
$i == ceil($last_page * 0.5) /* half */ ||
$i == ceil($last_page * 0.75) /* third quater */ ||
(in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
(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 */ ||
$i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
$i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
$page_select.= $select;
$dotdot_made = 0;
continue;
}
}
if(!$dotdot_made) {
$page_select.= "......... ";
$dotdot_made = 1;
}
}
/* only show the page selector if we have more then one page */
if($last_page > 1)
$this->tmpl->assign('page_selector', $page_select);
}
$current_tags = $this->getCurrentTags();
$extern_link = "index.php?mode=showpi";
$rss_link = "index.php?mode=rss";
if($current_tags != "") {
$extern_link.= "&tags=". $current_tags;
$rss_link.= "&tags=". $current_tags;
}
if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
$extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
$rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
}
$export_link = "index.php?mode=export";
$slideshow_link = "index.php?mode=slideshow";
$this->tmpl->assign('extern_link', $extern_link);
$this->tmpl->assign('slideshow_link', $slideshow_link);
$this->tmpl->assign('export_link', $export_link);
$this->tmpl->assign('rss_link', $rss_link);
$this->tmpl->assign('count', $count);
$this->tmpl->assign('width', $this->cfg->thumb_width);
$this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
$this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
$this->tmpl->assign('images', $images);
$this->tmpl->assign('img_width', $img_width);
$this->tmpl->assign('img_height', $img_height);
$this->tmpl->assign('img_id', $img_id);
$this->tmpl->assign('img_name', $img_name);
$this->tmpl->assign('img_title', $img_title);
$this->tmpl->assign('thumbs', $thumbs);
$this->tmpl->show("photo_index.tpl");
if(isset($anchor))
print "\n";
} // showPhotoIndex()
/**
* show credit template
*/
public function showCredits()
{
$this->tmpl->assign('version', $this->cfg->version);
$this->tmpl->assign('product', $this->cfg->product);
$this->tmpl->assign('db_version', $this->dbver);
$this->tmpl->show("credits.tpl");
} // showCredits()
/**
* create_thumbnails for the requested width
*
* this function creates image thumbnails of $orig_image
* stored as $thumb_image. It will check if the image is
* in a supported format, if necessary rotate the image
* (based on EXIF orientation meta headers) and re-sizing.
*/
public function create_thumbnail($orig_image, $thumb_image, $width)
{
if(!file_exists($orig_image)) {
return false;
}
$details = getimagesize($orig_image);
/* check if original photo is a support image type */
if(!$this->checkifImageSupported($details['mime']))
return false;
$meta = $this->get_meta_informations($orig_image);
$rotate = 0;
$flip_hori = false;
$flip_vert = false;
switch($meta['Orientation']) {
case 1: /* top, left */
/* nothing to do */ break;
case 2: /* top, right */
$rotate = 0; $flip_hori = true; break;
case 3: /* bottom, left */
$rotate = 180; break;
case 4: /* bottom, right */
$flip_vert = true; break;
case 5: /* left side, top */
$rotate = 90; $flip_vert = true; break;
case 6: /* right side, top */
$rotate = 90; break;
case 7: /* left side, bottom */
$rotate = 270; $flip_vert = true; break;
case 8: /* right side, bottom */
$rotate = 270; break;
}
$src_img = @imagecreatefromjpeg($orig_image);
if(!$src_img) {
print "Can't load image from ". $orig_image ."\n";
return false;
}
/* grabs the height and width */
$cur_width = imagesx($src_img);
$cur_height = imagesy($src_img);
// If requested width is more then the actual image width,
// do not generate a thumbnail, instead safe the original
// as thumbnail but with lower quality. But if the image
// is to heigh too, then we still have to resize it.
if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
$result = imagejpeg($src_img, $thumb_image, 75);
imagedestroy($src_img);
return true;
}
// If the image will be rotate because EXIF orientation said so
// 'virtually rotate' the image for further calculations
if($rotate == 90 || $rotate == 270) {
$tmp = $cur_width;
$cur_width = $cur_height;
$cur_height = $tmp;
}
/* calculates aspect ratio */
$aspect_ratio = $cur_height / $cur_width;
/* sets new size */
if($aspect_ratio < 1) {
$new_w = $width;
$new_h = abs($new_w * $aspect_ratio);
} else {
/* 'virtually' rotate the image and calculate it's ratio */
$tmp_w = $cur_height;
$tmp_h = $cur_width;
/* now get the ratio from the 'rotated' image */
$tmp_ratio = $tmp_h/$tmp_w;
/* now calculate the new dimensions */
$tmp_w = $width;
$tmp_h = abs($tmp_w * $tmp_ratio);
// now that we know, how high they photo should be, if it
// gets rotated, use this high to scale the image
$new_h = $tmp_h;
$new_w = abs($new_h / $aspect_ratio);
// If the image will be rotate because EXIF orientation said so
// now 'virtually rotate' back the image for the image manipulation
if($rotate == 90 || $rotate == 270) {
$tmp = $new_w;
$new_w = $new_h;
$new_h = $tmp;
}
}
/* creates new image of that size */
$dst_img = imagecreatetruecolor($new_w, $new_h);
imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
/* copies resized portion of original image into new image */
imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
/* needs the image to be flipped horizontal? */
if($flip_hori) {
$this->_debug("(FLIP)");
$dst_img = $this->flipImage($dst_img, 'hori');
}
/* needs the image to be flipped vertical? */
if($flip_vert) {
$this->_debug("(FLIP)");
$dst_img = $this->flipImage($dst_img, 'vert');
}
if($rotate) {
$this->_debug("(ROTATE)");
$dst_img = $this->rotateImage($dst_img, $rotate);
}
/* write down new generated file */
$result = imagejpeg($dst_img, $thumb_image, 75);
/* free your mind */
imagedestroy($dst_img);
imagedestroy($src_img);
if($result === false) {
print "Can't write thumbnail ". $thumb_image ."\n";
return false;
}
return true;
} // create_thumbnail()
/**
* return all exif meta data from the file
*/
public function get_meta_informations($file)
{
return exif_read_data($file);
} // get_meta_informations()
/**
* create phpfspot own sqlite database
*
* this function creates phpfspots own sqlite database
* if it does not exist yet. this own is used to store
* some necessary informations (md5 sum's, ...).
*/
public function check_config_table()
{
// if the config table doesn't exist yet, create it
if(!$this->cfg_db->db_check_table_exists("images")) {
$this->cfg_db->db_exec("
CREATE TABLE images (
img_idx int primary key,
img_md5 varchar(32)
)
");
}
} // check_config_table
/**
* Generates a thumbnail from photo idx
*
* This function will generate JPEG thumbnails from provided F-Spot photo
* indizes.
*
* 1. Check if all thumbnail generations (width) are already in place and
* readable
* 2. Check if the md5sum of the original file has changed
* 3. Generate the thumbnails if needed
*/
public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
{
$error = 0;
$resolutions = Array(
$this->cfg->thumb_width,
$this->cfg->photo_width,
$this->cfg->mini_width,
);
/* get details from F-Spot's database */
$details = $this->get_photo_details($idx);
/* calculate file MD5 sum */
$full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
if(!file_exists($full_path)) {
$this->_error("File ". $full_path ." does not exist\n");
return;
}
if(!is_readable($full_path)) {
$this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
return;
}
$file_md5 = md5_file($full_path);
$this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
$changes = false;
foreach($resolutions as $resolution) {
$generate_it = false;
$thumb_sub_path = substr($file_md5, 0, 2);
$thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
if(!file_exists(dirname($thumb_path))) {
mkdir(dirname($thumb_path), 0755);
}
/* if the thumbnail file doesn't exist, create it */
if(!file_exists($thumb_path)) {
$generate_it = true;
}
/* if the file hasn't changed there is no need to regen the thumb */
elseif($file_md5 != $this->getMD5($idx) || $force) {
$generate_it = true;
}
if($generate_it || $overwrite) {
$this->_debug(" ". $resolution ."px");
if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
$error = 1;
$changes = true;
}
}
if(!$changes) {
$this->_debug(" already exist");
}
/* set the new/changed MD5 sum for the current photo */
if(!$error) {
$this->setMD5($idx, $file_md5);
}
$this->_debug("\n");
} // gen_thumb()
/**
* returns stored md5 sum for a specific photo
*
* this function queries the phpfspot database for a
* stored MD5 checksum of the specified photo
*/
public function getMD5($idx)
{
$result = $this->cfg_db->db_query("
SELECT img_md5
FROM images
WHERE img_idx='". $idx ."'
");
if(!$result)
return 0;
$img = $this->cfg_db->db_fetch_object($result);
return $img['img_md5'];
} // getMD5()
/**
* set MD5 sum for the specific photo
*/
private function setMD5($idx, $md5)
{
$result = $this->cfg_db->db_exec("
REPLACE INTO images (img_idx, img_md5)
VALUES ('". $idx ."', '". $md5 ."')
");
} // setMD5()
/**
* store current tag condition
*
* this function stores the current tag condition
* (AND or OR) in the users session variables
*/
public function setTagCondition($mode)
{
$_SESSION['tag_condition'] = $mode;
} // setTagCondition()
/**
* invoke tag & date search
*
* this function will return all matching tags and store
* them in the session variable selected_tags. furthermore
* it also handles the date search.
* getPhotoSelection() will then only return the matching
* photos.
*/
public function startSearch($searchfor, $from = 0, $to = 0)
{
$this->get_tags();
$_SESSION['searchfor'] = $searchfor;
if($from != 0)
$_SESSION['from_date'] = strtotime($from ." 00:00:00");
else
unset($_SESSION['from_date']);
if($to != 0)
$_SESSION['to_date'] = strtotime($to ." 23:59:59");
else
unset($_SESSION['to_date']);
if($searchfor != "") {
/* new search, reset the current selected tags */
$_SESSION['selected_tags'] = Array();
foreach($this->avail_tags as $tag) {
if(preg_match('/'. $searchfor .'/i', $this->tags[$tag]))
array_push($_SESSION['selected_tags'], $tag);
}
}
} // startSearch()
/**
* updates sort order in session variable
*
* this function is invoked by RPC and will sort the requested
* sort order in the session variable.
*/
public function updateSortOrder($order)
{
if(isset($this->sort_orders[$order])) {
$_SESSION['sort_order'] = $order;
return "ok";
}
return "unkown error";
} // updateSortOrder()
/**
* rotate image
*
* this function rotates the image according the
* specified angel.
*/
private function rotateImage($img, $degrees)
{
if(function_exists("imagerotate")) {
$img = imagerotate($img, $degrees, 0);
} else {
function imagerotate($src_img, $angle)
{
$src_x = imagesx($src_img);
$src_y = imagesy($src_img);
if ($angle == 180) {
$dest_x = $src_x;
$dest_y = $src_y;
}
elseif ($src_x <= $src_y) {
$dest_x = $src_y;
$dest_y = $src_x;
}
elseif ($src_x >= $src_y) {
$dest_x = $src_y;
$dest_y = $src_x;
}
$rotate=imagecreatetruecolor($dest_x,$dest_y);
imagealphablending($rotate, false);
switch ($angle) {
case 90:
for ($y = 0; $y < ($src_y); $y++) {
for ($x = 0; $x < ($src_x); $x++) {
$color = imagecolorat($src_img, $x, $y);
imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
}
}
break;
case 270:
for ($y = 0; $y < ($src_y); $y++) {
for ($x = 0; $x < ($src_x); $x++) {
$color = imagecolorat($src_img, $x, $y);
imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
}
}
break;
case 180:
for ($y = 0; $y < ($src_y); $y++) {
for ($x = 0; $x < ($src_x); $x++) {
$color = imagecolorat($src_img, $x, $y);
imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
}
}
break;
default:
$rotate = $src_img;
break;
};
return $rotate;
}
$img = imagerotate($img, $degrees);
}
return $img;
} // rotateImage()
/**
* returns flipped image
*
* this function will return an either horizontal or
* vertical flipped truecolor image.
*/
private function flipImage($image, $mode)
{
$w = imagesx($image);
$h = imagesy($image);
$flipped = imagecreatetruecolor($w, $h);
switch($mode) {
case 'vert':
for ($y = 0; $y < $h; $y++) {
imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
}
break;
case 'hori':
for ($x = 0; $x < $w; $x++) {
imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
}
break;
}
return $flipped;
} // flipImage()
/**
* return all assigned tags for the specified photo
*/
private function get_photo_tags($idx)
{
$result = $this->db->db_query("
SELECT t.id, t.name
FROM tags t
INNER JOIN photo_tags pt
ON t.id=pt.tag_id
WHERE pt.photo_id='". $idx ."'
");
$tags = Array();
while($row = $this->db->db_fetch_object($result))
$tags[$row['id']] = $row['name'];
return $tags;
} // get_photo_tags()
/**
* create on-the-fly images with text within
*/
public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
{
if (strlen($color) != 6)
$color = 000000;
$int = hexdec($color);
$h = imagefontheight($font);
$fw = imagefontwidth($font);
$txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
$lines = count($txt);
$im = imagecreate($w, (($h * $lines) + ($lines * $space)));
$bg = imagecolorallocate($im, 255, 255, 255);
$color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
$y = 0;
foreach ($txt as $text) {
$x = (($w - ($fw * strlen($text))) / 2);
imagestring($im, $font, $x, $y, $text, $color);
$y += ($h + $space);
}
Header("Content-type: image/png");
ImagePng($im);
} // showTextImage()
/**
* check if all requirements are met
*/
private function checkRequirements()
{
if(!function_exists("imagecreatefromjpeg")) {
print "PHP GD library extension is missing
\n";
$missing = true;
}
if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
print "PHP SQLite3 library extension is missing
\n";
$missing = true;
}
/* Check for HTML_AJAX PEAR package, lent from Horde project */
ini_set('track_errors', 1);
@include_once 'HTML/AJAX/Server.php';
if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
print "PEAR HTML_AJAX package is missing
\n";
$missing = true;
}
@include_once 'Calendar/Calendar.php';
if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
print "PEAR Calendar package is missing
\n";
$missing = true;
}
@include_once 'Console/Getopt.php';
if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
print "PEAR Console_Getopt package is missing
\n";
$missing = true;
}
ini_restore('track_errors');
if(isset($missing))
return false;
return true;
} // checkRequirements()
private function _debug($text)
{
if($this->fromcmd) {
print $text;
}
} // _debug()
/**
* check if specified MIME type is supported
*/
public function checkifImageSupported($mime)
{
if(in_array($mime, Array("image/jpeg")))
return true;
return false;
} // checkifImageSupported()
public function _error($text)
{
switch($this->cfg->logging) {
case 'display':
print "\n";
print $text;
break;
case 'errorlog':
error_log($text);
break;
case 'logfile':
error_log($text, 3, $his->cfg->log_file);
break;
}
} // _error()
/**
* output calendard input fields
*/
private function get_calendar($mode)
{
$year = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
$month = isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
$day = isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
$output = "\n";
$output.= "\n";
$output.= "\n";
return $output;
} // get_calendar()
/**
* output calendar matrix
*/
public function get_calendar_matrix($year = 0, $month = 0, $day = 0)
{
if (!isset($year)) $year = date('Y');
if (!isset($month)) $month = date('m');
if (!isset($day)) $day = date('d');
$rows = 1;
$cols = 1;
$matrix = Array();
require_once CALENDAR_ROOT.'Month/Weekdays.php';
require_once CALENDAR_ROOT.'Day.php';
// Build the month
$month = new Calendar_Month_Weekdays($year,$month);
// Create links
$prevStamp = $month->prevMonth(true);
$prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
$nextStamp = $month->nextMonth(true);
$next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
$selectedDays = array (
new Calendar_Day($year,$month,$day),
new Calendar_Day($year,12,25),
);
// Build the days in the month
$month->build($selectedDays);
$this->tmpl->assign('current_month', date('F Y',$month->getTimeStamp()));
$this->tmpl->assign('prev_month', $prev);
$this->tmpl->assign('next_month', $next);
while ( $day = $month->fetch() ) {
if(!isset($matrix[$rows]))
$matrix[$rows] = Array();
$string = "";
$dayStamp = $day->thisDay(true);
$link = "javascript:setCalendarDate(". date('Y',$dayStamp) .", ". date('n',$dayStamp).", ". date('j',$dayStamp) .");";
// isFirst() to find start of week
if ( $day->isFirst() )
$string.= "