Index: extensions/Icy_Picture_Modify/CHANGELOG
===================================================================
--- extensions/Icy_Picture_Modify/CHANGELOG	(revision 11637)
+++ extensions/Icy_Picture_Modify/CHANGELOG	(revision 11934)
@@ -1,2 +1,15 @@
+1.0.3 2011-08-12
+
+  - Compatible with Piwigo 2.2.3, Piwigo 2.2.4 and the lastest version
+    of the plugin 'community' (*)
+  - Allow user to make links for image (image may be associated to or
+    dissociate from any uploadable album). See KNOWN PROBLEMS in the file
+    README for more details.
+  - Allow user to make represent for uploadable albums. See KNOWN PROBLEMS
+    in the file README for more details.
+  - Clean up code
+
+  (*) http://piwigo.org/ext/extension_view.php?eid=303
+
 1.0.2 2011-07-04
 
Index: extensions/Icy_Picture_Modify/README
===================================================================
--- extensions/Icy_Picture_Modify/README	(revision 11637)
+++ extensions/Icy_Picture_Modify/README	(revision 11934)
@@ -1,6 +1,8 @@
-Name
-  Icy Picture Modify
+NAME
 
-Description
+  icy_picture_modify -- Allow user to modify their pictures
+
+DESCRIPTION
+
   A piwigo extension that allows users to modify pictures they uploaded.
   This extension just works as the 'picture_modify.php' for administrator.
@@ -9,16 +11,5 @@
   'picture_modify.php' of the Piwigo distribution (version 2.2.3.)
 
-Author
-  icy
-
-License
-  GPL2
-
-Homepage
-  At Github.com:  https://github.com/icy/icy_picture_modify
-  At Piwigo.com:  http://piwigo.org/ext/extension_view.php?eid=563
-  SVN repository: http://piwigo.org/svn/extensions/Icy_Picture_Modify/
-
-Usage
+USAGE
 
   1. Install and enable the plugin "community" which allows users to upload pictures
@@ -27,19 +18,37 @@
      to modify that picture's information.
 
-Features
+FEATURES
 
   Users can modify the following information of a picture
-    1. Author
-    2. Description
-    3. Picture's date
-    4. Tags
-    5. ACL
-    6. Synchronize meta data
-    7. Delete picture
+    * Author
+    * Description
+    * Picture's date
+    * Tags
+    * ACL
+    * Synchronize meta data
+    * Delete picture
+    * Associcate/Dissociated image to/from uploadable category
+    * Use image to represent for uploadable category
 
-Known problems
+KNOWN PROBLEMS
 
-  1. Doesn't support all known templates
-  2. The source code isn't cleaned up for some hidden features:
-      - Link picture to some albums
-      - Use picture as some albums' thumbnail
+  * This plugin doesn't support all known templates
+  * This plugin may not work smoothly without plugin 'community'
+  * User can delete an image which is associated to some albums to which
+    the user doesn't have permission to write/access. When the plugin
+    'community' isn't installed, user can create image's link and/or
+    represents for any visible albums in the gallery.
+
+AUTHOR
+
+  Anh K. Huỳnh (icy)
+
+LICENSE
+
+  GPL2
+
+HOMGEPAGE
+
+  At Github.com:  https://github.com/icy/icy_picture_modify
+  At Piwigo.com:   http://piwigo.org/ext/extension_view.php?eid=563
+  SVN repository:  http://piwigo.org/svn/extensions/Icy_Picture_Modify/
Index: extensions/Icy_Picture_Modify/icy_picture_modify.php
===================================================================
--- extensions/Icy_Picture_Modify/icy_picture_modify.php	(revision 11637)
+++ extensions/Icy_Picture_Modify/icy_picture_modify.php	(revision 11934)
@@ -58,7 +58,4 @@
 
 // Simplify redirect to administrator page if current user == admin
-// FIXME: when a non-existent image_id is provided, the original code
-// FIXME: picture_modify doesn't work well. It should deny to modify
-// FIXME: such picture.
 if (is_admin())
 {
@@ -68,4 +65,5 @@
     $url.= '&amp;image_id='.$_GET['image_id'];
     $url.= isset($_GET['cat_id']) ? '&amp;cat_id='.$_GET['cat_id'] : '';
+    // FIXME: What happens if a POST data were sent within admin uid?
     redirect_http($url);
   }
