Index: /trunk/admin/include/functions.php
===================================================================
--- /trunk/admin/include/functions.php	(revision 8801)
+++ /trunk/admin/include/functions.php	(revision 8802)
@@ -884,15 +884,4 @@
 
 /**
- * stupidly returns the current microsecond since Unix epoch
- */
-function micro_seconds()
-{
-  $t1 = explode(' ', microtime());
-  $t2 = explode('.', $t1[0]);
-  $t2 = $t1[1].substr($t2[1], 0, 6);
-  return $t2;
-}
-
-/**
  * synchronize base users list and related users list
  *
Index: /trunk/include/category_cats.inc.php
===================================================================
--- /trunk/include/category_cats.inc.php	(revision 8801)
+++ /trunk/include/category_cats.inc.php	(revision 8802)
@@ -28,44 +28,43 @@
  */
 
-if ($page['section']=='recent_cats')
-{
-  // $user['forbidden_categories'] including with USER_CACHE_CATEGORIES_TABLE
-  $query = '
+// $user['forbidden_categories'] including with USER_CACHE_CATEGORIES_TABLE
+$query = '
 SELECT
-  c.*, nb_images, date_last, max_date_last, count_images, count_categories
-  FROM '.CATEGORIES_TABLE.' c INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.'
-  ON id = cat_id and user_id = '.$user['id'].'
-  WHERE date_last >= '.pwg_db_get_recent_period_expression($user['recent_period']).'
-'.get_sql_condition_FandF
-  (
-    array
-      (
-        'visible_categories' => 'id',
-      ),
-    'AND'
-  ).'
+    c.*,
+    user_representative_picture_id,
+    nb_images,
+    date_last,
+    max_date_last,
+    count_images,
+    count_categories
+  FROM '.CATEGORIES_TABLE.' c
+    INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.' ucc ON id = cat_id AND user_id = '.$user['id'];
+
+if ('recent_cats' == $page['section'])
+{
+  $query.= '
+  WHERE date_last >= '.pwg_db_get_recent_period_expression($user['recent_period']);
+}
+else
+{
+  $query.= '
+  WHERE id_uppercat '.(!isset($page['category']) ? 'is NULL' : '= '.$page['category']['id']);
+}
+
+$query.= get_sql_condition_FandF(
+  array(
+    'visible_categories' => 'id',
+    ),
+  'AND'
+  );
+
+if ('recent_cats' != $page['section'])
+{
+  $query.= '
+  ORDER BY rank';
+}
+
+$query.= '
 ;';
-}
-else
-{
-  // $user['forbidden_categories'] including with USER_CACHE_CATEGORIES_TABLE
-  $query = '
-SELECT
-  c.*, nb_images, date_last, max_date_last, count_images, count_categories
-  FROM '.CATEGORIES_TABLE.' c INNER JOIN '.USER_CACHE_CATEGORIES_TABLE.'
-  ON id = cat_id and user_id = '.$user['id'].'
-  WHERE id_uppercat '.
-  (!isset($page['category']) ? 'is NULL' : '= '.$page['category']['id']).'
-'.get_sql_condition_FandF
-  (
-    array
-      (
-        'visible_categories' => 'id',
-      ),
-    'AND'
-  ).'
-  ORDER BY rank
-;';
-}
 
 $result = pwg_query($query);
@@ -73,4 +72,5 @@
 $category_ids = array();
 $image_ids = array();
+$user_representative_updates_for = array();
 
 while ($row = pwg_db_fetch_assoc($result))
@@ -78,38 +78,16 @@
   $row['is_child_date_last'] = @$row['max_date_last']>@$row['date_last'];
 
-  if (isset($row['representative_picture_id'])
-      and is_numeric($row['representative_picture_id']))
+  if (!empty($row['user_representative_picture_id']))
+  {
+    $image_id = $row['user_representative_picture_id'];
+  }
+  else if (!empty($row['representative_picture_id']))
   { // if a representative picture is set, it has priority
     $image_id = $row['representative_picture_id'];
   }
   else if ($conf['allow_random_representative'])
