From 094bbe3cee63806dbdfba108f81817e3a85dada9 Mon Sep 17 00:00:00 2001 From: z0rglub Date: Sat, 18 Sep 2004 16:09:47 +0000 Subject: - new version of create_file_listing.php for future 1.4 branch - new feature : remote site management (generate listing, update, clean, delete) - on category page, no display of filesize if info not available in database git-svn-id: http://piwigo.org/svn/trunk@526 68402e56-0260-453c-a942-63ccdbb3a9ee --- admin.php | 9 +- admin/create_listing_file.php | 359 +++++++++++++---- admin/remote_site.php | 652 +++++++++++++++++++++++++++++++ include/category_default.inc.php | 9 +- language/en_UK.iso-8859-1/admin.lang.php | 21 + template/default/admin.tpl | 4 +- template/default/admin/remote_site.tpl | 61 +++ template/default/default.css | 19 + 8 files changed, 1039 insertions(+), 95 deletions(-) create mode 100644 admin/remote_site.php create mode 100644 template/default/admin/remote_site.tpl diff --git a/admin.php b/admin.php index 83714c0d6..c4c8c6faf 100644 --- a/admin.php +++ b/admin.php @@ -144,6 +144,12 @@ switch ( $_GET['page'] ) $title = $lang['title_picmod']; $page_valide = true; break; + case 'remote_site' : + { + $title = $lang['remote_sites']; + $page_valide = true; + break; + } default: $title = $lang['title_default']; break; } @@ -181,7 +187,7 @@ $template->assign_vars(array( 'L_GALLERY_INDEX'=>$lang['gallery_index'], 'L_GENERAL'=>$lang['general'], 'L_CONFIG'=>$lang['config'], - 'L_SITES'=>$lang['remote_site'], + 'L_SITES'=>$lang['remote_sites'], 'L_DEFAULT'=>$lang['gallery_default'], 'L_PHPINFO'=>$lang['phpinfos'], 'L_HISTORY'=>$lang['history'], @@ -212,6 +218,7 @@ $template->assign_vars(array( 'U_THUMBNAILS'=>add_session_id($link_start.'thumbnail' ), 'U_HISTORY'=>add_session_id($link_start.'stats' ), 'U_FAQ'=>add_session_id($link_start.'help' ), + 'U_SITES'=>add_session_id($link_start.'remote_site'), 'U_RETURN'=>add_session_id(PHPWG_ROOT_PATH.'category.php') )); diff --git a/admin/create_listing_file.php b/admin/create_listing_file.php index e58fe5720..bccf71479 100644 --- a/admin/create_listing_file.php +++ b/admin/create_listing_file.php @@ -24,35 +24,83 @@ // | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | // | USA. | // +-----------------------------------------------------------------------+ + +// +-----------------------------------------------------------------------+ +// | parameters | +// +-----------------------------------------------------------------------+ + +// prefix for thumbnails in "thumbnail" sub directories $conf['prefix_thumbnail'] = 'TN-'; -$conf['picture_ext'] = array ( 'jpg', 'gif', 'png', 'JPG', 'GIF', 'PNG' ); -$listing = ''; +// $conf['file_ext'] lists all extensions (case insensitive) allowed for +// your PhpWebGallery installation +$conf['file_ext'] = array('jpg','JPG','png','PNG','gif','GIF','mpg','zip', + 'avi','mp3','ogg'); -$end = strrpos( $_SERVER['PHP_SELF'], '/' ) + 1; -$local_folder = substr( $_SERVER['PHP_SELF'], 0, $end ); -$url = 'http://'.$_SERVER['HTTP_HOST'].$local_folder; +// $conf['picture_ext'] must bea subset of $conf['file_ext'] +$conf['picture_ext'] = array('jpg','JPG','png','PNG','gif','GIF'); -$listing.= ''.$url.''; +// $conf['version'] is used to verify the compatibility of the generated +// listing.xml file and the PhpWebGallery version you're running +$conf['version'] = 'BSF'; + +// +-----------------------------------------------------------------------+ +// | functions | +// +-----------------------------------------------------------------------+ + +/** + * returns a float value coresponding to the number of seconds since the + * unix epoch (1st January 1970) and the microseconds are precised : + * e.g. 1052343429.89276600 + * + * @return float + */ +function get_moment() +{ + $t1 = explode(' ', microtime()); + $t2 = explode('.', $t1[0]); + $t2 = $t1[1].'.'.$t2[1]; + return $t2; +} /** - * returns an array with all picture files according to $conf['picture_ext'] + * returns the number of seconds (with 3 decimals precision) between the + * start time and the end time given. + * + * @param float start + * @param float end + * @return void + */ +function get_elapsed_time($start, $end) +{ + return number_format($end - $start, 3, '.', ' ').' s'; +} + +/** + * returns an array with all picture files according to $conf['file_ext'] * * @param string $dir * @return array */ -function get_picture_files( $dir ) +function get_pwg_files($dir) { global $conf; $pictures = array(); - if ( $opendir = opendir( $dir ) ) + if ($opendir = opendir($dir)) { - while ( $file = readdir( $opendir ) ) + while ($file = readdir($opendir)) { - if ( in_array( get_extension( $file ), $conf['picture_ext'] ) ) + if (in_array(get_extension($file), $conf['file_ext'])) { - array_push( $pictures, $file ); + array_push($pictures, $file); + if (!preg_match('/^[a-zA-Z0-9-_.]+$/', $file)) + { + echo 'PWG-WARNING-2: "'.$file.'" : '; + echo 'The name of the file should be composed of '; + echo 'letters, figures, "-", "_" or "." ONLY'; + echo "\n"; + } } } } @@ -66,145 +114,286 @@ function get_picture_files( $dir ) * @param string $dir * @return array */ -function get_thumb_files( $dir ) +function get_thumb_files($dir) { global $conf; - $prefix_length = strlen( $conf['prefix_thumbnail'] ); + $prefix_length = strlen($conf['prefix_thumbnail']); $thumbnails = array(); - if ( $opendir = @opendir( $dir ) ) + if ($opendir = @opendir($dir.'/thumbnail')) { - while ( $file = readdir( $opendir ) ) + while ($file = readdir($opendir)) { - if ( in_array( get_extension( $file ), $conf['picture_ext'] ) - and substr($file,0,$prefix_length) == $conf['prefix_thumbnail'] ) + if (in_array(get_extension($file), $conf['picture_ext']) + and substr($file,0,$prefix_length) == $conf['prefix_thumbnail']) { - array_push( $thumbnails, $file ); + array_push($thumbnails, $file); } } } return $thumbnails; } -// get_dirs retourne un tableau contenant tous les sous-répertoires d'un -// répertoire -function get_dirs( $basedir, $indent, $level ) +/** + * returns an array with representative picture files of a directory + * according to $conf['picture_ext'] + * + * @param string $dir + * @return array + */ +function get_representative_files($dir) +{ + global $conf; + + $pictures = array(); + if ($opendir = @opendir($dir.'/representative')) + { + while ($file = readdir($opendir)) + { + if (in_array(get_extension($file), $conf['picture_ext'])) + { + array_push($pictures, $file); + } + } + } + return $pictures; +} + +/** + * search in $basedir the sub-directories and calls get_pictures + * + * @return void + */ +function get_dirs($basedir, $indent, $level) { $fs_dirs = array(); $dirs = ""; - if ( $opendir = opendir( $basedir ) ) + if ($opendir = opendir($basedir)) { - while ( $file = readdir( $opendir ) ) + while ($file = readdir($opendir)) { - if ( $file != '.' - and $file != '..' - and is_dir ( $basedir.'/'.$file ) - and $file != 'thumbnail' ) + if ($file != '.' + and $file != '..' + and is_dir ($basedir.'/'.$file) + and $file != 'thumbnail') { - array_push( $fs_dirs, $file ); - if ( !preg_match( '/^[a-zA-Z0-9-_.]+$/', $file ) ) + array_push($fs_dirs, $file); + if (!preg_match('/^[a-zA-Z0-9-_.]+$/', $file)) { - echo '"'.$file.'" : '; + echo 'PWG-WARNING-1: "'.$file.'" : '; echo 'The name of the directory should be composed of '; echo 'letters, figures, "-", "_" or "." ONLY'; - echo '
'; + echo "\n"; } } } } // write of the dirs - foreach ( $fs_dirs as $fs_dir ) { + foreach ($fs_dirs as $fs_dir) + { $dirs.= "\n".$indent.''; - $dirs.= get_pictures( $basedir.'/'.$fs_dir, $indent.' ' ); - $dirs.= get_dirs( $basedir.'/'.$fs_dir, $indent.' ', $level + 1 ); + $dirs.= get_pictures($basedir.'/'.$fs_dir, $indent.' '); + $dirs.= get_dirs($basedir.'/'.$fs_dir, $indent.' ', $level + 1); $dirs.= "\n".$indent.''; } return $dirs; } // get_extension returns the part of the string after the last "." -function get_extension( $filename ) +function get_extension($filename) { - return substr( strrchr( $filename, '.' ), 1, strlen ( $filename ) ); + return substr(strrchr($filename, '.'), 1, strlen ($filename)); } // get_filename_wo_extension returns the part of the string before the last // ".". -// get_filename_wo_extension( 'test.tar.gz' ) -> 'test.tar' -function get_filename_wo_extension( $filename ) +// get_filename_wo_extension('test.tar.gz') -> 'test.tar' +function get_filename_wo_extension($filename) { - return substr( $filename, 0, strrpos( $filename, '.' ) ); + return substr($filename, 0, strrpos($filename, '.')); } -function get_pictures( $dir, $indent ) +function get_pictures($dir, $indent) { global $conf; - // fs means filesystem : $fs_pictures contains pictures in the filesystem - // found in $dir, $fs_thumbnails contains thumbnails... - $fs_pictures = get_picture_files( $dir ); - $fs_thumbnails = get_thumb_files( $dir.'/thumbnail' ); + // fs means FileSystem : $fs_files contains files in the filesystem found + // in $dir that can be managed by PhpWebGallery (see get_pwg_files + // function), $fs_thumbnails contains thumbnails, $fs_representatives + // contains potentially representative pictures for non picture files + $fs_files = get_pwg_files($dir); + $fs_thumbnails = get_thumb_files($dir); + $fs_representatives = get_representative_files($dir); - $root = "\n".$indent.''; + $elements = array(); + + foreach ($fs_files as $fs_file) + { + $element = array(); + $element['file'] = $fs_file; + $element['filesize'] = floor(filesize($dir.'/'.$fs_file) / 1024); + + $file_wo_ext = get_filename_wo_extension($fs_file); - foreach ( $fs_pictures as $fs_picture ) { - $file_wo_ext = get_filename_wo_extension( $fs_picture ); - $tn_ext = ''; - foreach ( $conf['picture_ext'] as $ext ) { + foreach ($conf['picture_ext'] as $ext) + { $test = $conf['prefix_thumbnail'].$file_wo_ext.'.'.$ext; - if ( !in_array( $test, $fs_thumbnails ) ) continue; - else { $tn_ext = $ext; break; } + if (!in_array($test, $fs_thumbnails)) + { + continue; + } + else + { + $element['tn_ext'] = $ext; + break; + } } - // if we found a thumnbnail corresponding to our picture... - if ( $tn_ext != '' ) + + // 2 cases : the element is a picture or not. Indeed, for a picture + // thumbnail is mandatory and for non picture element, thumbnail and + // representative is optionnal + if (in_array(get_extension($fs_file), $conf['picture_ext'])) { - list( $width,$height ) = @getimagesize( $dir.'/'.$fs_picture ); - - $root.= "\n".$indent.' '; - $root.= '"'.$fs_picture.'" : '; - echo 'The name of the picture should be composed of '; - echo 'letters, figures, "-", "_" or "." ONLY'; - echo '
'; + echo 'PWG-ERROR-1: The thumbnail is missing for '.$dir.'/'.$fs_file; + echo '-> '.$dir.'/thumbnail/'; + echo $conf['prefix_thumbnail'].$file_wo_ext.'.xxx'; + echo ' ("xxx" can be : '; + echo implode(', ', $conf['picture_ext']); + echo ')'."\n"; } } else { - echo 'The thumbnail is missing for '.$dir.'/'.$fs_picture; - echo '-> '.$dir.'/thumbnail/'; - echo $conf['prefix_thumbnail'].$file_wo_ext.'.xxx'; - echo ' ("xxx" can be : '; - echo implode( ', ', $conf['picture_ext'] ); - echo ')
'; + foreach ($conf['picture_ext'] as $ext) + { + $candidate = $file_wo_ext.'.'.$ext; + if (!in_array($candidate, $fs_representatives)) + { + continue; + } + else + { + $element['representative_ext'] = $ext; + break; + } + } + + array_push($elements, $element); } } - $root.= "\n".$indent.'
'; + $xml = "\n".$indent.''; + $attributes = array('file','tn_ext','representative_ext','filesize', + 'width','height'); + foreach ($elements as $element) + { + $xml.= "\n".$indent.' '; + $xml.= ''; - return $root; + return $xml; } -$listing.= get_dirs( '.', '', 0 ); - -if ( $fp = @fopen("./listing.xml","w") ) +// +-----------------------------------------------------------------------+ +// | script | +// +-----------------------------------------------------------------------+ +if (isset($_GET['action'])) { - fwrite( $fp, $listing ); - fclose( $fp ); - echo "listing.xml created"; + $page['action'] = $_GET['action']; } else { - echo "I can't write the file listing.xml"; + $page['action'] = 'generate'; +} +echo '
';
+switch ($page['action'])
+{
+  case 'generate' :
+  {
+    $start = get_moment();
+    
+    $listing = '';
 ?>
diff --git a/admin/remote_site.php b/admin/remote_site.php
new file mode 100644
index 000000000..5a5b2de91
--- /dev/null
+++ b/admin/remote_site.php
@@ -0,0 +1,652 @@
+              |
+// | branch        : BSF (Best So Far)                                     |
+// +-----------------------------------------------------------------------+
+// | file          : $RCSfile$
+// | last update   : $Date$
+// | last modifier : $Author$
+// | revision      : $Revision$
+// +-----------------------------------------------------------------------+
+// | 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                                          |
+// |                                                                       |
+// | 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA.                                                                  |
+// +-----------------------------------------------------------------------+
+
+if (!defined('PHPWG_ROOT_PATH'))
+{
+  die ("Hacking attempt!");
+}
+include_once(PHPWG_ROOT_PATH.'admin/include/isadmin.inc.php');
+
+define('CURRENT_DATE', date('Y-m-d'));
+// +-----------------------------------------------------------------------+
+// |                               functions                               |
+// +-----------------------------------------------------------------------+
+
+/**
+ * requests the given $url (a remote create_listing_file.php) and fills a
+ * list of lines corresponding to request output
+ *
+ * @param string $url
+ * @return void
+ */
+function remote_output($url)
+{
+  global $template, $errors, $lang;
+  
+  if($lines = @file($url))
+  {
+    $template->assign_block_vars('remote_output', array());
+    // cleaning lines from HTML tags
+    foreach ($lines as $line)
+    {
+      $line = trim(strip_tags($line));
+      if (preg_match('/^PWG-([A-Z]+)-/', $line, $matches))
+      {
+        $template->assign_block_vars(
+          'remote_output.remote_line',
+          array(
+            'CLASS' => 'remote'.ucfirst(strtolower($matches[1])),
+            'CONTENT' => $line
+           )
+         );
+      }
+    }
+  }
+  else
+  {
+    array_push($errors, $lang['remote_site_file_not_found']);
+  }
+}
+
+/**
+ * returns an array where are linked the sub-categories id and there
+ * directories corresponding to the given uppercat id
+ *
+ * @param int site_id
+ * @param mixed id_uppercat
+ * @return array
+ */
+function database_subdirs($site_id, $id_uppercat)
+{
+  $database_dirs = array();
+  
+  $query = '
+SELECT id,dir
+  FROM '.CATEGORIES_TABLE.'
+  WHERE site_id = '.$site_id;
+  if (!is_numeric($id_uppercat))
+  {
+    $query.= '
+    AND id_uppercat IS NULL';
+  }
+  else
+  {
+    $query.= '
+    AND id_uppercat = '.$id_uppercat;
+  }
+  // virtual categories not taken
+  $query.= '
+    AND dir IS NOT NULL
+;';
+  $result = mysql_query($query);
+  while ($row = mysql_fetch_array($result))
+  {
+    $database_dirs[$row['id']] = $row['dir'];
+  }
+
+  return $database_dirs;
+}
+
+/**
+ * inserts multiple lines in a table
+ *
+ * @param string table_name
+ * @param array dbields
+ * @param array inserts
+ * @return void
+ */
+function mass_inserts($table_name, $dbfields, $inserts)
+{
+  // inserts all found categories
+  $query = '
+INSERT INTO '.$table_name.'
+  ('.implode(',', $dbfields).')
+   VALUES';
+  foreach ($inserts as $insert_id => $insert)
+  {
+    $query.= '
+  ';
+    if ($insert_id > 0)
+    {
+      $query.= ',';
+    }
+    $query.= '(';
+    foreach ($dbfields as $field_id => $dbfield)
+    {
+      if ($field_id > 0)
+      {
+        $query.= ',';
+      }
+      
+      if (!isset($insert[$dbfield]) or $insert[$dbfield] == '')
+      {
+        $query.= 'NULL';
+      }
+      else
+      {
+        $query.= "'".$insert[$dbfield]."'";
+      }
+    }
+    $query.=')';
+  }
+  $query.= '
+;';
+  mysql_query($query);
+}
+
+/**
+ * searchs the "dir" node of the xml_dir given and insert the contained
+ * categories if the are not in the database yet. The function also deletes
+ * the categories that are in the database and not in the xml_file.
+ *
+ * @param string xml_content
+ * @param int site_id
+ * @param mixed id_uppercat
+ * @param int level
+ * @return void
+ */
+function insert_remote_category($xml_content, $site_id, $id_uppercat, $level)
+{
+  global $counts, $removes;
+  
+  $uppercats = '';
+  // 0. retrieving informations on the category to display
+		
+  if (is_numeric($id_uppercat))
+  {
+    $query = '
+SELECT name,uppercats,dir
+  FROM '.CATEGORIES_TABLE.'
+  WHERE id = '.$id_uppercat.'
+;';
+    $row = mysql_fetch_array(mysql_query($query));
+    
+    $uppercats = $row['uppercats'];
+    $name = $row['name'];
+
+    insert_remote_element($xml_content, $id_uppercat);
+  }
+
+  // $xml_dirs contains dir names contained in the xml file for this
+  // id_uppercat
+  $xml_dirs = array();
+  $temp_dirs = getChildren($xml_content, 'dir'.$level);
+  foreach ($temp_dirs as $temp_dir)
+  {
+    array_push($xml_dirs, getAttribute($temp_dir, 'name'));
+  }
+
+  // $database_dirs contains dir names contained in the database for this
+  // id_uppercat and site_id
+  $database_dirs = database_subdirs($site_id, $id_uppercat);
+  
+  // 3. we have to remove the categories of the database not present anymore
+  $to_delete = array();
+  foreach ($database_dirs as $id => $dir)
+  {
+    if (!in_array($dir, $xml_dirs))
+    {
+      array_push($to_delete, $id);
+      array_push($removes, get_complete_dir($id));
+    }
+  }
+  delete_categories($to_delete);
+
+  // array of new categories to insert
+  $inserts = array();
+  
+  foreach ($xml_dirs as $xml_dir)
+  {
+    // 5. Is the category already existing ? we create a subcat if not
+    //    existing
+    $category_id = array_search($xml_dir, $database_dirs);
+    if (!is_numeric($category_id))
+    {
+      $name = str_replace('_', ' ', $xml_dir);
+
+      $insert = array();
+
+      $insert{'dir'} = $xml_dir;
+      $insert{'name'} = $name;
+      $insert{'site_id'} = $site_id;
+      $insert{'uppercats'} = 'undef';
+      if (is_numeric($id_uppercat))
+      {
+        $insert{'id_uppercat'} = $id_uppercat;
+      }
+      array_push($inserts, $insert);
+    }
+  }
+
+  // we have to create the category
+  if (count($inserts) > 0)
+  {
+    // inserts all found categories
+    $dbfields = array('dir','name','site_id','uppercats','id_uppercat');
+    mass_inserts(CATEGORIES_TABLE, $dbfields, $inserts);
+    $counts{'new_categories'}+= count($inserts);
+    
+    // updating uppercats field
+    $query = '
+UPDATE '.CATEGORIES_TABLE.'
+  SET uppercats = ';
+    if ($uppercats != '')
+    {
+      $query.= "CONCAT('".$uppercats."',',',id)";
+    }
+    else
+    {
+      $query.= 'id';
+    }
+    $query.= '
+  WHERE id_uppercat ';
+    if (!is_numeric($id_uppercat))
+    {
+      $query.= 'IS NULL';
+    }
+    else
+    {
+      $query.= '= '.$id_uppercat;
+    }
+    $query.= '
+;';
+    mysql_query($query);
+  }
+
+  // Recursive call on the sub-categories (not virtual ones)
+  $database_dirs = database_subdirs($site_id, $id_uppercat);
+  
+  foreach ($temp_dirs as $temp_dir)
+  {
+    $dir = getAttribute($temp_dir, 'name');
+    $id_uppercat = array_search($dir, $database_dirs);
+    insert_remote_category($temp_dir, $site_id, $id_uppercat, $level+1);
+  }
+}
+
+/**
+ * searchs the "root" node of $xml_dir (xml string), inserts elements in the
+ * database if new
+ *
+ * @param string xml_dir
+ * @param int category_id
+ * @return void
+ */
+function insert_remote_element($xml_dir, $category_id)
+{
+  global $counts, $lang, $removes;
+
+  $output = '';
+  $root = getChild($xml_dir, 'root');
+
+  $xml_files = array();
+  $xml_elements = getChildren($root, 'element');
+  foreach ($xml_elements as $xml_element)
+  {
+    array_push($xml_files, getAttribute($xml_element,'file'));
+  }
+  
+  // we have to delete all the images from the database that are not in the
+  // directory anymore (not in the XML anymore)
+  $query = '
+SELECT id,file
+  FROM '.IMAGES_TABLE.'
+  WHERE storage_category_id = '.$category_id.'
+;';
+  $result = mysql_query($query);
+  while ($row = mysql_fetch_array($result))
+  {
+    if (!in_array($row['file'], $xml_files))
+    {
+      array_push($removes, $row['file']);
+      delete_element($row['id']);
+    }
+  }
+
+  $database_elements = array();
+  $query = '
+SELECT file
+  FROM '.IMAGES_TABLE.'
+  WHERE storage_category_id = '.$category_id.'
+;';
+  $result = mysql_query($query);
+  while ($row = mysql_fetch_array($result))
+  {
+    array_push($database_elements, $row['file']);
+  }
+
+  $inserts = array();
+  foreach ($xml_elements as $xml_element)
+  {
+    // minimal tag : 
+    $file = getAttribute($xml_element, 'file');
+
+    // is the picture already existing in the database ?
+    if (!in_array($file, $database_elements))
+    {
+      $insert = array();
+      $insert{'file'} = $file;
+      $insert{'storage_category_id'} = $category_id;
+      $insert{'date_available'} = CURRENT_DATE;
+      $optional_atts = array('tn_ext',
+                             'representative_ext',
+                             'filesize',
+                             'width',
+                             'height');
+      foreach ($optional_atts as $att)
+      {
+        if (getAttribute($xml_element, $att) != '')
+        {
+          $insert{$att} = getAttribute($xml_element, $att);
+        }
+      }
+      array_push($inserts, $insert);
+    }
+  }
+
+  if (count($inserts) > 0)
+  {
+    $dbfields = array('file','storage_category_id','date_available','tn_ext',
+                      'filesize','width','height');
+    mass_inserts(IMAGES_TABLE, $dbfields, $inserts);
+    $counts{'new_elements'}+= count($inserts);
+
+    // what are the ids of the pictures in the $category_id ?
+    $ids = array();
+
+    $query = '
+SELECT id
+  FROM '.IMAGES_TABLE.'
+  WHERE storage_category_id = '.$category_id.'
+;';
+    $result = mysql_query($query);
+    while ($row = mysql_fetch_array($result))
+    {
+      array_push($ids, $row['id']);
+    }
+
+    // recreation of the links between this storage category pictures and
+    // its storage category
+    $query = '
+DELETE FROM '.IMAGE_CATEGORY_TABLE.'
+  WHERE category_id = '.$category_id.'
+    AND image_id IN ('.implode(',', $ids).')
+;';
+    mysql_query($query);
+
+    $query = '
+INSERT INTO '.IMAGE_CATEGORY_TABLE.'
+  (category_id,image_id)
+  VALUES';
+    foreach ($ids as $num => $image_id)
+    {
+      $query.= '
+  ';
+      if ($num > 0)
+      {
+        $query.= ',';
+      }
+      $query.= '('.$category_id.','.$image_id.')';
+    }
+    $query.= '
+;';
+    mysql_query($query);
+  }
+}
+// +-----------------------------------------------------------------------+
+// |                             template init                             |
+// +-----------------------------------------------------------------------+
+$template->set_filenames(array('remote_site'=>'admin/remote_site.tpl'));
+
+$action = PHPWG_ROOT_PATH.'admin.php?page=remote_site';
+
+$template->assign_vars(
+  array(
+    'L_SUBMIT'=>$lang['submit'],
+    'L_REMOTE_SITE_CREATE'=>$lang['remote_site_create'],
+    'L_REMOTE_SITE_GENERATE'=>$lang['remote_site_generate'],
+    'L_REMOTE_SITE_GENERATE_HINT'=>$lang['remote_site_generate_hint'],
+    'L_REMOTE_SITE_UPDATE'=>$lang['remote_site_update'],
+    'L_REMOTE_SITE_UPDATE_HINT'=>$lang['remote_site_update_hint'],
+    'L_REMOTE_SITE_CLEAN'=>$lang['remote_site_clean'],
+    'L_REMOTE_SITE_CLEAN_HINT'=>$lang['remote_site_clean_hint'],
+    'L_REMOTE_SITE_DELETE'=>$lang['remote_site_delete'],
+    'L_REMOTE_SITE_DELETE_HINT'=>$lang['remote_site_delete_hint'],
+    'L_NB_NEW_ELEMENTS'=>$lang['update_nb_new_elements'],
+    'L_NB_NEW_CATEGORIES'=>$lang['update_nb_new_categories'],
+    'L_NB_DEL_ELEMENTS'=>$lang['update_nb_del_elements'],
+    'L_NB_DEL_CATEGORIES'=>$lang['update_nb_del_categories'],
+    'L_REMOTE_SITE_REMOVED_TITLE'=>$lang['remote_site_removed_title'],
+    'L_REMOTE_SITE_REMOVED'=>$lang['remote_site_removed'],
+    
+    'F_ACTION'=>add_session_id(PHPWG_ROOT_PATH.'admin.php?page=remote_site')
+   )
+ );
+// +-----------------------------------------------------------------------+
+// |                        new site creation form                         |
+// +-----------------------------------------------------------------------+
+$errors = array();
+
+if (isset($_POST['submit']))
+{
+  // site must start by http:// or https://
+  if (!preg_match('/^https?:\/\/[~\/\.\w-]+$/', $_POST['galleries_url']))
+  {
+    array_push($errors, $lang['remote_site_uncorrect_url']);
+  }
+  else
+  {
+    $page['galleries_url'] = preg_replace('/[\/]*$/',
+                                          '',
+                                          $_POST['galleries_url']);
+    $page['galleries_url'].= '/';
+    // site must not exists
+    $query = '
+SELECT COUNT(id) AS count
+  FROM '.SITES_TABLE.'
+  WHERE galleries_url = \''.$page['galleries_url'].'\'
+;';
+    $row = mysql_fetch_array(mysql_query($query));
+    if ($row['count'] > 0)
+    {
+      array_push($errors, $lang['remote_site_already_exists']);
+    }
+  }
+
+  if (count($errors) == 0)
+  {
+    $url = $page['galleries_url'].'create_listing_file.php';
+    $url.= '?action=test';
+    $url.= '&version='.PHPWG_VERSION;
+    if ($lines = @file($url))
+    {
+      $first_line = strip_tags($lines[0]);
+      if (!preg_match('/^PWG-INFO-2:/', $first_line))
+      {
+        array_push($errors, $lang['remote_site_error'].' : '.$first_line);
+      }
+    }
+    else
+    {
+      array_push($errors, $lang['remote_site_file_not_found']);
+    }
+  }
+  
+  if (count($errors) == 0)
+  {
+    $query = '
+INSERT INTO '.SITES_TABLE.'
+  (galleries_url)
+  VALUES
+  (\''.$page['galleries_url'].'\')
+;';
+    mysql_query($query);
+
+    $template->assign_block_vars(
+      'confirmation',
+      array(
+        'CONTENT'=>$page['galleries_url'].' '.$lang['remote_site_created']
+        ));
+  }
+}
+// +-----------------------------------------------------------------------+
+// |                            actions on site                            |
+// +-----------------------------------------------------------------------+
+if (isset($_GET['site']) and is_numeric($_GET['site']))
+{
+  $page['site'] = $_GET['site'];
+}
+
+if (isset($_GET['action']))
+{
+  $query = '
+SELECT galleries_url
+  FROM '.SITES_TABLE.'
+  WHERE id = '.$page['site'].'
+;';
+  $row = mysql_fetch_array(mysql_query($query));
+  $clf = $row['galleries_url'].'create_listing_file.php';
+  
+  switch($_GET['action'])
+  {
+    case 'delete' :
+    {
+      delete_site($page['site']);
+      
+      $template->assign_block_vars(
+        'confirmation',
+        array(
+          'CONTENT'=>$row['galleries_url'].' '.$lang['remote_site_deleted']
+          ));
+      
+      break;
+    }
+    case 'generate' :
+    {
+      $title = $row['galleries_url'].' : '.$lang['remote_site_generate'];
+      $template->assign_vars(array('REMOTE_SITE_TITLE'=>$title));
+      remote_output($clf.'?action=generate');
+      break;
+    }
+    case 'update' :
+    {
+      $title = $row['galleries_url'].' : '.$lang['remote_site_update'];
+      $template->assign_vars(array('REMOTE_SITE_TITLE'=>$title));
+      
+      if (@fopen($row['galleries_url'].'listing.xml', 'r'))
+      {
+        $counts = array(
+          'new_elements' => 0,
+          'new_categories' => 0,
+          'del_elements' => 0,
+          'del_categories' => 0
+          );
+        $removes = array();
+        
+        $xml_content = getXmlCode($row['galleries_url'].'listing.xml');
+        insert_remote_category($xml_content, $page{'site'}, 'NULL', 0);
+        update_category();
+        
+        $template->assign_block_vars(
+          'update',
+          array(
+            'NB_NEW_CATEGORIES'=>$counts['new_categories'],
+            'NB_DEL_CATEGORIES'=>$counts['del_categories'],
+            'NB_NEW_ELEMENTS'=>$counts['new_elements'],
+            'NB_DEL_ELEMENTS'=>$counts['del_elements']
+            ));
+        
+        if (count($removes) > 0)
+        {
+          $template->assign_block_vars('update.removes', array());
+        }
+        foreach ($removes as $remove)
+        {
+          $template->assign_block_vars('update.removes.remote_remove',
+                                       array('NAME'=>$remove));
+        }
+      }
+      else
+      {
+        array_push($errors, $lang['remote_site_listing_not_found']);
+      }
+      break;
+    }
+    case 'clean' :
+    {
+      $title = $row['galleries_url'].' : '.$lang['remote_site_clean'];
+      $template->assign_vars(array('REMOTE_SITE_TITLE'=>$title));
+      remote_output($clf.'?action=clean');
+      break;
+    }
+  }
+}
+// +-----------------------------------------------------------------------+
+// |                           remote sites list                           |
+// +-----------------------------------------------------------------------+
+
+// site 1 is the local site, should not be taken into account
+$query = '
+SELECT id, galleries_url
+  FROM '.SITES_TABLE.'
+  WHERE id != 1
+;';
+$result = mysql_query($query);
+while ($row = mysql_fetch_array($result))
+{
+  $base_url = PHPWG_ROOT_PATH.'admin.php';
+  $base_url.= '?page=remote_site';
+  $base_url.= '&site='.$row['id'];
+  $base_url.= '&action=';
+  
+  $template->assign_block_vars(
+    'site',
+    array(
+      'NAME' => $row['galleries_url'],
+      'U_GENERATE' => add_session_id($base_url.'generate'),
+      'U_UPDATE' => add_session_id($base_url.'update'),
+      'U_CLEAN' => add_session_id($base_url.'clean'),
+      'U_DELETE' => add_session_id($base_url.'delete')
+     )
+   );
+}
+// +-----------------------------------------------------------------------+
+// |                             errors display                            |
+// +-----------------------------------------------------------------------+
+if (count($errors) != 0)
+{
+  $template->assign_block_vars('errors',array());
+  foreach ($errors as $error)
+  {
+    $template->assign_block_vars('errors.error',array('ERROR'=>$error));
+  }
+}
+// +-----------------------------------------------------------------------+
+// |                           sending html code                           |
+// +-----------------------------------------------------------------------+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'remote_site');
+?>
\ No newline at end of file
diff --git a/include/category_default.inc.php b/include/category_default.inc.php
index 6eba15e8c..178f5a404 100644
--- a/include/category_default.inc.php
+++ b/include/category_default.inc.php
@@ -103,15 +103,10 @@ while ($row = mysql_fetch_array($result))
   
   // message in title for the thumbnail
   $thumbnail_title = $row['file'];
-  if (!isset($row['filesize']) or $row['filesize'] == '')
+  if (isset($row['filesize']))
   {
-    $filesize = floor(filesize($cat_directory.$row['file']) / 1024);
+    $thumbnail_title .= ' : '.$row['filesize'].' KB';
   }
-  else
-  {
-    $filesize = $row['filesize'];
-  }
-  $thumbnail_title .= ' : '.$filesize.' KB';
   // url link on picture.php page
   $url_link = PHPWG_ROOT_PATH.'picture.php?';
   if ($page['cat'] == 'random')
diff --git a/language/en_UK.iso-8859-1/admin.lang.php b/language/en_UK.iso-8859-1/admin.lang.php
index 1f1668d37..b5081e11b 100644
--- a/language/en_UK.iso-8859-1/admin.lang.php
+++ b/language/en_UK.iso-8859-1/admin.lang.php
@@ -45,9 +45,30 @@ $lang['forbidden'] = 'Forbidden';
 // Specific words
 $lang['phpinfos'] = 'PHP Information';
 $lang['remote_site'] = 'Remote site';
+$lang['remote_sites'] = 'Remote sites';
 $lang['gallery_default'] = 'Gallery Default';
 $lang['upload'] = 'Upload';
 
+// Remote sites management
+$lang['remote_site_create'] = 'Create a new site : (give its URL to generate_file_listing.php)';
+$lang['remote_site_uncorrect_url'] = 'Remote site url must start by http or https and must only contain characters among "/", "a-zA-Z0-9", "-" or "_"';
+$lang['remote_site_already_exists'] = 'This site already exists';
+$lang['remote_site_generate'] = 'generate listing';
+$lang['remote_site_generate_hint'] = 'generate file listing.xml on remote site';
+$lang['remote_site_update'] = 'update';
+$lang['remote_site_update_hint'] = 'read remote listing.xml and updates database';
+$lang['remote_site_clean'] = 'clean';
+$lang['remote_site_clean_hint'] = 'remove remote listing.xml file';
+$lang['remote_site_delete'] = 'delete';
+$lang['remote_site_delete_hint'] = 'delete this site and all its attached elements';
+$lang['remote_site_file_not_found'] = 'file create_listing_file.php on remote site was not found';
+$lang['remote_site_error'] = 'an error happened';
+$lang['remote_site_listing_not_found'] = 'remote listing file was not found';
+$lang['remote_site_removed'] = 'was removed on remote site';
+$lang['remote_site_removed_title'] = 'Removed elements';
+$lang['remote_site_created'] = 'created';
+$lang['remote_site_deleted'] = 'deleted';
+
 // Categorie words
 $lang['cat_up'] = 'Move up';
 $lang['cat_down'] = 'Move down';
diff --git a/template/default/admin.tpl b/template/default/admin.tpl
index f8493b420..5652be28f 100644
--- a/template/default/admin.tpl
+++ b/template/default/admin.tpl
@@ -17,7 +17,7 @@