Index: /trunk/admin.php
===================================================================
--- /trunk/admin.php	(revision 880)
+++ /trunk/admin.php	(revision 881)
@@ -74,4 +74,5 @@
     'U_CONFIG_DISPLAY'=>add_session_id($conf_link.'default' ),
     'U_CATEGORIES'=>add_session_id($link_start.'cat_list' ),
+    'U_MOVE'=>add_session_id($link_start.'cat_move' ),
     'U_CAT_UPLOAD'=>add_session_id($opt_link.'upload'),
     'U_CAT_COMMENTS'=>add_session_id($opt_link.'comments'),
Index: /trunk/admin/cat_modify.php
===================================================================
--- /trunk/admin/cat_modify.php	(revision 880)
+++ /trunk/admin/cat_modify.php	(revision 881)
@@ -68,5 +68,8 @@
   if (isset($_POST['parent']))
   {
-    move_category($_GET['cat_id'], $_POST['parent']);
+    move_categories(
+      array($_GET['cat_id']),
+      $_POST['parent']
+      );
   }
 
Index: /trunk/admin/cat_move.php
===================================================================
--- /trunk/admin/cat_move.php	(revision 881)
+++ /trunk/admin/cat_move.php	(revision 881)
@@ -0,0 +1,117 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | PhpWebGallery - a PHP based picture gallery                           |
+// | Copyright (C) 2002-2003 Pierrick LE GALL - pierrick@phpwebgallery.net |
+// | Copyright (C) 2003-2005 PhpWebGallery Team - http://phpwebgallery.net |
+// +-----------------------------------------------------------------------+
+// | 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');
+
+// +-----------------------------------------------------------------------+
+// |                               functions                               |
+// +-----------------------------------------------------------------------+
+
+
+// +-----------------------------------------------------------------------+
+// |                          categories movement                          |
+// +-----------------------------------------------------------------------+
+
+if (isset($_POST['submit']))
+{
+  if (count($_POST['selection']) > 0)
+  {
+    // TODO: tests
+    move_categories($_POST['selection'], $_POST['parent']);
+  }
+  else
+  {
+    array_push(
+      $page['errors'],
+      l10n('Select at least one category')
+      );
+  }
+}
+
+// +-----------------------------------------------------------------------+
+// |                       template initialization                         |
+// +-----------------------------------------------------------------------+
+
+$template->set_filenames(
+  array(
+    'cat_move' => 'admin/cat_move.tpl'
+    )
+  );
+
+$template->assign_vars(
+  array(
+    'F_ACTION' => add_session_id(PHPWG_ROOT_PATH.'admin.php?page=cat_move'),
+    )
+  );
+  
+// +-----------------------------------------------------------------------+
+// |                          Categories display                           |
+// +-----------------------------------------------------------------------+
+
+$query = '
+SELECT id,name,uppercats,global_rank
+  FROM '.CATEGORIES_TABLE.'
+  WHERE dir IS NULL
+;';
+
+display_select_cat_wrapper(
+  $query,
+  array(),
+  'category_option_selection'
+  );
+
+$blockname = 'category_option_parent';
+
+$template->assign_block_vars(
+  $blockname,
+  array(
+    'VALUE'=> 0,
+    'OPTION' => '------------'
+    )
+  );
+
+$query = '
+SELECT id,name,uppercats,global_rank
+  FROM '.CATEGORIES_TABLE.'
+;';
+
+display_select_cat_wrapper(
+  $query,
+  array(),
+  $blockname
+  );
+
+// +-----------------------------------------------------------------------+
+// |                          sending html code                            |
+// +-----------------------------------------------------------------------+
+
+$template->assign_var_from_handle('ADMIN_CONTENT', 'cat_move');
+?>
Index: /trunk/admin/include/functions.php
===================================================================
--- /trunk/admin/include/functions.php	(revision 880)
+++ /trunk/admin/include/functions.php	(revision 881)
@@ -1338,34 +1338,76 @@
 
 /**
- * change the parent category of the given category. The category is
+ * change the parent category of the given categories. The categories are
  * supposed virtual.
  *
- * @param int category identifier
+ * @param array category identifiers
  * @param int parent category identifier
  * @return void
  */