-  {// searching a random representant among elements in sub-categories
-    if ($row['count_images']>0)
-    {
-      $query = '
-SELECT image_id
-  FROM '.CATEGORIES_TABLE.' AS c INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic
-    ON ic.category_id = c.id';
-      $query.= '
-  WHERE (c.id='.$row['id'].' OR uppercats LIKE \''.$row['uppercats'].',%\')'
-    .get_sql_condition_FandF
-    (
-      array
-        (
-          'forbidden_categories' => 'c.id',
-          'visible_categories' => 'c.id',
-          'visible_images' => 'image_id'
-        ),
-      "\n  AND"
-    ).'
-  ORDER BY '.DB_RANDOM_FUNCTION.'()
-  LIMIT 1
-;';
-      $subresult = pwg_query($query);
-      if (pwg_db_num_rows($subresult) > 0)
-      {
-        list($image_id) = pwg_db_fetch_row($subresult);
-      }
-    }
+  {
+    // searching a random representant among elements in sub-categories
+    $image_id = get_random_image_in_category($row);
   }
   else
@@ -144,4 +122,9 @@
   if (isset($image_id))
   {
+    if ($row['user_representative_picture_id'] != $image_id)
+    {
+      $user_representative_updates_for[ $user['id'].'#'.$row['id'] ] = $image_id;
+    }
+    
     $row['representative_picture_id'] = $image_id;
     array_push($image_ids, $image_id);
@@ -194,7 +177,8 @@
 {
   $thumbnail_src_of = array();
+  $new_image_ids = array();
 
   $query = '
-SELECT id, path, tn_ext
+SELECT id, path, tn_ext, level
   FROM '.IMAGES_TABLE.'
   WHERE id IN ('.implode(',', $image_ids).')
@@ -203,6 +187,86 @@
   while ($row = pwg_db_fetch_assoc($result))
   {
-    $thumbnail_src_of[$row['id']] = get_thumbnail_url($row);
-  }
+    if ($row['level'] <= $user['level'])
+    {
+      $thumbnail_src_of[$row['id']] = get_thumbnail_url($row);
+    }
+    else
+    {
+      // problem: we must not display the thumbnail of a photo which has a
+      // higher privacy level than user privacy level
+      //
+      // * what is the represented category?
+      // * find a random photo matching user permissions
+      // * register it at user_representative_picture_id
+      // * set it as the representative_picture_id for the category
+
+      foreach ($categories as &$category)
+      {
+        if ($row['id'] == $category['representative_picture_id'])
+        {
+          if ($category['count_images']>0)
+          {
+            // searching a random representant among elements in sub-categories
+            $image_id = get_random_image_in_category($category);
+
+            if (isset($image_id))
+            {
+              if (!in_array($image_id, $image_ids))
+              {
+                array_push($new_image_ids, $image_id);
+              }
+              
+              $user_representative_updates_for[ $user['id'].'#'.$category['id'] ] = $image_id;
+
+              $category['representative_picture_id'] = $image_id;
+            }
+          }
+        }
+      }
+      unset($category);
+    }
+  }
+
+  if (count($new_image_ids) > 0)
+  {
+    $query = '
+SELECT id, path, tn_ext
+  FROM '.IMAGES_TABLE.'
+  WHERE id IN ('.implode(',', $new_image_ids).')
+;';
+    $result = pwg_query($query);
+    while ($row = pwg_db_fetch_assoc($result))
+    {
+      $thumbnail_src_of[$row['id']] = get_thumbnail_url($row);
+    }
+  }
+}
+
+if (count($user_representative_updates_for))
+{
+  $updates = array();
+  
+  foreach ($user_representative_updates_for as $user_cat => $image_id)
+  {
+    list($user_id, $cat_id) = explode('#', $user_cat);
+    
+    array_push(
+      $updates,
+      array(
+        'user_id' => $user_id,
+        'cat_id' => $cat_id,
+        'user_representative_picture_id' => $image_id,
+        )
+      );
+  }
+
+  mass_updates(
+    USER_CACHE_CATEGORIES_TABLE,
+    array(
+      'primary' => array('user_id', 'cat_id'),
+      'update'  => array('user_representative_picture_id')
+      ),
+    $updates
+    );
 }
 
Index: /trunk/include/functions.inc.php
===================================================================
--- /trunk/include/functions.inc.php	(revision 8801)
+++ /trunk/include/functions.inc.php	(revision 8802)
@@ -33,4 +33,15 @@
 
 //----------------------------------------------------------- generic functions
+
+/**
+ * stupidly returns the current microsecond since Unix epoch
+ */
+function micro_seconds()
+{
+  $t1 = explode(' ', microtime());
+  $t2 = explode('.', $t1[0]);
+  $t2 = $t1[1].substr($t2[1], 0, 6);
+  return $t2;
+}
 
 // The function get_moment returns a float value coresponding to the number
Index: /trunk/include/functions_category.inc.php
===================================================================
--- /trunk/include/functions_category.inc.php	(revision 8801)
+++ /trunk/include/functions_category.inc.php	(revision 8802)
@@ -496,3 +496,41 @@
 }
 
+/**
+ * Find a random photo among all photos below a given album in the tree (not
+ * only photo directly associated to the album but also to sub-albums)
+ *
+ * we need $category['uppercats'], $category['id'], $category['count_images']
+ */
+function get_random_image_in_category($category)
+{
+  $image_id = null;
+  if ($category['count_images']>0)
+  {
+    $query = '
+SELECT image_id
+  FROM '.CATEGORIES_TABLE.' AS c
+    INNER JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON ic.category_id = c.id
+  WHERE (c.id='.$category['id'].' OR uppercats LIKE \''.$category['uppercats'].',%\')'
+    .get_sql_condition_FandF
+    (
+      array
+        (
+          'forbidden_categories' => 'c.id',
+          'visible_categories' => 'c.id',
+          'visible_images' => 'image_id',
+        ),
+      "\n  AND"
+    ).'
+  ORDER BY '.DB_RANDOM_FUNCTION.'()
+  LIMIT 1
+;';
+    $result = pwg_query($query);
+    if (pwg_db_num_rows($result) > 0)
+    {
+      list($image_id) = pwg_db_fetch_row($result);
+    }
+  }
+
+  return $image_id;
+}
 ?>
Index: /trunk/install/db/95-database.php
===================================================================
--- /trunk/install/db/95-database.php	(revision 8802)
+++ /trunk/install/db/95-database.php	(revision 8802)
@@ -0,0 +1,53 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based photo gallery                                    |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008-2011 Piwigo Team                  http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team    http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL   http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | 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!');
+}
+
+$upgrade_description = 'New colum user_cache_categories.user_representative_picture_id';
+
+// Add column
+$query = 'ALTER TABLE '.USER_CACHE_CATEGORIES_TABLE.' ADD COLUMN ';
+
+if ('mysql' == $conf['dblayer'])
+{
+  $query.= ' `user_representative_picture_id` mediumint(8) unsigned default NULL';
+}
+
+if (in_array($conf['dblayer'], array('pgsql', 'sqlite', 'pdo-sqlite')))
+{
+  $query.= ' "user_representative_picture_id" INTEGER';
+}
+
+$query.= ';';
+
+pwg_query($query);
+
+echo
+"\n"
+. $upgrade_description
+."\n"
+;
+?>
Index: /trunk/install/piwigo_structure-mysql.sql
===================================================================
--- /trunk/install/piwigo_structure-mysql.sql	(revision 8801)
+++ /trunk/install/piwigo_structure-mysql.sql	(revision 8802)
@@ -369,4 +369,5 @@
   `count_images` mediumint(8) unsigned default '0',
   `count_categories` mediumint(8) unsigned default '0',
+  `user_representative_picture_id` mediumint(8) unsigned default NULL,
   PRIMARY KEY  (`user_id`,`cat_id`)
 ) TYPE=MyISAM;
