source: trunk/admin/include/functions_upload.inc.php @ 8249

Last change on this file since 8249 was 8249, checked in by plg, 13 years ago

feature 2083 added: implement method pwg.images.addSimple in core

makes admin/include/function_upload.inc.php not dependant from include/ws_functions.inc.php (moves functions file_path_for_type and ready_for_upload_message)

cleaner method to initialize the upload settings

File size: 20.2 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based picture gallery                                  |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2010 Piwigo Team                  http://piwigo.org |
6// | Copyright(C) 2003-2008 PhpWebGallery Team    http://phpwebgallery.net |
7// | Copyright(C) 2002-2003 Pierrick LE GALL   http://le-gall.net/pierrick |
8// +-----------------------------------------------------------------------+
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                                          |
12// |                                                                       |
13// | This program is distributed in the hope that it will be useful, but   |
14// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
15// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
16// | General Public License for more details.                              |
17// |                                                                       |
18// | You should have received a copy of the GNU General Public License     |
19// | along with this program; if not, write to the Free Software           |
20// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
21// | USA.                                                                  |
22// +-----------------------------------------------------------------------+
23
24include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
25
26// add default event handler for image and thumbnail resize
27add_event_handler('upload_image_resize', 'pwg_image_resize', EVENT_HANDLER_PRIORITY_NEUTRAL, 7);
28add_event_handler('upload_thumbnail_resize', 'pwg_image_resize', EVENT_HANDLER_PRIORITY_NEUTRAL, 7);
29
30function get_upload_form_config()
31{
32  // default configuration for upload
33  $upload_form_config = array(
34    'websize_resize' => array(
35      'default' => true,
36      'can_be_null' => false,
37      ),
38   
39    'websize_maxwidth' => array(
40      'default' => 800,
41      'min' => 100,
42      'max' => 1600,
43      'pattern' => '/^\d+$/',
44      'can_be_null' => true,
45      'error_message' => l10n('The websize maximum width must be a number between %d and %d'),
46      ),
47 
48    'websize_maxheight' => array(
49      'default' => 600,
50      'min' => 100,
51      'max' => 1200,
52      'pattern' => '/^\d+$/',
53      'can_be_null' => true,
54      'error_message' => l10n('The websize maximum height must be a number between %d and %d'),
55      ),
56 
57    'websize_quality' => array(
58      'default' => 95,
59      'min' => 50,
60      'max' => 100,
61      'pattern' => '/^\d+$/',
62      'can_be_null' => false,
63      'error_message' => l10n('The websize image quality must be a number between %d and %d'),
64      ),
65 
66    'thumb_maxwidth' => array(
67      'default' => 128,
68      'min' => 50,
69      'max' => 300,
70      'pattern' => '/^\d+$/',
71      'can_be_null' => false,
72      'error_message' => l10n('The thumbnail maximum width must be a number between %d and %d'),
73      ),
74 
75    'thumb_maxheight' => array(
76      'default' => 96,
77      'min' => 50,
78      'max' => 300,
79      'pattern' => '/^\d+$/',
80      'can_be_null' => false,
81      'error_message' => l10n('The thumbnail maximum height must be a number between %d and %d'),
82      ),
83 
84    'thumb_quality' => array(
85      'default' => 95,
86      'min' => 50,
87      'max' => 100,
88      'pattern' => '/^\d+$/',
89      'can_be_null' => false,
90      'error_message' => l10n('The thumbnail image quality must be a number between %d and %d'),
91      ),
92 
93    'hd_keep' => array(
94      'default' => true,
95      'can_be_null' => false,
96      ),
97 
98    'hd_resize' => array(
99      'default' => false,
100      'can_be_null' => false,
101      ),
102 
103    'hd_maxwidth' => array(
104      'default' => 2000,
105      'min' => 500,
106      'max' => 20000,
107      'pattern' => '/^\d+$/',
108      'can_be_null' => false,
109      'error_message' => l10n('The high definition maximum width must be a number between %d and %d'),
110      ),
111 
112    'hd_maxheight' => array(
113      'default' => 2000,
114      'min' => 500,
115      'max' => 20000,
116      'pattern' => '/^\d+$/',
117      'can_be_null' => false,
118      'error_message' => l10n('The high definition maximum height must be a number between %d and %d'),
119      ),
120 
121    'hd_quality' => array(
122      'default' => 95,
123      'min' => 50,
124      'max' => 100,
125      'pattern' => '/^\d+$/',
126      'can_be_null' => false,
127      'error_message' => l10n('The high definition image quality must be a number between %d and %d'),
128      ),
129    );
130
131  return $upload_form_config;
132}
133
134/*
135 * automatic fill of configuration parameters
136 */
137function prepare_upload_configuration()
138{
139  global $conf;
140
141  $inserts = array();
142 
143  foreach (get_upload_form_config() as $param_shortname => $param)
144  {
145    $param_name = 'upload_form_'.$param_shortname;
146 
147    if (!isset($conf[$param_name]))
148    {
149      $param_value = boolean_to_string($param['default']);
150     
151      array_push(
152        $inserts,
153        array(
154          'param' => $param_name,
155          'value' => $param_value,
156          )
157        );
158
159      $conf[$param_name] = $param_value;
160      if (is_bool($param['default']))
161      {
162        $conf[$param_name] = get_boolean($param_value);
163      }
164    }
165  }
166 
167  if (count($inserts) > 0)
168  {
169    mass_inserts(
170      CONFIG_TABLE,
171      array_keys($inserts[0]),
172      $inserts
173      );
174  }
175}
176
177function add_uploaded_file($source_filepath, $original_filename=null, $categories=null, $level=null)
178{
179  // Here is the plan
180  //
181  // 1) move uploaded file to upload/2010/01/22/20100122003814-449ada00.jpg
182  //
183  // 2) if taller than max_height or wider than max_width, move to pwg_high
184  //    + web sized creation
185  //
186  // 3) thumbnail creation from web sized
187  //
188  // 4) register in database
189 
190  // TODO
191  // * check md5sum (already exists?)
192 
193  global $conf;
194 
195  // current date
196  list($dbnow) = pwg_db_fetch_row(pwg_query('SELECT NOW();'));
197  list($year, $month, $day) = preg_split('/[^\d]/', $dbnow, 4);
198 
199  // upload directory hierarchy
200  $upload_dir = sprintf(
201    PHPWG_ROOT_PATH.$conf['upload_dir'].'/%s/%s/%s',
202    $year,
203    $month,
204    $day
205    );
206
207  // compute file path
208  $md5sum = md5_file($source_filepath);
209  $date_string = preg_replace('/[^\d]/', '', $dbnow);
210  $random_string = substr($md5sum, 0, 8);
211  $filename_wo_ext = $date_string.'-'.$random_string;
212  $file_path = $upload_dir.'/'.$filename_wo_ext.'.';
213
214  list($width, $height, $type) = getimagesize($source_filepath);
215  if (IMAGETYPE_PNG == $type)
216  {
217    $file_path.= 'png';
218  }
219  else
220  {
221    $file_path.= 'jpg';
222  }
223
224  prepare_directory($upload_dir);
225  if (is_uploaded_file($source_filepath))
226  {
227    move_uploaded_file($source_filepath, $file_path);
228  }
229  else
230  {
231    copy($source_filepath, $file_path);
232  }
233
234  if ($conf['upload_form_websize_resize']
235      and need_resize($file_path, $conf['upload_form_websize_maxwidth'], $conf['upload_form_websize_maxheight']))
236  {
237    $high_path = file_path_for_type($file_path, 'high');
238    $high_dir = dirname($high_path);
239    prepare_directory($high_dir);
240   
241    rename($file_path, $high_path);
242    $high_infos = pwg_image_infos($high_path);
243   
244    trigger_event(
245      'upload_image_resize',
246      false,
247      $high_path,
248      $file_path,
249      $conf['upload_form_websize_maxwidth'],
250      $conf['upload_form_websize_maxheight'],
251      $conf['upload_form_websize_quality'],
252      false
253      );
254
255    if (is_imagick())
256    {
257      if ($conf['upload_form_hd_keep'])
258      {
259        if ($conf['upload_form_hd_resize'])
260        {
261          $need_resize = need_resize($high_path, $conf['upload_form_hd_maxwidth'], $conf['upload_form_hd_maxheight']);
262       
263          if ($need_resize)
264          {
265            pwg_image_resize(
266              false,
267              $high_path,
268              $high_path,
269              $conf['upload_form_hd_maxwidth'],
270              $conf['upload_form_hd_maxheight'],
271              $conf['upload_form_hd_quality'],
272              false
273              );
274            $high_infos = pwg_image_infos($high_path);
275          }
276        }
277      }
278      else
279      {
280        unlink($high_path);
281        $high_infos = null;
282      }
283    }
284  }
285
286  $file_infos = pwg_image_infos($file_path);
287 
288  $thumb_path = file_path_for_type($file_path, 'thumb');
289  $thumb_dir = dirname($thumb_path);
290  prepare_directory($thumb_dir);
291 
292  trigger_event(
293    'upload_thumbnail_resize',
294    false,
295    $file_path,
296    $thumb_path,
297    $conf['upload_form_thumb_maxwidth'],
298    $conf['upload_form_thumb_maxheight'],
299    $conf['upload_form_thumb_quality'],
300    true
301    );
302 
303  $thumb_infos = pwg_image_infos($thumb_path);
304
305  // database registration
306  $insert = array(
307    'file' => pwg_db_real_escape_string(isset($original_filename) ? $original_filename : basename($file_path)),
308    'date_available' => $dbnow,
309    'tn_ext' => 'jpg',
310    'path' => preg_replace('#^'.preg_quote(PHPWG_ROOT_PATH).'#', '', $file_path),
311    'filesize' => $file_infos['filesize'],
312    'width' => $file_infos['width'],
313    'height' => $file_infos['height'],
314    'md5sum' => $md5sum,
315    );
316
317  if (isset($high_infos))
318  {
319    $insert['has_high'] = 'true';
320    $insert['high_filesize'] = $high_infos['filesize'];
321  }
322
323  if (isset($level))
324  {
325    $insert['level'] = $level;
326  }
327 
328  mass_inserts(
329    IMAGES_TABLE,
330    array_keys($insert),
331    array($insert)
332    );
333 
334  $image_id = pwg_db_insert_id(IMAGES_TABLE);
335
336  if (isset($categories) and count($categories) > 0)
337  {
338    associate_images_to_categories(
339      array($image_id),
340      $categories
341      );
342  }
343 
344  // update metadata from the uploaded file (exif/iptc)
345  if ($conf['use_exif'] and !function_exists('read_exif_data'))
346  {
347    $conf['use_exif'] = false;
348  }
349  update_metadata(array($image_id=>$file_path));
350 
351  invalidate_user_cache();
352
353  return $image_id;
354}
355
356function prepare_directory($directory)
357{
358  if (!is_dir($directory)) {
359    if (substr(PHP_OS, 0, 3) == 'WIN')
360    {
361      $directory = str_replace('/', DIRECTORY_SEPARATOR, $directory);
362    }
363    umask(0000);
364    $recursive = true;
365    if (!@mkdir($directory, 0777, $recursive))
366    {
367      die('[prepare_directory] cannot create directory "'.$directory.'"');
368    }
369  }
370
371  if (!is_writable($directory))
372  {
373    // last chance to make the directory writable
374    @chmod($directory, 0777);
375
376    if (!is_writable($directory))
377    {
378      die('[prepare_directory] directory "'.$directory.'" has no write access');
379    }
380  }
381
382  secure_directory($directory);
383}
384
385function need_resize($image_filepath, $max_width, $max_height)
386{
387  // TODO : the resize check should take the orientation into account. If a
388  // rotation must be applied to the resized photo, then we should test
389  // invert width and height.
390  list($width, $height) = getimagesize($image_filepath);
391 
392  if ($width > $max_width or $height > $max_height)
393  {
394    return true;
395  }
396
397  return false;
398}
399
400function get_resize_dimensions($width, $height, $max_width, $max_height, $rotation=null)
401{
402  $rotate_for_dimensions = false;
403  if (isset($rotation) and in_array(abs($rotation), array(90, 270)))
404  {
405    $rotate_for_dimensions = true;
406  }
407
408  if ($rotate_for_dimensions)
409  {
410    list($width, $height) = array($height, $width);
411  }
412 
413  $ratio_width  = $width / $max_width;
414  $ratio_height = $height / $max_height;
415 
416  // maximal size exceeded ?
417  if ($ratio_width > 1 or $ratio_height > 1)
418  {
419    if ($ratio_width < $ratio_height)
420    { 
421      $destination_width = ceil($width / $ratio_height);
422      $destination_height = $max_height;
423    }
424    else
425    { 
426      $destination_width = $max_width; 
427      $destination_height = ceil($height / $ratio_width);
428    }
429  }
430
431  if ($rotate_for_dimensions)
432  {
433    list($destination_width, $destination_height) = array($destination_height, $destination_width);
434  }
435 
436  return array(
437    'width' => $destination_width,
438    'height'=> $destination_height,
439    );
440}
441
442function pwg_image_resize($result, $source_filepath, $destination_filepath, $max_width, $max_height, $quality, $strip_metadata=false)
443{
444  if ($result !== false)
445  {
446    //someone hooked us - so we skip
447    return $result;
448  }
449
450  if (is_imagick())
451  {
452    return pwg_image_resize_im($source_filepath, $destination_filepath, $max_width, $max_height, $quality, $strip_metadata);
453  }
454  else
455  {
456    return pwg_image_resize_gd($source_filepath, $destination_filepath, $max_width, $max_height, $quality);
457  }
458}
459
460function pwg_image_resize_gd($source_filepath, $destination_filepath, $max_width, $max_height, $quality)
461{
462  if (!function_exists('gd_info'))
463  {
464    return false;
465  }
466
467  // extension of the picture filename
468  $extension = strtolower(get_extension($source_filepath));
469
470  $source_image = null;
471  if (in_array($extension, array('jpg', 'jpeg')))
472  {
473    $source_image = imagecreatefromjpeg($source_filepath);
474  }
475  else if ($extension == 'png')
476  {
477    $source_image = imagecreatefrompng($source_filepath);
478  }
479  else
480  {
481    die('unsupported file extension');
482  }
483
484  $rotation = null;
485  if (function_exists('imagerotate'))
486  {
487    $rotation = get_rotation_angle($source_filepath);
488  }
489 
490  // width/height
491  $source_width  = imagesx($source_image); 
492  $source_height = imagesy($source_image);
493 
494  $resize_dimensions = get_resize_dimensions($source_width, $source_height, $max_width, $max_height, $rotation);
495
496  // testing on height is useless in theory: if width is unchanged, there
497  // should be no resize, because width/height ratio is not modified.
498  if ($resize_dimensions['width'] == $source_width and $resize_dimensions['height'] == $source_height)
499  {
500    // the image doesn't need any resize! We just copy it to the destination
501    copy($source_filepath, $destination_filepath);
502    return true;
503  }
504 
505  $destination_image = imagecreatetruecolor($resize_dimensions['width'], $resize_dimensions['height']);
506 
507  imagecopyresampled(
508    $destination_image,
509    $source_image,
510    0,
511    0,
512    0,
513    0,
514    $resize_dimensions['width'],
515    $resize_dimensions['height'],
516    $source_width,
517    $source_height
518    );
519
520  // rotation occurs only on resized photo to avoid useless memory use
521  if (isset($rotation))
522  {
523    $destination_image = imagerotate($destination_image, $rotation, 0);
524  }
525 
526  $extension = strtolower(get_extension($destination_filepath));
527  if ($extension == 'png')
528  {
529    imagepng($destination_image, $destination_filepath);
530  }
531  else
532  {
533    imagejpeg($destination_image, $destination_filepath, $quality);
534  }
535  // freeing memory ressources
536  imagedestroy($source_image);
537  imagedestroy($destination_image);
538
539  // everything should be OK if we are here!
540  return true;
541}
542
543function pwg_image_resize_im($source_filepath, $destination_filepath, $max_width, $max_height, $quality, $strip_metadata=false)
544{
545  // extension of the picture filename
546  $extension = strtolower(get_extension($source_filepath));
547  if (!in_array($extension, array('jpg', 'jpeg', 'png')))
548  {
549    die('[Imagick] unsupported file extension');
550  }
551
552  $image = new Imagick($source_filepath);
553
554  $rotation = null;
555  if (function_exists('imagerotate'))
556  {
557    $rotation = get_rotation_angle($source_filepath);
558  }
559 
560  // width/height
561  $source_width  = $image->getImageWidth();
562  $source_height = $image->getImageHeight();
563 
564  $resize_dimensions = get_resize_dimensions($source_width, $source_height, $max_width, $max_height, $rotation);
565
566  // testing on height is useless in theory: if width is unchanged, there
567  // should be no resize, because width/height ratio is not modified.
568  if ($resize_dimensions['width'] == $source_width and $resize_dimensions['height'] == $source_height)
569  {
570    // the image doesn't need any resize! We just copy it to the destination
571    copy($source_filepath, $destination_filepath);
572    return true;
573  }
574
575  $image->setImageCompressionQuality($quality);
576  $image->setInterlaceScheme(Imagick::INTERLACE_LINE);
577 
578  if ($strip_metadata)
579  {
580    // we save a few kilobytes. For example a thumbnail with metadata
581    // weights 25KB, without metadata 7KB.
582    $image->stripImage();
583  }
584 
585  $image->resizeImage($resize_dimensions['width'], $resize_dimensions['height'], Imagick::FILTER_LANCZOS, 0.9);
586
587  if (isset($rotation))
588  {
589    $image->rotateImage(new ImagickPixel(), -$rotation);
590    $image->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
591  }
592
593  $image->writeImage($destination_filepath);
594  $image->destroy();
595
596  // everything should be OK if we are here!
597  return true;
598}
599
600function get_rotation_angle($source_filepath)
601{
602  $rotation = null;
603 
604  $exif = exif_read_data($source_filepath);
605 
606  if (isset($exif['Orientation']) and preg_match('/^\s*(\d)/', $exif['Orientation'], $matches))
607  {
608    $orientation = $matches[1];
609    if (in_array($orientation, array(3, 4)))
610    {
611      $rotation = 180;
612    }
613    elseif (in_array($orientation, array(5, 6)))
614    {
615      $rotation = 270;
616    }
617    elseif (in_array($orientation, array(7, 8)))
618    {
619      $rotation = 90;
620    }
621  }
622
623  return $rotation;
624}
625
626function pwg_image_infos($path)
627{
628  list($width, $height) = getimagesize($path);
629  $filesize = floor(filesize($path)/1024);
630 
631  return array(
632    'width'  => $width,
633    'height' => $height,
634    'filesize' => $filesize,
635    );
636}
637
638function is_valid_image_extension($extension)
639{
640  return in_array(strtolower($extension), array('jpg', 'jpeg', 'png'));
641}
642
643function file_upload_error_message($error_code)
644{
645  switch ($error_code) {
646    case UPLOAD_ERR_INI_SIZE:
647      return sprintf(
648        l10n('The uploaded file exceeds the upload_max_filesize directive in php.ini: %sB'),
649        get_ini_size('upload_max_filesize', false)
650        );
651    case UPLOAD_ERR_FORM_SIZE:
652      return l10n('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form');
653    case UPLOAD_ERR_PARTIAL:
654      return l10n('The uploaded file was only partially uploaded');
655    case UPLOAD_ERR_NO_FILE:
656      return l10n('No file was uploaded');
657    case UPLOAD_ERR_NO_TMP_DIR:
658      return l10n('Missing a temporary folder');
659    case UPLOAD_ERR_CANT_WRITE:
660      return l10n('Failed to write file to disk');
661    case UPLOAD_ERR_EXTENSION:
662      return l10n('File upload stopped by extension');
663    default:
664      return l10n('Unknown upload error');
665  }
666}
667
668function get_ini_size($ini_key, $in_bytes=true)
669{
670  $size = ini_get($ini_key);
671
672  if ($in_bytes)
673  {
674    $size = convert_shortand_notation_to_bytes($size);
675  }
676 
677  return $size;
678}
679
680function convert_shortand_notation_to_bytes($value)
681{
682  $suffix = substr($value, -1);
683  $multiply_by = null;
684 
685  if ('K' == $suffix)
686  {
687    $multiply_by = 1024;
688  }
689  else if ('M' == $suffix)
690  {
691    $multiply_by = 1024*1024;
692  }
693  else if ('G' == $suffix)
694  {
695    $multiply_by = 1024*1024*1024;
696  }
697 
698  if (isset($multiply_by))
699  {
700    $value = substr($value, 0, -1);
701    $value*= $multiply_by;
702  }
703
704  return $value;
705}
706
707function add_upload_error($upload_id, $error_message)
708{
709  if (!isset($_SESSION['uploads_error']))
710  {
711    $_SESSION['uploads_error'] = array();
712  }
713  if (!isset($_SESSION['uploads_error'][$upload_id]))
714  {
715    $_SESSION['uploads_error'][$upload_id] = array();
716  }
717
718  array_push($_SESSION['uploads_error'][$upload_id], $error_message);
719}
720
721function is_imagick()
722{
723  if (extension_loaded('imagick'))
724  {
725    return true;
726  }
727
728  return false;
729}
730
731function ready_for_upload_message()
732{
733  global $conf;
734
735  $relative_dir = preg_replace('#^'.PHPWG_ROOT_PATH.'#', '', $conf['upload_dir']);
736
737  if (!is_dir($conf['upload_dir']))
738  {
739    if (!is_writable(dirname($conf['upload_dir'])))
740    {
741      return sprintf(
742        l10n('Create the "%s" directory at the root of your Piwigo installation'),
743        $relative_dir
744        );
745    }
746  }
747  else
748  {
749    if (!is_writable($conf['upload_dir']))
750    {
751      @chmod($conf['upload_dir'], 0777);
752     
753      if (!is_writable($conf['upload_dir']))
754      {
755        return sprintf(
756          l10n('Give write access (chmod 777) to "%s" directory at the root of your Piwigo installation'),
757          $relative_dir
758          );
759      }
760    }
761  }
762
763  return null;
764}
765
766function file_path_for_type($file_path, $type='thumb')
767{
768  // resolve the $file_path depending on the $type
769  if ('thumb' == $type) {
770    $file_path = get_thumbnail_location(
771      array(
772        'path' => $file_path,
773        'tn_ext' => 'jpg',
774        )
775      );
776  }
777
778  if ('high' == $type) {
779    @include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
780    $file_path = get_high_location(
781      array(
782        'path' => $file_path,
783        'has_high' => 'true'
784        )
785      );
786  }
787
788  return $file_path;
789}
790?>
Note: See TracBrowser for help on using the repository browser.