-function move_category($category_id, $new_parent = -1)
-{
-  // verifies if the move is necessary
-  $query = '
-SELECT id_uppercat, status
+function move_categories($category_ids, $new_parent = -1)
+{
+  global $page;
+
+  if (count($category_ids) == 0)
+  {
+    return;
+  }
+
+  $new_parent = $new_parent < 1 ? 'NULL' : $new_parent;
+
+  $categories = array();
+  
+  $query = '
+SELECT id, id_uppercat, status, uppercats
   FROM '.CATEGORIES_TABLE.'
-  WHERE id = '.$category_id.'
-;';
-  list($old_parent, $status) = mysql_fetch_row(pwg_query($query));
-
-  $old_parent = empty($old_parent) ? 'NULL' : $old_parent;
-  $new_parent = $new_parent < 1 ? 'NULL' : $new_parent;
-
-  if ($new_parent == $old_parent)
-  {
-    // no need to move !
-    return;
-  }
+  WHERE id IN ('.implode(',', $category_ids).')
+;';
+  $result = pwg_query($query);
+  while ($row = mysql_fetch_array($result))
+  {
+    $categories[$row['id']] =
+      array(
+        'parent' => empty($row['id_uppercat']) ? 'NULL' : $row['id_uppercat'],
+        'status' => $row['status'],
+        'uppercats' => $row['uppercats']
+        );
+  }
+  
+  // is the movement possible? The movement is impossible if you try to move
+  // a category in a sub-category or itself
+  if ('NULL' != $new_parent)
+  {
+    $query = '
+SELECT uppercats
+  FROM '.CATEGORIES_TABLE.'
+  WHERE id = '.$new_parent.'
+;';
+    list($new_parent_uppercats) = mysql_fetch_row(pwg_query($query));
+
+    foreach ($categories as $category)
+    {
+      // technically, you can't move a category with uppercats 12,125,13,14
+      // into a new parent category with uppercats 12,125,13,14,24
+      if (preg_match('/^'.$category['uppercats'].'/', $new_parent_uppercats))
+      {
+        array_push(
+          $page['errors'],
+          l10n('You cannot move a category in its own sub category')
+          );
+        return;
+      }
+    }
+  }
+  
+  $tables =
+    array(
+      USER_ACCESS_TABLE => 'user_id',
+      GROUP_ACCESS_TABLE => 'group_id'
+      );
   
   $query = '
 UPDATE '.CATEGORIES_TABLE.'
   SET id_uppercat = '.$new_parent.'
-  WHERE id = '.$category_id.'
+  WHERE id IN ('.implode(',', $category_ids).')
 ;';
   pwg_query($query);
@@ -1392,53 +1434,58 @@
   if ('private' == $parent_status)
   {
-    switch ($status)
-    {
-      case 'public' :
-      {
-        set_cat_status(array($category_id), 'private');
-        break;
-      }
-      case 'private' :
-      {
-        $subcats = get_subcat_ids(array($category_id));
-
-        $tables =
-          array(
-            USER_ACCESS_TABLE => 'user_id',
-            GROUP_ACCESS_TABLE => 'group_id'
-            );
+    foreach ($categories as $cat_id => $category)
+    {
+      switch ($category['status'])
+      {
+        case 'public' :
+        {
+          set_cat_status(array($cat_id), 'private');
+          break;
+        }
+        case 'private' :
+        {
+          $subcats = get_subcat_ids(array($cat_id));
         
-        foreach ($tables as $table => $field)
-        {
-          $query = '
+          foreach ($tables as $table => $field)
+          {
+            $query = '
 SELECT '.$field.'
   FROM '.$table.'
-  WHERE category_id = '.$category_id.'
-;';
-          $category_access = array_from_query($query, $field);
-
-          $query = '
+  WHERE cat_id = '.$cat_id.'
+;';
+            $category_access = array_from_query($query, $field);
+
+            $query = '
 SELECT '.$field.'
   FROM '.$table.'
-  WHERE category_id = '.$new_parent.'
-;';
-          $parent_access = array_from_query($query, $field);
+  WHERE cat_id = '.$new_parent.'
+;';
+            $parent_access = array_from_query($query, $field);
           
-          $to_delete = array_diff($parent_access, $category_access);
+            $to_delete = array_diff($parent_access, $category_access);
           
-          if (count($to_delete) > 0)
-          {
-            $query = '
+            if (count($to_delete) > 0)
+            {
+              $query = '
 DELETE FROM '.$table.'
   WHERE '.$field.' IN ('.implode(',', $to_delete).')
-    AND category_id IN ('.implode(',', $subcats).')
-;';
-            pwg_query($query);
+    AND cat_id IN ('.implode(',', $subcats).')
+;';
+              pwg_query($query);
+            }
           }
+          break;
         }
-        break;
-      }
-    }
-  }
+      }
+    }
+  }
+
+  array_push(
+    $page['infos'],
+    sprintf(
+      l10n('%d categories moved'),
+      count($categories)
+      )
+    );
 }
 ?>
Index: /trunk/doc/ChangeLog
===================================================================
--- /trunk/doc/ChangeLog	(revision 880)
+++ /trunk/doc/ChangeLog	(revision 881)
@@ -1,2 +1,7 @@
+2005-10-08 Pierrick LE GALL
+
+	* new: mass virtual categories movement manager in
+	Administration>Categories>Move screen.
+
 2005-10-05 Pierrick LE GALL
 
Index: /trunk/language/en_UK.iso-8859-1/admin.lang.php
===================================================================
--- /trunk/language/en_UK.iso-8859-1/admin.lang.php	(revision 880)
+++ /trunk/language/en_UK.iso-8859-1/admin.lang.php	(revision 881)
@@ -27,4 +27,5 @@
 
 $lang['%d categories including %d physical and %d virtual'] = '%d categories including %d physical and %d virtual';
+$lang['%d categories moved'] = '%d categories moved';
 $lang['%d comments'] = '%d comments';
 $lang['%d elements'] = '%d elements';
@@ -83,6 +84,8 @@
 $lang['Members'] = 'Members';
 $lang['Metadata synchronized from file'] = 'Metadata synchronized from file';
+$lang['Move categories'] = 'Move categories';
 $lang['Move'] = 'Move';
 $lang['Name'] = 'Name';
+$lang['New parent category'] = 'New parent category';
 $lang['No'] = 'No';
 $lang['Number of comments per page'] = 'Number of comments per page';
@@ -130,8 +133,11 @@
 $lang['Validate'] = 'Validate';
 $lang['Validation'] = 'Validation';
+$lang['Virtual categories movement'] = 'Virtual categories movement';
+$lang['Virtual categories to move'] = 'Virtual categories to move';
 $lang['Webmaster cannot be deleted'] = 'Webmaster cannot be deleted';
 $lang['Yes'] = 'Yes';
 $lang['You are running on development sources, no check possible.'] = 'You are running on development sources, no check possible.';
 $lang['You are running the latest version of PhpWebGallery.'] = 'You are running the latest version of PhpWebGallery.';
+$lang['You cannot move a category in its own sub category'] = 'You cannot move a category in its own sub category';
 $lang['You need to confirm deletion'] = 'You need to confirm deletion';
 $lang['actions'] = 'actions';
Index: /trunk/language/fr_FR.iso-8859-1/admin.lang.php
===================================================================
--- /trunk/language/fr_FR.iso-8859-1/admin.lang.php	(revision 880)
+++ /trunk/language/fr_FR.iso-8859-1/admin.lang.php	(revision 881)
@@ -27,4 +27,5 @@
 
 $lang['%d categories including %d physical and %d virtual'] = '%d catégories dont %d physiques et %d virtuelles';
+$lang['%d categories moved'] = '%d catégories déplacées';
 $lang['%d comments'] = '%d commentaires utilisateur';
 $lang['%d elements'] = '%d éléments';
@@ -83,6 +84,8 @@
 $lang['Members'] = 'Membres';
 $lang['Metadata synchronized from file'] = 'Meta-données synchronisées à partir du fichier';
+$lang['Move categories'] = 'Déplacer les catégories';
 $lang['Move'] = 'Déplacer';
 $lang['Name'] = 'Nom';
+$lang['New parent category'] = 'Nouvelle catégorie parente';
 $lang['No'] = 'Non';
 $lang['Number of comments per page'] = 'Nombre de commentaires utilisateur par page';
@@ -130,8 +133,11 @@
 $lang['Validate'] = 'Valider';
 $lang['Validation'] = 'Validation';
+$lang['Virtual categories movement'] = 'Déplacement de catégories virtuelles';
+$lang['Virtual categories to move'] = 'Catégories virtuelles à déplacer';
 $lang['Webmaster cannot be deleted'] = 'Le webmestre ne peut pas être supprimé';
 $lang['Yes'] = 'Oui';
 $lang['You are running on development sources, no check possible.'] = 'Vous travaillez avec les sources de développement, impossible de vérifier la dernière version.';
 $lang['You are running the latest version of PhpWebGallery.'] = 'Vous utilisez la dernière version de PhpWebGallery.';
+$lang['You cannot move a category in its own sub category'] = 'Vous ne pouvez pas déplacer une catégorie dans sa propre sous-catégorie';
 $lang['You need to confirm deletion'] = 'Vous devez confirmer la suppression';
 $lang['actions'] = 'actions';
Index: /trunk/template/yoga/admin.tpl
===================================================================
--- /trunk/template/yoga/admin.tpl	(revision 880)
+++ /trunk/template/yoga/admin.tpl	(revision 881)
@@ -34,4 +34,5 @@
       <ul>
 	<li><a href="{U_CATEGORIES}">{lang:manage}</a></li>
+	<li><a href="{U_MOVE}">{lang:Move}</a></li>
 	<li><a href="{U_CAT_UPLOAD}">{lang:upload}</a></li>
 	<li><a href="{U_CAT_COMMENTS}">{lang:comments}</a></li>
Index: /trunk/template/yoga/admin/cat_move.tpl
===================================================================
--- /trunk/template/yoga/admin/cat_move.tpl	(revision 881)
+++ /trunk/template/yoga/admin/cat_move.tpl	(revision 881)
@@ -0,0 +1,40 @@
+<!-- $Id$ -->
+<div class="titrePage">
+  <ul class="categoryActions">
+    <li><a href="{U_HELP}" onclick="popuphelp(this.href); return false;" title="{lang:Help}"><img src="template/yoga/theme/help.png" alt="(?)"></a></li>
+  </ul>
+  <h2>{lang:Move categories}</h2>
+</div>
+
+<form method="post" action="{F_ACTION}">
+  <fieldset>
+    <legend>{lang:Virtual categories movement}</legend>
+
+    <label>
+      {lang:Virtual categories to move}
+
+      <select class="categoryList" name="selection[]" multiple="multiple" size="30">
+        <!-- BEGIN category_option_selection -->
+        <option {category_option_selection.SELECTED} value="{category_option_selection.VALUE}">{category_option_selection.OPTION}</option>
+        <!-- END category_option_selection -->
+      </select>
+    </label>
+
+    <label>
+      {lang:New parent category}
+
+      <select class="categoryList" name="parent">
+        <!-- BEGIN category_option_parent -->
+        <option {category_option_parent.SELECTED} value="{category_option_parent.VALUE}">{category_option_parent.OPTION}</option>
+        <!-- END category_option_parent -->
+      </select>
+    </label>
+
+  </fieldset>
+
+  <p>
+    <input type="submit" name="submit" value="{lang:Submit}">
+    <input type="reset" name="reset" value="{lang:Reset}">
+  </p>
+
+</form>