@@ -94,4 +92,38 @@
 }
 
+// <find writable categories>
+
+// * Purpose: Find all categories that are reachable for the current user.
+// * FIXME:   This query will include all readable categories, those ones
+//            use can't write to them.
+
+$my_categories = array();
+$my_permissions = null;
+
+// <community support>
+if (is_file(PHPWG_PLUGINS_PATH.'community/include/functions_community.inc.php'))
+{
+  include_once(PHPWG_PLUGINS_PATH.'community/include/functions_community.inc.php');
+  $user_permissions = community_get_user_permissions($user['id']);
+  $my_categories = $user_permissions['upload_categories'];
+}
+// </community support>
+
+// FIXME: what happens if both of the following conditions are true
+// FIXME:    * true == $user_permissions['create_whole_gallery']
+// FIXME:    * 0    <  count($my_categories)
+if (empty($user_permissions) or $user_permissions['create_whole_gallery'])
+{
+  $query = '
+  SELECT category_id
+    FROM '.IMAGE_CATEGORY_TABLE.'
+  ;';
+
+  // list of categories to which the user can access
+  $my_categories = array_diff(
+    array_from_query($query, 'category_id'),
+    explode(',',calculate_permissions($user['id'], $user['status'])));
+}
+// </find writable categories>
 
 // +-----------------------------------------------------------------------+
@@ -128,8 +160,6 @@
 ;';
 
-  $authorizeds = array_diff(
-    array_from_query($query, 'category_id'),
-    explode(',', calculate_permissions($user['id'], $user['status']))
-    );
+  $authorizeds = array_intersect($my_categories,
+    array_from_query($query, 'category_id'));
 
   foreach ($authorizeds as $category_id)
@@ -164,5 +194,7 @@
 }
 
-//--------------------------------------------------------- update informations
+// +-----------------------------------------------------------------------+
+// |                          update informations                          |
+// +-----------------------------------------------------------------------+
 
 // first, we verify whether there is a mistake on the given creation date
@@ -231,5 +263,10 @@
   array_push($page['infos'], l10n('Photo informations updated'));
 }
+
+// +-----------------------------------------------------------------------+
+// |                              associate                                |
+// +-----------------------------------------------------------------------+
 // associate the element to other categories than its storage category
+//
 if (isset($_POST['associate'])
     and isset($_POST['cat_dissociated'])
@@ -239,7 +276,9 @@
   associate_images_to_categories(
     array($_GET['image_id']),
-    $_POST['cat_dissociated']
+    array_intersect($_POST['cat_dissociated'], $my_categories)
     );
 }
+
+
 // dissociate the element from categories (but not from its storage category)
 if (isset($_POST['dissociate'])
@@ -248,14 +287,15 @@
   )
 {
+  $arr_dissociate = array_intersect($_POST['cat_associated'], $my_categories);
   $query = '
 DELETE FROM '.IMAGE_CATEGORY_TABLE.'
   WHERE image_id = '.$_GET['image_id'].'
-    AND category_id IN ('.implode(',', $_POST['cat_associated']).')
+    AND category_id IN ('.implode(',', $arr_dissociate).')
 ';
   pwg_query($query);
 
-  update_category($_POST['cat_associated']);
-}
-// elect the element to represent the given categories
+  update_category($arr_dissociate);
+}
+// select the element to represent the given categories
 if (isset($_POST['elect'])
     and isset($_POST['cat_dismissed'])
@@ -264,13 +304,17 @@
 {
   $datas = array();
-  foreach ($_POST['cat_dismissed'] as $category_id)
-  {
-    array_push($datas,
-               array('id' => $category_id,
-                     'representative_picture_id' => $_GET['image_id']));
-  }
-  $fields = array('primary' => array('id'),
-                  'update' => array('representative_picture_id'));
-  mass_updates(CATEGORIES_TABLE, $fields, $datas);
+  $arr_dimissed = array_intersect($_POST['cat_dismissed'], $my_categories);
+  if (count($arr_dimissed) > 0)
+  {
+    foreach ($arr_dimissed as $category_id)
+    {
+      array_push($datas,
+                 array('id' => $category_id,
+                       'representative_picture_id' => $_GET['image_id']));
+    }
+    $fields = array('primary' => array('id'),
+                    'update' => array('representative_picture_id'));
+    mass_updates(CATEGORIES_TABLE, $fields, $datas);
+  }
 }
 // dismiss the element as representant of the given categories
@@ -280,5 +324,9 @@
   )
 {
-  set_random_representant($_POST['cat_elected']);
+  $arr_dismiss = array_intersect($_POST['cat_elected'], $my_categories);
+  if (count($arr_dismiss) > 0)
+  {
+    set_random_representant($arr_dismiss);
+  }
 }
 