Index: /trunk/install/piwigo_structure-pdo-sqlite.sql
===================================================================
--- /trunk/install/piwigo_structure-pdo-sqlite.sql	(revision 8801)
+++ /trunk/install/piwigo_structure-pdo-sqlite.sql	(revision 8802)
@@ -426,4 +426,5 @@
   "count_images" INTEGER default 0,
   "count_categories" INTEGER default 0,
+  "user_representative_picture_id" INTEGER,
   PRIMARY KEY ("user_id","cat_id")
 );
Index: /trunk/install/piwigo_structure-pgsql.sql
===================================================================
--- /trunk/install/piwigo_structure-pgsql.sql	(revision 8801)
+++ /trunk/install/piwigo_structure-pgsql.sql	(revision 8802)
@@ -515,4 +515,5 @@
   "count_images" INTEGER default 0,
   "count_categories" INTEGER default 0,
+  "user_representative_picture_id" INTEGER,
   PRIMARY KEY ("user_id","cat_id")
 );
Index: /trunk/install/piwigo_structure-sqlite.sql
===================================================================
--- /trunk/install/piwigo_structure-sqlite.sql	(revision 8801)
+++ /trunk/install/piwigo_structure-sqlite.sql	(revision 8802)
@@ -426,4 +426,5 @@
   "count_images" INTEGER default 0,
   "count_categories" INTEGER default 0,
+  "user_representative_picture_id" INTEGER,
   PRIMARY KEY ("user_id","cat_id")
 );
@@ -536,2 +537,3 @@
 CREATE INDEX "comments_i2" ON "piwigo_comments" ("validation_date");
 CREATE INDEX "comments_i1" ON "piwigo_comments" ("image_id");
+
Index: /trunk/picture.php
===================================================================
--- /trunk/picture.php	(revision 8801)
+++ /trunk/picture.php	(revision 8802)
@@ -285,4 +285,12 @@
 ;';
         pwg_query($query);
+
+        $query = '
+UPDATE '.USER_CACHE_CATEGORIES_TABLE.'
+  SET user_representative_picture_id = NULL
+  WHERE user_id = '.$user['id'].'
+    AND cat_id = '.$page['category']['id'].'
+;';
+        pwg_query($query);
       }
 
