Index: /trunk/admin/include/functions.php
===================================================================
--- /trunk/admin/include/functions.php	(revision 9190)
+++ /trunk/admin/include/functions.php	(revision 9191)
@@ -167,11 +167,7 @@
 }
 
-// The function delete_elements deletes the elements identified by the
-// (numeric) values of the array $ids. It also deletes (in the database) :
-//    - all the comments related to elements
-//    - all the links between categories and elements
-//    - all the favorites associated to elements
-// @return number of deleted elements
-function delete_elements($ids, $physical_deletion=false)
+// Deletes all files (on disk) related to given image ids
+// @return image ids where files are deleted successfully
+function delete_element_files($ids)
 {
   if (count($ids) == 0)
@@ -179,12 +175,10 @@
     return 0;
   }
-  trigger_action('begin_delete_elements', $ids);
-
-  if ($physical_deletion)
-  {
-    include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
-    $new_ids=array();
-
-    $query = '
+
+  include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
+  
+  $new_ids = array();
+
+  $query = '
 SELECT
     id,
@@ -196,38 +190,72 @@
   WHERE id IN ('.implode(',', $ids).')
 ;';
-    $result = pwg_query($query);
-    while ($row = pwg_db_fetch_assoc($result))
-    {
-      if (url_is_remote($row['path']))
-        continue;
-      $files = array();
-      $files[] = get_element_path($row);
-      if (!empty($row['tn_ext']))
-        $files[] = get_thumbnail_path($row);
-      if (!empty($row['has_high']) and get_boolean($row['has_high']))
-        $files[] = get_high_path($row);
-      if (!empty($row['representative_ext']))
+  $result = pwg_query($query);
+  while ($row = pwg_db_fetch_assoc($result))
+  {
+    if (url_is_remote($row['path']))
+    {
+      continue;
+    }
+    
+    $files = array();
+    $files[] = get_element_path($row);
+
+    if (!empty($row['tn_ext']))
+    {
+      $files[] = get_thumbnail_path($row);
+    }
+    
+    if (!empty($row['has_high']) and get_boolean($row['has_high']))
+    {
+      $files[] = get_high_path($row);
+    }
+    
+    if (!empty($row['representative_ext']))
+    {
+      $pi = pathinfo($row['path']);
+      $file_wo_ext = get_filename_wo_extension($pi['basename']);
+      $files[] = PHPWG_ROOT_PATH.$pi['dirname'].'/pwg_representative/'.$file_wo_ext.'.'.$row['representative_ext'];
+    }
+
+    $ok = true;
+    foreach ($files as $path)
+    {
+      if (is_file($path) and !unlink($path))
       {
-        $pi = pathinfo($row['path']);
-        $file_wo_ext = get_filename_wo_extension($pi['basename']);
-        $files[] = PHPWG_ROOT_PATH.$pi['dirname'].'/pwg_representative/'.$file_wo_ext.'.'.$element_info['representative_ext'];
+        $ok = false;
+        trigger_error('"'.$path.'" cannot be removed', E_USER_WARNING);
+        break;
       }
-
-      $ok = true;
-      foreach ($files as $path)
-      {
-        if (is_file($path) and !unlink($path))
-        {
-          $ok = false;
-          trigger_error('"'.$path.'" cannot be removed', E_USER_WARNING);
-          break;
-        }
-      }
-      if ($ok)
-        $new_ids[] += $row['id'];
-      else
-        break;
-    }
-    $ids = $new_ids;
+    }
+    
+    if ($ok)
+    {
+      $new_ids[] += $row['id'];
+    }
+    else
+    {
+      break;
+    }
+  }
+  return $new_ids;
+}
+
+// The function delete_elements deletes the elements identified by the
+// (numeric) values of the array $ids. It also deletes (in the database) :
+//    - all the comments related to elements
+//    - all the links between categories and elements
+//    - all the favorites associated to elements
+// @return number of deleted elements
+function delete_elements($ids, $physical_deletion=false)
+{
+  if (count($ids) == 0)
+  {
+    return 0;
+  }
+  trigger_action('begin_delete_elements', $ids);
+
+  if ($physical_deletion)
+  {
+    $ids = delete_element_files($ids);
     if (count($ids)==0)
     {
Index: /trunk/admin/include/functions_upload.inc.php
===================================================================
--- /trunk/admin/include/functions_upload.inc.php	(revision 9190)
+++ /trunk/admin/include/functions_upload.inc.php	(revision 9191)
@@ -169,5 +169,5 @@
 }
 
-function add_uploaded_file($source_filepath, $original_filename=null, $categories=null, $level=null)
+function add_uploaded_file($source_filepath, $original_filename=null, $categories=null, $level=null, $image_id=null)
 {
   // Here is the plan
@@ -186,35 +186,66 @@
   
   global $conf, $user;
-  
-  // current date
-  list($dbnow) = pwg_db_fetch_row(pwg_query('SELECT NOW();'));
-  list($year, $month, $day) = preg_split('/[^\d]/', $dbnow, 4);
-  
-  // upload directory hierarchy
-  $upload_dir = sprintf(
-    PHPWG_ROOT_PATH.$conf['upload_dir'].'/%s/%s/%s',
-    $year,
-    $month,
-    $day
-    );
-
-  // compute file path
+
   $md5sum = md5_file($source_filepath);
-  $date_string = preg_replace('/[^\d]/', '', $dbnow);
-  $random_string = substr($md5sum, 0, 8);
-  $filename_wo_ext = $date_string.'-'.$random_string;
-  $file_path = $upload_dir.'/'.$filename_wo_ext.'.';
-
-  list($width, $height, $type) = getimagesize($source_filepath);
-  if (IMAGETYPE_PNG == $type)
-  {
-    $file_path.= 'png';
+  $file_path = null;
+  
+  if (isset($image_id))
+  {
+    // we are performing an update
+    $query = '
+SELECT
+    path
+  FROM '.IMAGES_TABLE.'
+  WHERE id = '.$image_id.'
+;';
+    $result = pwg_query($query);
+    while ($row = pwg_db_fetch_assoc($result))
+    {
+      $file_path = $row['path'];
+    }
+    
+    if (!isset($file_path))
+    {
+      die('['.__FUNCTION__.'] this photo does not exist in the database');
+    }
+
+    // delete all physical files related to the photo (thumbnail, web site, HD)
+    delete_element_files(array($image_id));
   }
   else
   {
-    $file_path.= 'jpg';
-  }
-
-  prepare_directory($upload_dir);
+    // this photo is new
+    
+    // current date
+    list($dbnow) = pwg_db_fetch_row(pwg_query('SELECT NOW();'));
+    list($year, $month, $day) = preg_split('/[^\d]/', $dbnow, 4);
+  
+    // upload directory hierarchy
+    $upload_dir = sprintf(
+      PHPWG_ROOT_PATH.$conf['upload_dir'].'/%s/%s/%s',
+      $year,
+      $month,
+      $day
+      );
+
+    // compute file path
+    $date_string = preg_replace('/[^\d]/', '', $dbnow);
+    $random_string = substr($md5sum, 0, 8);
+    $filename_wo_ext = $date_string.'-'.$random_string;
+    $file_path = $upload_dir.'/'.$filename_wo_ext.'.';
+
+    list($width, $height, $type) = getimagesize($source_filepath);
+    if (IMAGETYPE_PNG == $type)
+    {
+      $file_path.= 'png';
+    }
+    else
+    {
+      $file_path.= 'jpg';
+    }
+
+    prepare_directory($upload_dir);
+  }
+
   if (is_uploaded_file($source_filepath))
   {
@@ -297,35 +328,75 @@
   $thumb_infos = pwg_image_infos($thumb_path);
 
-  // database registration
-  $insert = array(
-    'file' => pwg_db_real_escape_string(isset($original_filename) ? $original_filename : basename($file_path)),
-    'date_available' => $dbnow,
-    'tn_ext' => 'jpg',
-    'path' => preg_replace('#^'.preg_quote(PHPWG_ROOT_PATH).'#', '', $file_path),
-    'filesize' => $file_infos['filesize'],
-    'width' => $file_infos['width'],
-    'height' => $file_infos['height'],
-    'md5sum' => $md5sum,
-    'added_by' => $user['id'],
-    );
-
-  if (isset($high_infos))
-  {
-    $insert['has_high'] = 'true';
-    $insert['high_filesize'] = $high_infos['filesize'];
-  }
-
-  if (isset($level))
-  {
-    $insert['level'] = $level;
-  }
-  
-  mass_inserts(
-    IMAGES_TABLE,
-    array_keys($insert),
-    array($insert)
-    );
-  
-  $image_id = pwg_db_insert_id(IMAGES_TABLE);
+  if (isset($image_id))
+  {
+    $update = array(
+      'id' => $image_id,
+      'file' => pwg_db_real_escape_string(isset($original_filename) ? $original_filename : basename($file_path)),
+      'filesize' => $file_infos['filesize'],
+      'width' => $file_infos['width'],
+      'height' => $file_infos['height'],
+      'md5sum' => $md5sum,
+      'added_by' => $user['id'],
+      );
+    
+    if (isset($high_infos))
+    {
+      $update['has_high'] = 'true';
+      $update['high_filesize'] = $high_infos['filesize'];
+    }
+    else
+    {
+      $update['has_high'] = 'false';
+      $update['high_filesize'] = null;
+    }
+
+    if (isset($level))
+    {
+      $update['level'] = $level;
+    }
+
+    mass_updates(
+      IMAGES_TABLE,
+      array(
+        'primary' => array('id'),
+        'update' => array_keys($update)
+        ),
+      array($update)
+      );
+  }
+  else
+  {
+    // database registration
+    $insert = array(
+      'file' => pwg_db_real_escape_string(isset($original_filename) ? $original_filename : basename($file_path)),
+      'date_available' => $dbnow,
+      'tn_ext' => 'jpg',
+      'path' => preg_replace('#^'.preg_quote(PHPWG_ROOT_PATH).'#', '', $file_path),
+      'filesize' => $file_infos['filesize'],
+      'width' => $file_infos['width'],
+      'height' => $file_infos['height'],
+      'md5sum' => $md5sum,
+      'added_by' => $user['id'],
+      );
+
+    if (isset($high_infos))
+    {
+      $insert['has_high'] = 'true';
+      $insert['high_filesize'] = $high_infos['filesize'];
+    }
+
+    if (isset($level))
+    {
+      $insert['level'] = $level;
+    }
+  
+    mass_inserts(
+      IMAGES_TABLE,
+      array_keys($insert),
+      array($insert)
+      );
+  
+    $image_id = pwg_db_insert_id(IMAGES_TABLE);
+  }
 
   if (isset($categories) and count($categories) > 0)
@@ -343,5 +414,5 @@
   }
   update_metadata(array($image_id=>$file_path));
-  
+
   invalidate_user_cache();
 
Index: /trunk/include/ws_functions.inc.php
===================================================================
--- /trunk/include/ws_functions.inc.php	(revision 9190)
+++ /trunk/include/ws_functions.inc.php	(revision 9191)
@@ -1353,8 +1353,26 @@
     return new PwgError(405, "This method requires HTTP POST");
   }
+  
+  $params['image_id'] = (int)$params['image_id'];
+  if ($params['image_id'] > 0)
+  {
+    include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
+
+    $query='
+SELECT *
+  FROM '.IMAGES_TABLE.'
+  WHERE id = '.$params['image_id'].'
+;';
+
+    $image_row = pwg_db_fetch_assoc(pwg_query($query));
+    if ($image_row == null)
+    {
+      return new PwgError(404, "image_id not found");
+    }
+  }
 
   // category
   $params['category'] = (int)$params['category'];
-  if ($params['category'] <= 0)
+  if ($params['category'] <= 0 and $params['image_id'] <= 0)
   {
     return new PwgError(WS_ERR_INVALID_PARAM, "Invalid category_id");
@@ -1367,6 +1385,7 @@
     $_FILES['image']['tmp_name'],
     $_FILES['image']['name'],
-    array($params['category']),
-    8
+    $params['category'] > 0 ? array($params['category']) : null,
+    8,
+    $params['image_id'] > 0 ? $params['image_id'] : null
     );
 
@@ -1416,21 +1435,23 @@
   }
 
-  $query = '
+  $url_params = array('image_id' => $image_id);
+
+  if ($params['category'] > 0)
+  {
+    $query = '
 SELECT id, name, permalink
   FROM '.CATEGORIES_TABLE.'
   WHERE id = '.$params['category'].'
 ;';
-  $result = pwg_query($query);
-  $category = pwg_db_fetch_assoc($result);
+    $result = pwg_query($query);
+    $category = pwg_db_fetch_assoc($result);
+
+    $url_params['section'] = 'categories';
+    $url_params['category'] = $category;
+  }
 
   return array(
     'image_id' => $image_id,
-    'url' => make_picture_url(
-      array(
-        'image_id' => $image_id,
-        'section' => 'categories',
-        'category' => $category
-        )
-      ),
+    'url' => make_picture_url($url_params),
     );
 }
Index: /trunk/ws.php
===================================================================
--- /trunk/ws.php	(revision 9190)
+++ /trunk/ws.php	(revision 9191)
@@ -234,6 +234,7 @@
         ),
       'tags' => array('default' => null),
-      ),
-    'POST method only.<br>Use the <b>image</b> field for uploading file.<br>Set the form encoding to "form-data"<br><b>category</b> is the numeric identifier of the destination category.'
+      'image_id' => array('default' => null),
+      ),
+    'POST method only.<br>Use the <b>image</b> field for uploading file.<br>Set the form encoding to "form-data"<br><b>category</b> is the numeric identifier of the destination category.<br>You can update an existing photo if you define an existing image_id.'
     );
 
