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

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

simpler code, useless to convert value twice

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