po/src/include/import.php

1399 lines
48 KiB
PHP

<?php
// Copyright (C) 2002-2006 Balint Kis (balint@k-i-s.net)
// Copyright (C) 2005-2013 Solomon Peachy (pizza@shaftnet.org)
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
include_once "config.php";
include_once "mime.php";
include_once "exif.php";
include_once "iptc.php";
include_once "rdf.php";
include_once "site.php";
function check_utf8($in_str) {
$cur_encoding = mb_detect_encoding($in_str) ;
if($cur_encoding == "UTF-8" && mb_check_encoding($in_str,"UTF-8"))
return $in_str;
else
return utf8_encode($in_str);
}
function count_storage_space_by_user($database, $user_id) {
$file_sizes = pg_query($database, "
select sum(filesize)
from files, photo_version, photo
where photo.users = $user_id
and photo.identifier = photo_version.photo
and files.version = photo_version.identifier");
$size = pg_fetch_row($file_sizes);
return $size[0];
}
function dirsize($database, $directory) {
$result = array("size" => 0, "files" => 0);
/* If we get a bogus directory... */
if (!strlen($directory))
return $result;
$query = pg_query($database, "select count(identifier), sum(filesize) from files where path LIKE '$directory%'");
if ($query && pg_num_rows($query)) {
$row = pg_fetch_row($query);
$result['files'] = $row[0];
$result['size'] = $row[1];
}
return $result;
}
function get_current_volume($database, $image_file) {
global $image_repository_path;
global $po_options_default;
$curr_volume = readlink($image_repository_path."/current");
$new_volume = $curr_volume;
if (substr($curr_volume, 0, 1) == '/') {
$curr_volume = substr($curr_volume, strlen($image_repository_path));
}
do {
$dir_size = dirsize($database, $new_volume);
if (is_file($image_file)) {
$image_size = filesize($image_file);
} else {
$image_size = 0;
}
$dir_size['size'] = round($dir_size['size']/1048576, 2);
/* if size is smaller return the volume as current volume */
if (($dir_size['size'] + $image_size/1048576) < $po_options_default['volume_max_size'])
break;
if ($po_options_default['volume_max_count'] &&
($po_options_default['volume_max_count'] < $dir_size['files']))
break;
$new_volume++;
if (($directory_handle = @opendir($image_repository_path."/".$new_volume))) {
/* if the next volume check if there is enough space */
closedir($directory_handle);
continue;
} else {
/* if the next volume does not exist, try to create it */
mkdir($image_repository_path."/".$new_volume."/orig", 0700, TRUE);
mkdir($image_repository_path."/".$new_volume."/thumb", 0700, TRUE);
if (($directory_handle = @opendir($image_repository_path."/".$new_volume))) {
closedir($directory_handle);
break;
} else {
return FALSE;
}
}
} while (TRUE);
if ($new_volume != $curr_volume) {
unlink($image_repository_path."/current");
po_symlink($new_volume, "$image_repository_path/current");
}
/* Make sure we pass back an absolute path */
if (substr($new_volume, 0, 1) != '/') {
$new_volume = "$image_repository_path/$new_volume";
}
return $new_volume;
}
function extract_exiftool_metadata($image_data, $index, $type = "EXIF", &$output) {
global $sys_exiftool;
global $strings;
$image_data["exiftool_data_$type"] = NULL;
$exif_data = array();
$file = $image_data['file'][$index]['name_tmp'];
$exclude_args = "";
switch ($type) {
case 'EXIF' :
$exclude_args = "-x xmp:all -x iptc:all";
break;
case 'IPTC' :
$exclude_args = "-iptc:all -charset IPTC=UTF8";
break;
case 'XMP' :
$exclude_args = "-xmp:all";
if (isset($image_data['sidecar'])) {
$file = $image_data['sidecar'];
}
break;
case 'ORIENT' :
$exclude_args = "-Orientation -ICCProfile -ColorSpace -ProfileClass -Make -Model -SerialNumber";
break;
}
$output .= "<li>".$strings['import_extracting_metadata']." ($type) ... ";
if (!is_executable($sys_exiftool)) {
$output .= err_str($strings['errors_exiftool_not_installed']) ."</li>\n";
return $image_data;
}
$handle = popen("$sys_exiftool $exclude_args " . escapeshellarg($file), "r");
if ($handle === FALSE) {
$output .= err_str($strings['errors_exiftool_failed']) ."</li>\n";
return $image_data;
}
while (!feof($handle)) {
$line = fgets($handle);
$parts = explode(":", $line, 2);
$parts[0] = trim($parts[0]);
if (!isset($parts[1])) continue;
$parts[1] = trim($parts[1]);
if ($parts[0] == "File Name") continue;
if ($parts[0] == "Directory") continue;
if (substr($parts[1], 2, 6) == "Binary") continue;
// print "<li>key: $parts[0] value: $parts[1]</li>";
$exif_data[$parts[0]] = $parts[1];
}
pclose($handle);
$image_data["exiftool_data_$type"] = $exif_data;
$output .= $strings['generic_done']."</li>\n";
return $image_data;
}
function transfer_metadata($source_name, $dest_name, $fix_orient, $image_data, &$output) {
global $sys_exiftool;
global $strings;
$po_options = $image_data['po_options'];
$retval = FALSE;
/* Copy over image tags */
if (is_executable($sys_exiftool)) {
if ($fix_orient) {
$orient = " -Orientation=1 -n ";
} else {
$orient = "";
}
$cmdline = "$sys_exiftool -q -overwrite_original -TagsFromFile " . escapeshellarg($source_name) ." $orient ". escapeshellarg($dest_name);
system($cmdline, $retval);
if ($retval) {
$output .= "<font color=\"red\">".$trings['import_transferring_metadata'] . " " . $strings['generic_failed'].".</font></li>\n";
$output .= "<li><pre>$cmdline</pre></li>\n";
return FALSE;
}
}
return TRUE;
}
function trim_filename($name) {
/* Trims the temporary directory name that is used during bulk upload */
$pos = strpos($name, "/");
if (is_integer($pos)) {
$original_name = substr($name, strrpos($name, "/") + 1);
} else {
$original_name = $name;
}
return $original_name;
}
function photo_import_all($database, $image_data) {
global $mime_type;
global $strings;
$po_user = $image_data['po_user'];
print "<ul>\n";
for ($index = 0; $index < $image_data['num_of_files']; $index++) {
$original_name = trim_filename($image_data['file'][$index]['name']);
$image_data['file'][$index]['original_name'] = $original_name;
$original_file_type = strtolower($image_data['file'][$index]['type']);
print "<li>".sprintf($strings['import_queueing'], $original_name, $mime_type[$original_file_type]['name'])."</li>\n";
}
print "</ul>\n";
if (isset($image_data['folder']) && $image_data['folder']) {
$folder = "'$image_data[folder]'";
} else {
$folder = "null";
}
$serialized = base64_encode(serialize($image_data));
/* This is implicitly ROW EXCLUSIVE locked */
pg_query($database, "insert into pending_imports(identifier, users, folder, photo_data) VALUES (nextval('pending_imports_sequence'), $po_user[id], $folder, '$serialized')");
return $image_data['num_of_files'];
}
function photo_import_worker($database, $userid = FALSE, $background = TRUE) {
while (TRUE) {
// printf("worker %d...\n", posix_getpid());
$add = '';
if ($userid !== FALSE) {
$add = " where users = $userid ";
}
/* Obtain a row from the database */
pg_query($database, "BEGIN");
$result = pg_query($database, "LOCK TABLE pending_imports IN SHARE ROW EXCLUSIVE MODE");
if ($result === FALSE) {
pg_query($database, "ABORT");
break;
}
$data = pg_query($database, "select identifier, photo_data from pending_imports $add order by identifier LIMIT 1");
if ($data !== FALSE && pg_num_rows($data) >= 1) {
$row = pg_fetch_row($data);
pg_query($database, "delete from pending_imports where identifier = $row[0]");
$result = pg_query($database, "COMMIT");
} else {
$result = pg_query($database, "ABORT");
break;
}
if ($result === FALSE) # Just in case!
break;
$image_data = unserialize(base64_decode($row[1]));
$po_options = $image_data['po_options'];
$output = "";
/* Make sure at least one is marked a master! */
if ($image_data['photo_id'] === FALSE) {
$master = FALSE;
for ($index = 0; $index < $image_data['num_of_files']; $index++) {
if ($image_data['file'][$index]['master'] == 't') {
$master = $index;
break;
}
}
/* If we don't have a master already, mark the appropriate one */
if ($master === FALSE) {
if ($po_options['import_last_master'] == 't') {
$master = $image_data['num_of_files']-1;
} else {
$master = 0;
}
$image_data['file'][$master]['master'] = 't';
}
/* Upload the master version */
$photo_master_id = photo_import_single($database, $master, $image_data, $output);
} else {
$photo_master_id = $image_data['photo_id'];
$master = -1;
}
/* upload all other photos as versions */
for ($index = 0; $index < $image_data['num_of_files']; $index++) {
if ($index === $master) continue;
if ($image_data['file'][$index]['master'] != 't') {
$image_data['file'][$index]['master'] = $po_options['import_last_master'];
}
$image_data['photo_id'] = $photo_master_id;
photo_import_single($database, $index, $image_data, $output);
}
if ($background) {
$po_user = $image_data['po_user'];
$output = pg_escape_string($database, $output);
$foo = pg_query($database, "insert into import_results(users, log_data) values ($po_user[id], '$output')");
} else {
print $output;
}
}
}
function photo_import_single($database, $index, &$image_data, &$output) {
global $mime_type;
global $strings;
global $image_repository_path;
global $memcache;
$master_photo_id = $image_data['photo_id'];
$po_user = $image_data['po_user'];
$po_options = $image_data['po_options'];
$old_master = 0;
$result = TRUE;
$query = '';
$cleanup_files = array();
if (($current_volume = get_current_volume($database, $image_data['file'][$index]['name_tmp'])) == FALSE) {
$output .= err_str($strings['errors_failed_volume'])."<br/>";
return FALSE;
}
if (!is_readable($image_data['file'][$index]['name_tmp'])) {
$output .= err_str($strings['errors_invalid_file'] . " (" . $image_data['file'][$index]['name_tmp'] .")")."<br/>";
return FALSE;
}
$cleanup_files[] = $image_data['file'][$index]['name_tmp'];
$original_name = $image_data['file'][$index]['original_name'];
if ($original_name == '') {
$output .= err_str($strings['errors_invalid_file'] . " (" . $image_data['file'][$index]['name'] .")")."<br/>";
return FALSE;
}
$original_file_type = strtolower($image_data['file'][$index]['type']);
$multi_page_parameter = $mime_type[$original_file_type]['page'];
$decoder = $mime_type[$original_file_type]['decoder'];
if (!$master_photo_id) {
/* Check ownership of folder */
$target_folder = pg_fetch_assoc(pg_query($database, "select caption, users from folder where identifier = $image_data[folder]"));
if (($po_user['type'] != PO_USER_TYPE_ADMIN) &&
($target_folder['users'] != $po_user['id'])) {
$output .= err_str($strings['errors_not_folder_owner']." (".emit_a(generate_link('folder', $image_data['folder']), $target_folder['caption']). ")")."<br/>";
return FALSE;
}
} else {
/* Check ownership of photo */
$photo_data = pg_fetch_assoc(pg_query($database, "select caption, users, identifier from photo where photo.identifier = '$master_photo_id'"));
if (($po_user['type'] != PO_USER_TYPE_ADMIN) &&
($photo_data['users'] != $po_user['id'])) {
$output .= err_str($strings['errors_not_owner']." (".emit_a(generate_link('photo', $photo_data['users']), "$photo_data[identifier] - $photo_data[caption]"). ")")."<br/>";
return FALSE;
}
}
/* Obtain new photo/version IDs as needed. Outside the transaction
because Postgresql serializes all sequences anyway */
if (!$master_photo_id) {
$next_index = pg_fetch_row(pg_query($database, "select nextval('photo_id_sequence')"));
$photo_id = $next_index[0];
} else {
$photo_id = $master_photo_id;
}
$next_version = pg_fetch_row(pg_query($database, "select nextval('photo_version_id_sequence')"));
$version_id = $next_version[0];
$hires_name = $current_volume . "/orig/" . $photo_id . "_" . $version_id ."_". "_0_orig." . $original_file_type;
if (!$master_photo_id) {
$output .= "<p>".sprintf($strings['import_importing_into_folder'], $original_name, $mime_type[$original_file_type]['name'], emit_a(generate_link('folder', $image_data['folder']), "<strong>$target_folder[caption]</strong>"))."</p>";
} else {
$master = "";
if ($image_data['file'][$index]['master'] == 't') {
$master = "( $strings[generic_master] )";
}
$output .= "<p>". sprintf($strings['import_importing_new_version'], $original_name, $mime_type[$original_file_type]['name'], emit_a(generate_link('photo', $master_photo_id, array('detail_info'=>5)), "<strong>$master_photo_id</strong>")) ." $master </p>";
}
$output .= "<ul class=\"import\">";
/* Check quotas */
if ($result && ($po_options['quota_size'] > 0)) {
$used_storage_space = count_storage_space_by_user($database, $po_user['id']);
$used_storage_space += filesize($image_data['file'][$index]['name_tmp']);
if ($po_options['quota_size'] <= $used_storage_space) {
$result = FALSE;
$po_options['quota_size'] /= 1048576;
$output .= "<li>";
$output .= err_str($strings['errors_quota_space_full'] . " ($po_options[quota_size] MB)")."<br/>";
$output .= $strings['import_no_more_uploads'];
$output .= "</li>\n";
}
}
if ($result && ($po_options['quota_count'] > 0)) {
$num_of_photo_versions = pg_fetch_row(pg_query($database, "select count_photo_versions_by_user($po_user[id])"));
$num_of_photo_versions++;
if ($po_options['quota_count'] <= $num_of_photo_versions[0]) {
$result = FALSE;
$output .= "<li>";
$output .= err_str($strings['errors_quota_count_full'] . " ($po_options[quota_count] ".$strings['generic_files'] .")")."<br/>";
$output .= $strings['import_no_more_uploads'];
$output .= "</li>\n";
}
}
if (!$master_photo_id) {
$master = 't';
if ($result) {
/* Extract any metadata */
$types = explode(',', $image_data['import_metadata_order']);
foreach ($types as $type) {
switch (strtolower($type)) {
case 'exif':
$image_data = photo_parse_exif($database, $index, $po_user['id'], $image_data, $output);
break;
case 'xmp':
$image_data = photo_parse_rdf($database, $index, $po_user['id'], $image_data, $output);
break;
case 'iptc':
$image_data = photo_parse_iptc($database, $index, $po_user['id'], $image_data, $output);
break;
}
}
}
} else {
$master = $image_data['file'][$index]['master'];
$image_data = extract_exiftool_metadata($image_data, $index, "ORIENT", $output);
$image_data = photo_parse_exif_orientation_colorspace($database, $index, $po_user['id'], $image_data, "ORIENT");
}
/* Start transaction here -- it's the first write */
pg_query($database, "begin");
if ($master_photo_id && ($master == 't')) {
if ($image_data['replace_existing'] == 't') {
$data = pg_fetch_row(pg_query($database, "select identifier from photo_version where photo = $master_photo_id and master = 't'"));
$old_master = $data[0];
}
$query = "update photo_version set master='f' where photo=$master_photo_id";
$result = pg_query($database, $query);
}
/* Figure out orientation and necessary transforms */
if ($result) {
$orientation = orientation_id_from_string($database, $image_data['file'][$index]["orientation"]);
$image_data['file'][$index]["orientation_xform"] = orientation_to_xform($database, $orientation);
if ($image_data['file'][$index]['colorspace'] == "") {
$image_data['file'][$index]['colorspace'] = "1";
}
}
/* Add Photo record into Database */
if ($result && !$master_photo_id) {
/* Sanity-check the timestamp */
$image_data['date_of_exposure'] = check_date_validity($image_data['date_of_exposure']);
$query = "insert into photo
(identifier, users, folder, location,
caption, date_of_exposure, access_rights, copyright_statement,
views, hide_original, author, title,
caption_writer, category, credit, source,
headline, instructions, transmission_reference,
supplemental_category, web_statement, store_url, comments)
values ($photo_id, '$po_user[id]', '$image_data[folder]', $image_data[location],
'$image_data[caption]', $image_data[date_of_exposure], '$image_data[access_rights]', '$image_data[copyright]',
0, '$image_data[hide_original]', '$image_data[author]', '$image_data[title]',
'$image_data[caption_writer]', '$image_data[category]', '$image_data[credit]', '$image_data[source]',
'$image_data[headline]', '$image_data[instructions]', '$image_data[transmission_reference]',
'$image_data[supplemental_category]', '$image_data[web_statement]', '$image_data[store_url]', '$image_data[remark]')";
$result = pg_query($database, $query);
}
/* Add Photo_Version record into Database */
if ($result) {
if ($master_photo_id) {
$key = pg_fetch_row(pg_query($database, "select max(key) from photo_version where photo=$master_photo_id"));
$key[0]++;
} else {
$key = array(1);
}
$original_name = pg_escape_string($database, $original_name);
$comment = $image_data['file'][$index]['remark'];
$orientation = orientation_id_from_string($database, $image_data['file'][$index]["orientation"]);
$query = "insert into photo_version(identifier, key, photo, master, original_image_name, comment, colorspace, orientation)
values ($version_id, '$key[0]', $photo_id, '$master', '$original_name', '$comment', ".$image_data['file'][$index]['colorspace']." , $orientation )";
$result = pg_query($database, $query);
}
/* Update album records to point to new version if desired */
if ($master_photo_id && $result && ($old_master != $version_id)) {
$result = pg_query($database, "update album_content set version = $version_id where photo = $master_photo_id and version = $old_master");
}
/* Now for the keywords. New photos only. */
if (!$master_photo_id && $image_data['keywords']) {
foreach ($image_data['keywords'] as $keyword) {
if ($result) {
$keyword = strtolower(trim($keyword));
$keyword = trim($keyword, ','); /* Trailing commas */
$keyword = trim($keyword);
if ($keyword == "") continue;
$keyword = pg_escape_string($database, $keyword);
$query = "insert into photo_keywords (photo, keyword)
values ($photo_id, '$keyword')";
$result = pg_query($database, $query);
}
}
}
/* Technical Info */
if (!$master_photo_id && $result) {
if ($image_data['latitude'] == "") $image_data['latitude'] = "null";
if ($image_data['longitude'] == "") $image_data['longitude'] = "null";
if ($image_data['altitude'] == "") $image_data['altitude'] = "null";
if ($image_data['direction'] == "") $image_data['direction'] = "null";
/* These are relatively raw, and need to be santitized */
$image_data['exif'] = check_utf8($image_data['exif']);
$image_data['iptc'] = check_utf8($image_data['iptc']);
$image_data['rdf'] = check_utf8($image_data['rdf']);
$query = "insert into photo_tech (identifier, photo, camera, film, scan_resolution, aperture, focal_length, shutter, camera_metering, camera_program, flash_mode, exposure_comp, flash_comp, ev_difference, iso_override, scan_bitdepth, scan_multiscan, exif, iptc, latitude, longitude, altitude, img_direction, rdf)
values (nextval('photo_tech_id_sequence'), $photo_id, $image_data[camera],
$image_data[film], $image_data[scan_resolution], $image_data[aperture],
$image_data[focal_length], $image_data[shutter], $image_data[camera_metering],
$image_data[camera_program], $image_data[flash_mode], $image_data[exp_comp],
$image_data[flash_comp], $image_data[exp_diff], $image_data[iso_override],
$image_data[scan_bitdepth], $image_data[scan_multiscan],
'$image_data[exif]', '$image_data[iptc]', $image_data[latitude], $image_data[longitude], $image_data[altitude], $image_data[direction], '$image_data[rdf]')";
$result = pg_query($database, $query);
}
/* Take care of equipment tables */
if (!$master_photo_id && $result && $image_data['equipment']) {
foreach ($image_data['equipment'] as $equip) {
$parts = explode(":", $equip);
$parts[0] = pg_escape_string($database, $parts[0]);
$parts[1] = pg_escape_string($database, $parts[1]);
if ($result) {
$query = "insert into photo_equipment (photo, equipment, type) VALUES ($photo_id, $parts[0], $parts[1])";
$result = pg_query($database, $query);
}
}
}
/* Commit the photo record to the DB. All that's left are the
image generation and insertions for each size into the DB. */
if ($result) {
pg_query($database, "commit");
} else {
if ($query != "") {
$error = pg_last_error($database);
$output .= "<li>" . err_str($error) ."</li>\n";
$output .= "<li>" . err_str($strings['errors_db_insert_failed']) ."</li>\n";
$output .= "<li><pre>$query</pre></li>\n";
}
pg_query($database, "rollback");
return FALSE;
}
/* Deal with original file! */
if ($result) {
$cleanup_files[] = $hires_name;
$result = import_orig_file($image_data, $index, $hires_name,
$decoder, $database, $version_id, $output);
}
/* Now for the XMP sidecar, if present. Masters only. */
if ($result && !$master_photo_id && isset($image_data['sidecar'])) {
$xmp_file = $current_volume . "/thumb/".$photo_id."_".$version_id."_0_orig.xmp";
if (!copy($image_data['sidecar'], $xmp_file)) {
$result = FALSE;
echo "<li><b>can't copy $image_data[sidecar] to $xmp_file</b></li>\n";
}
if ($result) {
$cleanup_files[] = $xmp_file;
$result = import_file($database, $xmp_file, $image_repository_path, $version_id, -1, FALSE, "", "");
if (!$result) {
echo "<li>file import blew up</li>\n";
}
unlink($image_data['sidecar']);
unset($image_data['sidecar']);
}
}
/* Generate scaled images */
if ($result) {
$result = import_generate_all_scaled($image_data['file'][$index]['name_tmp'], $multi_page_parameter, $hires_name, $database, $version_id, '', '', $current_volume, $photo_id, $cleanup_files, $hires_name, $image_data, $index, $output);
}
/* Final cleanups */
if ($result) {
$err = error_reporting(0);
unlink($image_data['file'][$index]['name_tmp']); /* We're done with it */
error_reporting($err);
if ($master_photo_id) {
po_log("photo_version_add: $version_id for $photo_id");
if ($memcache) {
po_log("memcache: delete versions.$photo_id", PEAR_LOG_DEBUG);
$memcache->delete("versions.$photo_id");
}
} else {
po_log("photo_add: $photo_id");
$output .= "<li>".$strings['import_complete']." (". emit_a(generate_link('photo', $photo_id), "#" . $photo_id) .")</li>\n";
}
} else {
$output .= "<li>".$strings['import_cleaning_up']."</li>\n";
$err = error_reporting(0);
foreach ($cleanup_files as $file) {
unlink($file);
}
error_reporting($err);
if ($master_photo_id) {
/* Nuke the stillborn version only! */
photo_version_delete($database, $master_photo_id, $version_id);
} else {
/* Move it into the trash so the next trash empty will clean it up for us */
pg_query($database, "update photo set folder = $po_user[trash_folder] where identifier = $photo_id");
}
}
$output .= "</ul>\n";
if (!$result) {
return FALSE;
}
/* If we have an embedded preview image, import it as a secondary version */
if (!$master_photo_id && $image_data['jpgfromraw']) {
$new_index = sizeof($image_data['file']);
$image_data['num_of_files']++;
/* Transfer metadata over to preview image */
$output .= "<li>";
transfer_metadata($hires_name,
$image_data['jpgfromraw'], FALSE, $image_data, $output);
$output .= "</li>";
/* Fill in the blanks */
$image_data['file'][$new_index]['name'] = $image_data['file'][$index]['name'] . "_jpgfromraw.jpg";
$image_data['file'][$new_index]['original_name'] = trim_filename($image_data['file'][$index]['name'] . "_jpgfromraw.jpg");
$image_data['file'][$new_index]['name_tmp'] = $image_data['jpgfromraw'];
$image_data['file'][$new_index]['type'] = "jpeg";
$image_data['file'][$new_index]['master'] = $po_options['jpgfromraw_master'];
$image_data['file'][$new_index]['orientation'] = $image_data['file'][$index]['orientation_orig'];
$image_data['file'][$new_index]['remark'] = "Preview image extracted from RAW";
$image_data['replace_existing'] = 'f';
$image_data['user'] = $po_user['id'];
}
/* attempt to remove the directory that the file was in. */
{
$path = dirname($image_data['file'][$index]['name']);
$err = error_reporting(0);
rmdir($path);
error_reporting($err);
}
if ($master_photo_id) {
return $version_id;
} else {
return $photo_id;
}
}
function import_decode_dcraw($input_file_name, $camera_input_profile, &$image_data, $index, &$output) {
global $sys_dcraw;
global $tmp_volume_path;
global $strings;
$po_options = $image_data['po_options'];
$temporary_file_name = tempnam($tmp_volume_path, "po");
unlink($temporary_file_name);
$temporary_file_name = $temporary_file_name . ".ppm";
if (!is_executable($sys_dcraw))
return FALSE;
$output .= "<li>".sprintf($strings['import_decoding_raw_using'], 'dcraw')." ... ";
/* Default to highest quality, and sRGB colorspace output */
$options = " -q 3 -o 1";
if ($po_options['dcraw_brightness'] != 1) {
$options .= " -b $po_options[dcraw_brightness]";
}
if ($po_options['raw_denoise'] > 0) {
$options .= " -n $po_options[raw_denoise]";
}
switch ($po_options['raw_white_balance']) {
case "automatic":
$options .= " -a";
break;
case "camera":
$options .= " -w";
break;
}
if ($po_options['raw_bitdepth'] == 48) {
$options .= " -4";
}
if ($camera_input_profile != FALSE) {
if ($camera_input_profile == "") $camera_input_profile = "embed";
$options = $options . " -p $camera_input_profile";
}
$cmdline = "$sys_dcraw $options -c " . escapeshellarg($input_file_name) . " > $temporary_file_name ";
system($cmdline, $retval_decoding);
if ($retval_decoding) {
$output .= "<font color=\"red\">".$strings['generic_failed'].".</font></li>\n";
$output .= "<li><pre>$cmdline</pre></li>\n";
return FALSE;
} else {
/* dcraw automatically rotates images */
$image_data['file'][$index]["orientation"] = "Normal (O deg)";
$output .= $strings['generic_done'].".</li>\n";
return $temporary_file_name;
}
}
function import_decode_exiftool($input_file_name, $camera_input_profile, &$image_data, $index, &$output) {
global $sys_exiftool;
global $tmp_volume_path;
global $strings;
if (!is_executable($sys_exiftool))
return FALSE;
/* If we don't have a RAW preview image, we're SOL here */
if (!$image_data['jpgfromraw'])
return FALSE;
/* If we're already extracted the image, just use that one. */
if (file_exists($image_data['jpgfromraw']))
return $image_data['jpgfromraw'];
/* Clear the jpegfromraw data so we don't think we have a secondary
image to import. If we actually do, the caller has to fix it up. */
$image_data['jpgfromraw'] = FALSE;
$temporary_file_name = tempnam($tmp_volume_path, "po");
unlink($temporary_file_name);
$temporary_file_name = $temporary_file_name . ".jpg";
$options = " -b -JpgFromRaw > " . escapeshellarg($temporary_file_name);
$output .= "<li>".sprintf($strings['import_decoding_raw_using'], 'ExifTool')." ... ";
$cmdline = "$sys_exiftool " . escapeshellarg($input_file_name) . " $options ";
system($cmdline , $retval_decoding);
if ($retval_decoding) {
$output .= "<font color=\"red\">".$strings['generic_failed'].".</font></li>\n";
$output .= "<li><pre>$cmdline</pre></li>\n";
return FALSE;
} else {
$output .= $strings['generic_done'].".</li>\n";
return $temporary_file_name;
}
}
function import_decode_gimp($input_file_name, $camera_input_profile, &$image_data, $index, &$output) {
global $sys_gimp;
global $tmp_volume_path;
global $strings;
if (!is_executable($sys_gimp))
return FALSE;
$temporary_file_name = tempnam($tmp_volume_path, "po");
unlink($temporary_file_name);
$temporary_file_name = $temporary_file_name . ".jpg";
$output .= "<li>".sprintf($strings['import_decoding_raw_using'], 'The GIMP')." ... ";
$script_fu = "
(let*((in-name \"$input_file_name\")
(out-name \"$temporary_file_name\")
(img (car (gimp-xcf-load 0 in-name in-name)))
(layer (car (gimp-image-flatten img))))
(file-jpeg-save 1 img layer out-name out-name 1.0 0.0 0 0 \"Temporary scratch file\" 0 0 0 1 )
(gimp-image-delete img)
(gimp-quit 0))
";
putenv("GIMP2_DIRECTORY=$tmp_volume_path/.gimp2");
putenv("DISPLAY=");
$cmdline = "$sys_gimp -n -f -i -s -d -c -b '$script_fu'";
system($cmdline , $retval_decoding);
if ($retval_decoding) {
$output .= "<font color=\"red\">".$strings['generic_failed'].".</font></li>\n";
$output .= "<li><pre>$cmdline</pre></li>\n";
return FALSE;
} else {
$output .= $strings['generic_done'].".</li>\n";
return $temporary_file_name;
}
}
function import_decode_mplayer($input_file_name, $camera_input_profile, &$image_data, $index, &$output) {
global $sys_mplayer;
global $tmp_volume_path;
global $strings;
if (!is_executable($sys_mplayer))
return FALSE;
$temporary_file_name = tempnam($tmp_volume_path, "po");
unlink($temporary_file_name);
$output .= "<li>".sprintf($strings['import_decoding_raw_using'], 'mplayer')." ... ";
$cmdline = "$sys_mplayer -quiet -ao null -vo pnm:ppm:outdir='$temporary_file_name' -frames 10 ".escapeshellarg($input_file_name);
system($cmdline , $retval_decoding);
if ($retval_decoding) {
$output .= "<font color=\"red\">".$strings['generic_failed'].".</font></li>\n";
$output .= "<li><pre>$cmdline</pre></li>\n";
return FALSE;
}
/* Grab the last frame we decoded, and erase the rest. */
$fname = FALSE;
for ($i = 10 ; $i > 0 ; $i--) {
$newname = sprintf("$temporary_file_name/000000%02d.ppm", $i);
if (file_exists($newname)) {
if ($fname === FALSE)
$fname = $newname;
else
unlink($newname);
}
}
$output .= $strings['generic_done'].".</li>\n";
return $fname;
}
function import_decode_ufraw($input_file_name, $camera_input_profile, &$image_data, $index, &$output) {
// global $icc_profiles;
global $sys_ufraw;
global $tmp_volume_path;
global $strings;
$po_options = $image_data['po_options'];
if (!is_executable($sys_ufraw))
return FALSE;
$options = "";
if ($camera_input_profile != FALSE && $camera_input_profile != "") {
$temporary_config_file = tempnam($tmp_volume_path, "po");
$output_file=fopen($temporary_config_file,"w");
fwrite($output_file,"<?xml version='1.0' encoding='utf-8'?>\n");
fwrite($output_file,"<UFRaw Version='7'>\n");
fwrite($output_file,"<InputProfile Current='yes'>Profile\n");
fwrite($output_file," <File>".$camera_input_profile."</File>\n");
fwrite($output_file,"</InputProfile>\n");
fwrite($output_file,"<sRGBOutputProfile Current='yes'>\n");
fwrite($output_file,"</sRGBOutputProfile>\n");
fwrite($output_file,"</UFRaw>\n");
fclose($output_file);
$options = $options . " --conf=$temporary_config_file";
}
$curve = "camera"; // curve == linear/camera/filename
$exposure = "auto"; // exposure compensation
$linearity = $po_options['ufraw_gamma_linearity'];
$gamma = $po_options['ufraw_gamma'];
if (($curve == "camera") || ($curve == "linear") || ($curve=="custom")) {
$curve = "--base-curve=$curve";
} else {
$curve = "--base-curve-file=$curve";
}
$temporary_file_name = tempnam($tmp_volume_path, "po");
unlink($temporary_file_name);
$temporary_file_name = $temporary_file_name . ".ppm";
$output .= "<li>".sprintf($strings['import_decoding_raw_using'], 'ufraw')." ... ";
if ($po_options['raw_denoise'] > 0) {
$options .= " --wavelet-denoising-threshold=$po_options[raw_denoise]";
}
$options .= " --exposure=$exposure --gamma=$gamma --linearity=$linearity --interpolation=ahd $curve";
switch ($po_options['raw_white_balance']) {
case "automatic":
$options .= " --wb=auto";
break;
case "camera":
$options .= " --wb=camera";
}
switch ($po_options['raw_bitdepth']) {
case 24:
$options .= " --out-type=ppm8";
break;
case 48:
$options .= " --out-type=ppm16";
}
$cmdline = "$sys_ufraw $options --silent --output=$temporary_file_name " . escapeshellarg($input_file_name);
system($cmdline, $retval_decoding);
if ($retval_decoding) {
$output .= "<font color=\"red\">".$strings['generic_failed'].".</font></li>\n";
$output .= "<li><pre>$cmdline</pre></li>\n";
unlink($temporary_config_file);
return FALSE;
} else {
/* ufraw automatically rotates images */
$image_data['file'][$index]["orientation"] = "Normal (O deg)";
$output .= $strings['generic_done'].".</li>\n";
unlink($temporary_config_file);
return $temporary_file_name;
}
}
function import_decode_dcm2pnm($input_file_name, $camera_input_profile, &$image_data, $index, &$output) {
global $sys_dcm2pnm;
global $tmp_volume_path;
global $strings;
if (!is_executable($sys_dcm2pnm))
return FALSE;
$temporary_file_name = tempnam($tmp_volume_path, "po");
unlink($temporary_file_name);
$temporary_file_name = $temporary_file_name . ".pnm";
$output .= "<li>".sprintf($strings['import_decoding_raw_using'], 'dcm2pnm')." ... ";
$options = "";
switch ($po_options['raw_bitdepth']) {
case 24:
$options = $options . " --write-8-bit-pnm";
break;
case 48:
$options = $options . " --write-16-bit-pnm";
}
$cmdline = "$sys_dcm2pnm $options " . escapeshellarg($input_file_name) ." $temporary_file_name ";
system($cmdline, $retval_decoding);
if ($retval_decoding) {
$output .= "<font color=\"red\">".$strings['generic_failed'].".</font></li>\n";
$output .= "<li><pre>$cmdline</pre></li>\n";
return FALSE;
} else {
$output .= $strings['generic_done'].".</li>\n";
return $temporary_file_name;
}
}
function import_decode_darktable($input_file_name, $camera_input_profile, &$image_data, $index, &$output) {
global $sys_darktable;
global $tmp_volume_path;
global $strings;
$po_options = $image_data['po_options'];
$temporary_file_name = tempnam($tmp_volume_path, "po");
unlink($temporary_file_name);
$temporary_file_name = $temporary_file_name . ".ppm";
if (!is_executable($sys_darktable))
return FALSE;
$output .= "<li>".sprintf($strings['import_decoding_raw_using'], 'darktable')." ... ";
/* Default to highest quality, and sRGB colorspace output */
$options = " -hq 1 ";
// if ($po_options['dcraw_brightness'] != 1) {
// $options .= " -b $po_options[dcraw_brightness]";
// }
// if ($po_options['raw_denoise'] > 0) {
// $options .= " -n $po_options[raw_denoise]";
// }
// switch ($po_options['raw_white_balance']) {
// case "automatic":
// $options .= " -a";
// break;
// case "camera":
// $options .= " -w";
// break;
// }
if ($po_options['raw_bitdepth'] == 48) {
$options .= " --bpp 16";
} else {
$options .= " --bpp 8";
}
// if ($camera_input_profile != FALSE) {
// if ($camera_input_profile == "") $camera_input_profile = "embed";
// $options = $options . " -p $camera_input_profile";
// }
$cmdline = "$sys_darktable " . escapeshellarg($input_file_name) . " $temporary_file_name $options ";
system($cmdline, $retval_decoding);
if ($retval_decoding) {
$output .= "<font color=\"red\">".$strings['generic_failed'].".</font></li>\n";
$output .= "<li><pre>$cmdline</pre></li>\n";
return FALSE;
} else {
/* dcraw automatically rotates images */
$image_data['file'][$index]["orientation"] = "Normal (O deg)";
$output .= $strings['generic_done'].".</li>\n";
return $temporary_file_name;
}
}
function import_decode($input_file_name, $decoder, $camera_input_profile, &$image_data, $index, &$output) {
global $icc_profiles;
if (($camera_input_profile == FALSE) ||
($camera_input_profile == null) ||
($camera_input_profile == "None")) {
$camera_input_profile = FALSE;
} else {
$camera_input_profile = $icc_profiles[$camera_input_profile]['file'];
}
/* Figure out if we have an embedded preview image to extract */
if ($image_data['jpgfromraw']) {
/* This extracts the embedded image to a temporary file */
$image_data['jpgfromraw'] = import_decode_exiftool($input_file_name, $camera_input_profile, $image_data, $index, $output);
}
$res = FALSE;
$decoders = explode(",", $decoder);
foreach ($decoders as $decoder) {
$decoder = trim($decoder);
if ($decoder == "dcraw") {
$res = import_decode_dcraw($input_file_name, $camera_input_profile, $image_data, $index, $output);
} else if ($decoder == "exiftool") {
$res = import_decode_exiftool($input_file_name, $camera_input_profile, $image_data, $index, $output);
} else if ($decoder == "ufraw") {
$res = import_decode_ufraw($input_file_name, $camera_input_profile, $image_data, $index, $output);
} else if ($decoder == "darktable") {
$res = import_decode_darktable($input_file_name, $camera_input_profile, $image_data, $index, $output);
} else if ($decoder == "dcm2pnm") {
$res = import_decode_dcm2pnm($input_file_name, $camera_input_profile, $image_data, $index, $output);
} else if ($decoder == "gimp") {
$res = import_decode_gimp($input_file_name, $camera_input_profile, $image_data, $index, $output);
} else if ($decoder == "mplayer") {
$res = import_decode_mplayer($input_file_name, $camera_input_profile, $image_data, $index, $output);
} else {
$res = FALSE;
}
if ($res !== FALSE) break;
}
$image_data['files'][$index]['decoded'] = $res;
return $res;
}
function import_generate_scaled($input_file_name, $output_file_name,
$multi_page_parameter, $size_key,
$watermark, $original_file_name,
$database, $version_id, $params,
$comments, $master_file_name,
&$image_data, $index, &$output)
{
global $sys_convert;
global $sys_composite;
global $sys_gm;
global $use_gm;
global $icc_profiles;
global $strings;
global $image_repository_path;
$po_options = $image_data['po_options'];
$orientation = $image_data['file'][$index]['orientation_xform'];
$colorspace = $image_data['file'][$index]['colorspace'];
/* Resize */
switch ($size_key) {
case 'thumb':
$size_name = 'thumb';
$resize_cmd = ' -resize ' . escapeshellarg($po_options[$size_key.'_max_size'] ."x". $po_options[$size_key.'_max_size']) ."\> ";
break;
case 'full':
# no resizing.
$size_name = 'full';
$resize_cmd = '';
if ($image_data['files'][$index]['decoded'] === FALSE) {
/* There's no point in doing another full-res version */
return TRUE;
}
break;
default:
/* Don't generate "scaled" versions greater than the original size */
if ($po_options[$size_key.'_sizecode'] != 2 &&
$po_options[$size_key.'_max_size'] > $image_data['max_res']) {
return TRUE;
}
$size_name = 'preview';
$resize_cmd = ' -resize ' . escapeshellarg($po_options[$size_key.'_max_size'] ."x". $po_options[$size_key.'_max_size']) ."\> ";
break;
}
/* matting options */
if (($po_options[$size_key.'_border_width'] != 0) && ($po_options[$size_key.'_border_height'] != 0))
$im_framing_option = " -frame " . escapeshellarg($po_options[$size_key.'_border_width'] ."x". $po_options[$size_key.'_border_height']) . " -mattecolor " . escapeshellarg("#".$po_options[$size_key.'_border_color']);
else
$im_framing_option = "";
/* ICC transformation options */
$im_profile_a = $im_profile_b = "";
if ($colorspace != "" && $colorspace > "1") {
$im_profile_b = " -profile " . $icc_profiles[1]['file'];
if ($colorspace != "99") {
$im_profile_a = " +profile icm -profile " . $icc_profiles[$colorspace]['file'];
}
}
# Ensure we strip all profiles (icm, exif, etc) out of the final image..
$im_profile_b .= " +profile '*' ";
$output .= "<li>".$strings['import_generating_'.$size_name] ." ... ";
/* generate preview */
if ($use_gm) {
$cmdline = "$sys_gm convert";
} else {
$cmdline = "$sys_convert ";
}
$cmdline .=
$im_profile_a .
$resize_cmd .
" -unsharp ". escapeshellarg("0x". $po_options[$size_key.'_sharpen_sigma'] ."+". $po_options[$size_key.'_sharpen_amount'] ."+". $po_options[$size_key.'_sharpen_threshold']) ." ".
$im_framing_option ." ".
$orientation ." ".
$im_profile_b ." ".
(($po_options[$size_key.'_color_space'] == "RGB") ? "" : " -colorspace " . escapeshellarg($po_options[$size_key.'_color_space'])) ." ".
(($po_options[$size_key.'_gamma'] == 1) ? "" : " -gamma " . escapeshellarg($po_options[$size_key.'_gamma'])) .
" -quality " . escapeshellarg($po_options[$size_key.'_quality']) ." ".
escapeshellarg($input_file_name).escapeshellarg($multi_page_parameter) . ' ' .
($use_gm ? '' : ' -strip ') .
escapeshellarg($output_file_name);
system($cmdline, $retval_image_scaling);
if ($retval_image_scaling) {
$output .= "<font color=\"red\">".$strings['generic_failed'].".</font></li>\n";
$output .= "<li><pre>$cmdline</pre></li>\n";
return FALSE;
} else {
$output .= $strings['generic_done'] . " (". $po_options[$size_key.'_max_size'] ." ".$strings['generic_pixels']. ")</li>\n";
}
/* Are we supposed to watermark the image? */
if ($watermark) {
/* Fix up the watermark path as needed */
if (substr($po_options['watermark_path'], 0, 1) != '/') {
$po_options['watermark_path'] = "$image_repository_path/$po_options[watermark_path]";
}
$output .= "<li>".$strings['import_watermarking'] ." ... ";
if ($use_gm) {
$cmdline = "$sys_gm composite ";
} else {
$cmdline = "$sys_composite ";
}
$cmdline .=
" -gravity " . escapeshellarg($po_options[$size_key.'_watermark_location']) .
" -watermark " . escapeshellarg($po_options[$size_key.'_watermark_brightness']) ." ".
escapeshellarg($po_options['watermark_path']) ." ".
escapeshellarg($output_file_name)." ".
" -quality " . escapeshellarg($po_options[$size_key.'_quality']) ." ".
escapeshellarg($output_file_name);
system($cmdline, $retval_watermarking);
if ($retval_watermarking) {
$output .= "<font color=\"red\">".$strings['generic_failed'].".</font></li>\n";
$output .= "<li><pre>$cmdline</pre></li>\n";
return FALSE;
} else {
$output .= $strings['generic_done'].".</li>\n";
}
}
$result = TRUE;
/* A failure here isn't critical. Don't die. */
if ($po_options[$size_key.'_copy_metadata'] == 't') {
transfer_metadata($master_file_name, $output_file_name, TRUE, $image_data, $output);
}
if ($result) {
$result = import_file($database, $output_file_name, $image_repository_path,
$version_id, $po_options[$size_key.'_sizecode'],
$watermark, $params, $comments);
}
return $result;
}
/* Copies the original file into its resting place in the repository
and performs a RAW decode if necessary */
function import_orig_file(&$image_data, $index, $hires_name,
$decoder, $database, $version_id, &$output) {
global $strings;
global $image_repository_path;
$po_options = $image_data['po_options'];
$success = TRUE;
if ($image_data['file'][$index]['name_tmp']) {
$original_name = $image_data['file'][$index]['original_name'];
$output .= "<li>" . sprintf($strings['import_copying_into_repo'], $original_name) . " ... ";
/* copy the original to the repository */
if (!copy($image_data['file'][$index]['name_tmp'], $hires_name)) {
$output .= err_str($strings['generic_failed'])."</li>\n";
$output .= "<li><pre>cp ".$image_data['file'][$index]['name_tmp']." $hires_name</pre></li>\n";
return FALSE;
}
$output .= $strings['generic_done']."</li>\n";
/* Raw Decode */
$temporary_ppm_file = import_decode($image_data['file'][$index]['name_tmp'], $decoder, $image_data['camera_input_profile'], $image_data, $index, $output);
if ($temporary_ppm_file) {
unlink($image_data['file'][$index]['name_tmp']);
$image_data['file'][$index]['name_tmp'] = $temporary_ppm_file;
}
/* raw decode can alter orientation */
$orientation = orientation_id_from_string($database, $image_data['file'][$index]["orientation"]);
$image_data['file'][$index]["orientation_xform"] = orientation_to_xform($database, $orientation);
/* Copy the original image into the repository */
$success = import_file($database, $hires_name, $image_repository_path,
$version_id, 0, FALSE, '', '');
}
return $success;
}
/* Inserts a file record into the database. Also fixes permissions */
function import_file($database, $file_name, $base_path, $version_id,
$size_code, $watermark, $params, $comments)
{
global $query;
chmod($file_name, 0400); /* Owner Read Only */
if (!$watermark)
$watermark = 'null';
$params = pg_escape_string($database, $params);
$comments = pg_escape_string($database, $comments);
$sha1sum = sha1_file($file_name);
if ($size_code < 0) {
$image_dimension = array(0, 0);
} else {
$image_dimension = po_get_image_size($file_name);
}
$size = filesize($file_name);
$file = substr($file_name, strlen($base_path));
if (substr($file, 0, 1) == '/') {
$file = substr($file, 1);
}
$query = "INSERT INTO files
(identifier, version, size, x_res, y_res, path, filesize, params, comments, watermark, sha1sum)
VALUES ((select nextval('files_id_sequence')), $version_id, $size_code,
$image_dimension[0], $image_dimension[1], '$file',
$size, '$params', '$comments', $watermark, '$sha1sum')";
$result = pg_query($database, $query);
return $result;
}
function import_generate_all_scaled($input_file_name, $multi_page_parameter,
$original_file_name, $database, $version_id,
$params, $comments, $current_volume,
$photo_id, &$outfiles,
$master_file_name, &$image_data,
$index, &$output)
{
global $image_respository_path;
$po_options = $image_data['po_options'];
$sizes = explode(",", $po_options['scaled_generate']);
/* Get the original image dimensions */
$image_dimension = po_get_image_size($input_file_name);
/* If we can't deal with this file, don't import it? */
if (!$image_dimension[0] || !$image_dimension[1]) {
return TRUE;
}
$image_data['max_res'] = ($image_dimension[0] > $image_dimension[1]) ? $image_dimension[0] : $image_dimension[1];
foreach ($sizes as $size_key) {
$po_options = $image_data['po_options'];
if ($po_options[$size_key.'_watermark_location'] &&
$po_options[$size_key.'_watermark_brightness'] &&
$po_options['watermark_photo']) {
$watermark = $po_options['watermark_photo'];
// generate one watermark and one non-watermark image?
} else {
$watermark = FALSE;
}
$raw = sha1(sprintf("%d %d %d %s %s", time(), $photo_id, $version_id, $po_options[$size_key."_sizecode"], ($watermark? "_wm$watermark" : '')));
$output_file_name = "$current_volume/thumb/$raw" . "." . $po_options[$size_key."_format"];
$outfiles[] = $output_file_name;
$result = import_generate_scaled($input_file_name, $output_file_name,
$multi_page_parameter, $size_key,
$watermark, $original_file_name,
$database, $version_id, $params,
$comments, $master_file_name,
$image_data, $index, $output);
}
return $result;
}
?>