@@ -310,4 +358,5 @@
 $row = pwg_db_fetch_assoc(pwg_query($query));
 
+// the physical storage directory contains the image
 $storage_category_id = null;
 if (!empty($row['storage_category_id']))
@@ -463,12 +512,10 @@
 ;';
 
-$authorizeds = array_diff(
-  array_from_query($query, 'category_id'),
-  explode(
-    ',',
-    calculate_permissions($user['id'], $user['status'])
-    )
-  );
-
+// list of categories (OF THIS IMAGE) to which the user can access
+$authorizeds = array_intersect($my_categories,
+  array_from_query($query, 'category_id'));
+
+// if current category belongs to list of authorized categories
+// we simply provide link to that category
 if (isset($_GET['cat_id'])
     and in_array($_GET['cat_id'], $authorizeds))
@@ -482,4 +529,5 @@
     );
 }
+// otherwise we provide links to the *first* category in the list
 else
 {
@@ -493,4 +541,5 @@
         )
       );
+    // FIXME: why the first category is selected?
     break;
   }
@@ -507,5 +556,8 @@
   FROM '.CATEGORIES_TABLE.'
     INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id = category_id
-  WHERE image_id = '.$_GET['image_id'];
+  WHERE image_id = '.$_GET['image_id'] . '
+    AND id IN ('. join(",", $my_categories).')';
+// if the image belongs to a physical storage,
+// we simply ignore that storage album
 if (isset($storage_category_id))
 {
@@ -531,4 +583,5 @@
   FROM '.CATEGORIES_TABLE.'
   WHERE id NOT IN ('.implode(',', $associateds).')
+  AND id IN ('. join(",", $my_categories).')
 ;';
 display_select_cat_wrapper($query, array(), 'dissociated_options');
@@ -539,4 +592,5 @@
   FROM '.CATEGORIES_TABLE.'
   WHERE representative_picture_id = '.$_GET['image_id'].'
+    AND id IN ('. join(",", $my_categories).')
 ;';
 display_select_cat_wrapper($query, array(), 'elected_options');
@@ -545,6 +599,7 @@
 SELECT id,name,uppercats,global_rank
   FROM '.CATEGORIES_TABLE.'
-  WHERE representative_picture_id != '.$_GET['image_id'].'
-    OR representative_picture_id IS NULL
+  WHERE id IN ('. join(",", $my_categories).')
+    AND (representative_picture_id != '.$_GET['image_id'].'
+    OR representative_picture_id IS NULL)
 ;';
 display_select_cat_wrapper($query, array(), 'dismissed_options');
Index: extensions/Icy_Picture_Modify/main.inc.php
===================================================================
--- extensions/Icy_Picture_Modify/main.inc.php	(revision 11637)
+++ extensions/Icy_Picture_Modify/main.inc.php	(revision 11934)
@@ -2,5 +2,5 @@
 /*
 Plugin Name: Icy Modify Picture
-Version: 1.0.2
+Version: 1.0.3
 Description: Allow users to modify pictures they uploaded
 Plugin URI: http://piwigo.org/ext/extension_view.php?eid=563
Index: extensions/Icy_Picture_Modify/template/icy_picture_modify.tpl
===================================================================
--- extensions/Icy_Picture_Modify/template/icy_picture_modify.tpl	(revision 11637)
+++ extensions/Icy_Picture_Modify/template/icy_picture_modify.tpl	(revision 11934)
@@ -12,4 +12,5 @@
 {combine_css path= 'themes/default/js/ui/theme/'|@cat:'jquery.ui.datepicker.css'}
 {combine_css path= "$ICY_PICTURE_MODIFY_PATH/template/"|@cat:'datepicker.css'}
+{combine_css path= "$ICY_PICTURE_MODIFY_PATH/template/"|@cat:'ui-resizable.css'}
 
 {* Heavily copied from Piwigo distribution: picture_modify.tpl *}
@@ -183,2 +184,56 @@
 
 </form>
+
+<form id="associations" method="post" action="{$F_ACTION}#associations">
+  <fieldset>
+    <legend>{'Linked albums'|@translate}</legend>
+
+    <table class="doubleSelect">
+      <tr>
+        <td>
+          <h3>{'Associated'|@translate}</h3>
+          <select class="categoryList" name="cat_associated[]" multiple="multiple" size="30">
+            {html_options options=$associated_options}
+          </select>
+          <p><input class="submit" type="submit" value="&raquo;" name="dissociate" style="font-size:15px;"></p>
+        </td>
+
+        <td>
+          <h3>{'Dissociated'|@translate}</h3>
+          <select class="categoryList" name="cat_dissociated[]" multiple="multiple" size="30">
+            {html_options options=$dissociated_options}
+          </select>
+          <p><input class="submit" type="submit" value="&laquo;" name="associate" style="font-size:15px;"></p>
+        </td>
+      </tr>
+    </table>
+
+  </fieldset>
+</form>
+
+<form id="representation" method="post" action="{$F_ACTION}#representation">
+  <fieldset>
+    <legend>{'Representation of albums'|@translate}</legend>
+
+    <table class="doubleSelect">
+      <tr>
+        <td>
+          <h3>{'Represents'|@translate}</h3>
+          <select class="categoryList" name="cat_elected[]" multiple="multiple" size="30">
+            {html_options options=$elected_options}
+          </select>
+          <p><input class="submit" type="submit" value="&raquo;" name="dismiss" style="font-size:15px;"></p>
+        </td>
+
+        <td>
+          <h3>{'Does not represent'|@translate}</h3>
+          <select class="categoryList" name="cat_dismissed[]" multiple="multiple" size="30">
+            {html_options options=$dismissed_options}
+          </select>
+          <p><input class="submit" type="submit" value="&laquo;" name="elect" style="font-size:15px;"></p>
+        </td>
+      </tr>
+    </table>
+
+  </fieldset>
+</form>
Index: extensions/Icy_Picture_Modify/template/ui-resizable.css
===================================================================
--- extensions/Icy_Picture_Modify/template/ui-resizable.css	(revision 11934)
+++ extensions/Icy_Picture_Modify/template/ui-resizable.css	(revision 11934)
@@ -0,0 +1,16 @@
+/* jQuery ui resizable */
+.ui-resizable { position: relative; }
+.ui-wrapper { border: 0; }
+.ui-wrapper input, .ui-wrapper textarea { border: 0; }
+.ui-resizable-handle { position: absolute; display: none; font-size: 0.1px; }
+.ui-resizable .ui-resizable-handle { display: block; }
+body .ui-resizable-disabled .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
+body .ui-resizable-autohide .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
+.ui-resizable-n { cursor: n-resize; height: 6px; width: 100%; top: 0px; left: 0px; background: transparent url(icon/resizable-n.gif) no-repeat scroll center top; }
+.ui-resizable-s { cursor: s-resize; height: 6px; width: 100%; bottom: 0px; left: 0px; background: transparent url(icon/resizable-s.gif) no-repeat scroll center top; }
+.ui-resizable-e { cursor: e-resize; width: 6px; right: 0px; top: 0px; height: 100%; background: transparent url(icon/resizable-e.gif) no-repeat scroll right center; }
+.ui-resizable-w { cursor: w-resize; width: 6px; left: 0px; top: 0px; height: 100%; background: transparent url(icon/resizable-w.gif) no-repeat scroll right center; }
+.ui-resizable-se { cursor: se-resize; width: 9px; height: 9px; right: 0px; bottom: 0px; background: transparent url(icon/resizable-se.gif); }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: 0px; bottom: 0px; background: transparent url(icon/resizable-sw.gif); }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: 0px; top: 0px; background: transparent url(icon/resizable-nw.gif); }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: 0px; top: 0px; background: transparent url(icon/resizable-ne.gif); }
