Index: extensions/Comments_on_Albums/changelog.txt
===================================================================
--- extensions/Comments_on_Albums/changelog.txt	(revision 9624)
+++ extensions/Comments_on_Albums/changelog.txt	(revision 9624)
@@ -0,0 +1,5 @@
+0.1.a
+- bug corrected on coa_admin_comments.php
+
+0.1
+- Test version
Index: extensions/Comments_on_Albums/include/coa_admin_comments.php
===================================================================
--- extensions/Comments_on_Albums/include/coa_admin_comments.php	(revision 9624)
+++ extensions/Comments_on_Albums/include/coa_admin_comments.php	(revision 9624)
@@ -0,0 +1,137 @@
+<?php
+/* Code adapted from admin/comments.php */
+if (!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
+
+// +-----------------------------------------------------------------------+
+//				Tabsheet
+// +-----------------------------------------------------------------------+
+include_once(PHPWG_ROOT_PATH.'admin/include/tabsheet.class.php');
+
+$tabs = array(
+	array(
+		'code' => 'pictures',
+		'label' => l10n('Photos'),
+	),
+	array(
+		'code' => 'albums',
+		'label' => l10n('Albums'),
+	),
+);
+
+$tab_codes = array_map(create_function('$a', 'return $a["code"];'), $tabs);
+
+if (isset($_GET['section']) and in_array($_GET['section'], $tab_codes)) {
+	$page['tab'] = $_GET['section'];
+} else {
+	$page['tab'] = $tabs[0]['code'];
+}
+
+$tabsheet = new tabsheet();
+foreach ($tabs as $tab) {
+	$tabsheet->add(
+		$tab['code'],
+		$tab['label'],
+		get_root_url().'admin.php?page=comments&amp;section='.$tab['code']
+	);
+}
+$tabsheet->select($page['tab']);
+$tabsheet->assign();
+
+
+if ($page['tab'] == 'albums') {
+	$template->clear_assign(array('ADMIN_CONTENT', 'comments', 'LIST', 'F_ACTION'));
+
+	// +-----------------------------------------------------------------------+
+	//				Actions
+	// +-----------------------------------------------------------------------+
+	if (!empty($_POST)) {
+		if (!empty($_POST['comments'])) {
+			check_input_parameter('comments', $_POST, true, PATTERN_ID);
+
+			if (isset($_POST['validate_albums'])) {
+				$query = 'UPDATE '.COA_TABLE.' SET 
+						validated = \'true\',
+						validation_date = NOW()
+					WHERE id IN ('.implode(',', $_POST['comments']).')
+				;';
+				pwg_query($query);
+
+				array_push($page['infos'], l10n_dec(
+					'%d user comment validated', '%d user comments validated',
+					count($_POST['comments'])
+				));
+			}
+
+			if (isset($_POST['reject_albums'])) {
+				$query = 'DELETE FROM '.COA_TABLE.'
+					WHERE id IN ('.implode(',', $_POST['comments']).')
+				;';
+				pwg_query($query);
+
+				array_push($page['infos'], l10n_dec(
+					'%d user comment rejected', '%d user comments rejected',
+					count($_POST['comments'])
+				));
+			}
+		}
+	}
+
+	// +-----------------------------------------------------------------------+
+	//				Infos (re-assignation needed)
+	// +-----------------------------------------------------------------------+
+	if (count($page['infos']) != 0) {
+		$template->assign('infos', $page['infos']);
+	}
+
+	// +-----------------------------------------------------------------------+
+	//				Comments display
+	// +-----------------------------------------------------------------------+
+	$list = array();
+
+	$query = 'SELECT 
+			c.id, 
+			c.category_id, 
+			c.date, 
+			c.author,
+			'.$conf['user_fields']['username'].' AS username, 
+			c.content, 
+			i.name
+		FROM '.COA_TABLE.' AS c
+		INNER JOIN '.CATEGORIES_TABLE.' AS i
+		ON i.id = c.category_id
+		LEFT JOIN '.USERS_TABLE.' AS u
+		ON u.'.$conf['user_fields']['id'].' = c.author_id
+		WHERE validated = \'false\'
+		ORDER BY c.date DESC
+	;';
+	$result = pwg_query($query);
+
+	while ($row = pwg_db_fetch_assoc($result)) {
+		if (empty($row['author_id'])) {
+			$author_name = $row['author'];
+		} else {
+			$author_name = stripslashes($row['username']);
+		}
+		
+		$template->append('comments', array(
+			'CAT_URL' => PHPWG_ROOT_PATH.'admin.php?page=cat_modify&amp;cat_id='.$row['category_id'],
+			'CAT_NAME' => $row['name'],
+			'ID' => $row['id'],
+			'AUTHOR' => trigger_event('render_comment_author', $author_name),
+			'DATE' => format_date($row['date'], true),
+			'CONTENT' => trigger_event('render_comment_content',$row['content']),
+		));
+
+		array_push($list, $row['id']);
+	}
+
+	// template
+	$template->set_filename('comments', dirname(__FILE__) .'/../template/coa_admin.tpl');
+	$template->assign(array(
+		'F_ACTION' => get_root_url().'admin.php?page=comments&amp;section=albums',
+		'LIST' => implode(',', $list),
+	));
+	$template->assign_var_from_handle('ADMIN_CONTENT', 'comments');
+}
+
+?>
Index: extensions/Comments_on_Albums/include/coa_admin_intro.php
===================================================================
--- extensions/Comments_on_Albums/include/coa_admin_intro.php	(revision 9624)
+++ extensions/Comments_on_Albums/include/coa_admin_intro.php	(revision 9624)
@@ -0,0 +1,33 @@
+<?php
+/* adds info about comments count on main admin page, uses javascript */
+if (!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
+
+// comments count
+$query = '
+SELECT COUNT(*)
+	FROM '.COA_TABLE.'
+;';
+list($nb_comments) = pwg_db_fetch_row(pwg_query($query));
+
+$template->assign('NB_COMMENTS', l10n_dec('%d comment', '%d comments', $nb_comments) .' ('. l10n('Albums') .')');
+
+// unvalidated comments
+$query = '
+SELECT COUNT(*)
+	FROM '.COA_TABLE.'
+	WHERE validated=\'false\'
+;';
+list($nb_comments) = pwg_db_fetch_row(pwg_query($query));
+
+if ($nb_comments > 0) {
+	$template->assign('unvalidated_albums', array(
+		'URL' => PHPWG_ROOT_PATH.'admin.php?page=comments&amp;section=albums',
+		'INFO' => sprintf(l10n('%d waiting for validation'), $nb_comments)
+	));
+}
+
+// template
+$template->set_filename('comments_on_albums', dirname(__FILE__) .'/../template/coa_intro.tpl');
+$template->concat('ADMIN_CONTENT', $template->parse('comments_on_albums', true));
+
+?>
Index: extensions/Comments_on_Albums/include/coa_albums.php
===================================================================
--- extensions/Comments_on_Albums/include/coa_albums.php	(revision 9624)
+++ extensions/Comments_on_Albums/include/coa_albums.php	(revision 9624)
@@ -0,0 +1,263 @@
+<?php
+/* Code adapted from include/picture_comment.inc.php and picture.php */
+if (!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
+
+// +-----------------------------------------------------------------------+
+//				Category's infos
+// +-----------------------------------------------------------------------+
+$category = $page['category'];
+
+$url_self = duplicate_index_url(array(
+	'category' => array(
+		'id'=>$category['id'], 
+		'name'=>$category['name'], 
+		'permalink'=>$category['permalink']
+	), 
+	array('start')
+));
+
+
+// +-----------------------------------------------------------------------+
+//				Actions
+// +-----------------------------------------------------------------------+
+if (isset($_GET['action'])) {
+	switch ($_GET['action']) {
+		case 'edit_comment' : {
+			check_pwg_token();
+			include_once(COA_PATH.'include/functions_comment.inc.php'); // custom fonctions
+			check_input_parameter('comment_to_edit', $_GET, false, PATTERN_ID);
+			$author_id = get_comment_author_id($_GET['comment_to_edit']);
+
+			if (can_manage_comment('edit', $author_id)) {
+				if (!empty($_POST['content'])) {
+					update_user_comment(array(
+						'comment_id' => $_GET['comment_to_edit'],
+						'image_id' => $category['id'],
+						'content' => $_POST['content']
+					), $_POST['key']);
+
+					redirect($url_self);
+				} else {
+					$edit_comment = $_GET['comment_to_edit'];
+					break;
+				}
+			}
+		}
+		
+		case 'delete_comment' : {
+			check_pwg_token();
+			include_once(COA_PATH.'include/functions_comment.inc.php');
+			check_input_parameter('comment_to_delete', $_GET, false, PATTERN_ID);
+			$author_id = get_comment_author_id($_GET['comment_to_delete']);
+
+			if (can_manage_comment('delete', $author_id)) {
+				delete_user_comment($_GET['comment_to_delete']);
+			}
+
+			redirect($url_self);
+		}
+		
+		case 'validate_comment' : {
+			check_pwg_token();
+			include_once(COA_PATH.'include/functions_comment.inc.php');
+			check_input_parameter('comment_to_validate', $_GET, false, PATTERN_ID);
+			$author_id = get_comment_author_id($_GET['comment_to_validate']);
+
+			if (can_manage_comment('validate', $author_id)) {
+				validate_user_comment($_GET['comment_to_validate']);
+			}
+
+			redirect($url_self);
+		}
+	}
+}
+
+
+// +-----------------------------------------------------------------------+
+//				Insert comment
+// +-----------------------------------------------------------------------+
+if ($category['commentable'] and isset($_POST['content'])) {
+	if (is_a_guest() and !$conf['comments_forall']) {
+		die('Session expired');
+	}
+
+	$comm = array(
+		'author' => trim( @$_POST['author'] ),
+		'content' => trim( $_POST['content'] ),
+		'image_id' => $category['id'],
+	);
+
+	include_once(COA_PATH.'include/functions_comment.inc.php');
+	$comment_action = insert_user_comment($comm, @$_POST['key'], $infos);
+
+	switch ($comment_action) {
+		case 'moderate':
+			array_push($infos, l10n('An administrator must authorize your comment before it is visible.'));
+		case 'validate':
+			array_push($infos, l10n('Your comment has been registered'));
+			break;
+		case 'reject':
+			set_status_header(403);
+			array_push($infos, l10n('Your comment has NOT been registered because it did not pass the validation rules'));
+			break;
+		default:
+			trigger_error('Invalid comment action '.$comment_action, E_USER_WARNING);
+	}
+
+	$template->assign(($comment_action=='reject') ? 'errors' : 'infos', $infos);
+	trigger_action('user_comment_insertion', array_merge($comm, array('action'=>$comment_action)));
+	
+} elseif (isset($_POST['content'])) {
+	set_status_header(403);
+	die('ugly spammer');
+}
+
+
+// +-----------------------------------------------------------------------+
+//				Display comments
+// +-----------------------------------------------------------------------+
+if ($category['commentable']) {
+	if (!is_admin()) {
+		$validated_clause = " AND validated = 'true'";
+	} else {
+		$validated_clause = null;
+	}
+
+	// number of comments for this picture
+	$query = 'SELECT COUNT(*) AS nb_comments
+		FROM '.COA_TABLE.'
+		WHERE category_id = '.$category['id']
+		.$validated_clause.'
+	;';
+	$row = pwg_db_fetch_assoc(pwg_query($query));
+
+	// navigation bar creation, custom again
+	if (isset($_GET['start_comments'])) {
+		$page['start_comments'] = $_GET['start_comments'];
+	} else {
+		$page['start_comments'] = 0;
+	}
+	include_once(COA_PATH.'include/functions.inc.php');
+
+	$navigation_bar = create_comment_navigation_bar(
+		duplicate_index_url(array(), array('start')),
+		$row['nb_comments'],
+		$page['start_comments'],
+		$conf['nb_comment_page']
+	);
+
+	$template->assign(array(
+		'COMMENT_COUNT' => $row['nb_comments'],
+		'comment_navbar' => $navigation_bar,
+	));
+
+	if ($row['nb_comments'] > 0) {
+		// get comments
+		$query = 'SELECT
+				com.id,
+				author,
+				author_id,
+				'.$conf['user_fields']['username'].' AS username,
+				date,
+				category_id,
+				content,
+				validated
+			FROM '.COA_TABLE.' AS com
+			LEFT JOIN '.USERS_TABLE.' AS u
+			ON u.'.$conf['user_fields']['id'].' = author_id
+			WHERE category_id = '.$category['id'].'
+			'.$validated_clause.'
+			ORDER BY date ASC
+			LIMIT '.$conf['nb_comment_page'].' OFFSET '.$page['start_comments'].'
+		;';
+		$result = pwg_query($query);
+
+		while ($row = pwg_db_fetch_assoc($result)) {
+			// author
+			if (!empty($row['author'])) {
+				$author = $row['author'];
+				if ($author == 'guest') {
+					$author = l10n('guest');
+				}
+			} else {
+				$author = stripslashes($row['username']);
+			}
+			
+			// comment content
+			$tpl_comment = array(
+				'AUTHOR' => trigger_event('render_comment_author', $author),
+				'DATE' => format_date($row['date'], true),
+				'CONTENT' => trigger_event('render_comment_content', $row['content']),
+			);
+			
+			// rights
+			if (can_manage_comment('delete', $row['author_id'])) {
+				$tpl_comment['U_DELETE'] = add_url_params($url_self, array(
+					'action' => 'delete_comment',
+					'comment_to_delete' => $row['id'],
+					'pwg_token' => get_pwg_token(),
+				));
+			}
+			if (can_manage_comment('edit', $row['author_id'])) {
+				$tpl_comment['U_EDIT'] = add_url_params($url_self, array(
+					'action' => 'edit_comment',
+					'comment_to_edit' => $row['id'],
+					'pwg_token' => get_pwg_token(),
+				));
+				if (isset($edit_comment) and ($row['id'] == $edit_comment)) {
+					$key = get_ephemeral_key(2, $category['id']);
+					$tpl_comment['IN_EDIT'] = true;
+					$tpl_comment['KEY'] = $key;
+					$tpl_comment['CONTENT'] = $row['content'];
+				}
+			}
+			if (is_admin() AND $row['validated'] != 'true') {
+				$tpl_comment['U_VALIDATE'] = add_url_params($url_self, array(
+					'action' => 'validate_comment',
+					'comment_to_validate' => $row['id'],
+					'pwg_token' => get_pwg_token(),
+				));
+			}
+			
+			// template
+			$template->append('comments', $tpl_comment);
+		}
+	}
+
+	// comment form
+	$show_add_comment_form = true;
+	if (isset($edit_comment)) {
+		$show_add_comment_form = false;
+	}
+	if (is_a_guest() and !$conf['comments_forall']) {
+		$show_add_comment_form = false;
+	}
+
+	if ($show_add_comment_form) {
+		$key = get_ephemeral_key(3, $category['id']);
+		$content = null;
+		if ('reject'===@$comment_action) {
+			$content = htmlspecialchars(stripslashes($comm['content']));
+		}
+		$template->assign('comment_add', array(
+			'F_ACTION' => $url_self,
+			'KEY' => $key,
+			'CONTENT' => $content,
+			'SHOW_AUTHOR' => !is_classic_user(),
+		));
+	}
+	
+	// template
+	$template->assign(array(
+		'COA_PATH' => COA_PATH, // for css
+		'COA_ABSOLUTE_PATH' => dirname(__FILE__) .'/../', // for template
+	));
+	
+	$template->set_filename('comments_on_albums', dirname(__FILE__) .'/../template/coa_albums.tpl');
+	$template->concat('PLUGIN_INDEX_CONTENT_END', $template->parse('comments_on_albums', true));
+	
+	$template->set_filename('comments_on_albums_messages', dirname(__FILE__) .'/../template/coa_messages.tpl');
+	$template->concat('PLUGIN_INDEX_CONTENT_BEFORE', $template->parse('comments_on_albums_messages', true));
+}
+
+?>
Index: extensions/Comments_on_Albums/include/functions.inc.php
===================================================================
--- extensions/Comments_on_Albums/include/functions.inc.php	(revision 9624)
+++ extensions/Comments_on_Albums/include/functions.inc.php	(revision 9624)
@@ -0,0 +1,59 @@
+<?php
+/* This is from include/functions.inc.php but adapted for Comments On Albums */
+
+/**
+ * return an array which will be sent to template to display navigation bar
+ */
+function create_comment_navigation_bar($url, $nb_element, $start, $nb_element_page)
+{
+  global $conf;
+
+  $navbar = array();
+  $pages_around = $conf['paginate_pages_around'];
+  $start_str = (strpos($url, '?')===false ? '?':'&amp;').'start_comments=';
+
+  if (!isset($start) or !is_numeric($start) or (is_numeric($start) and $start < 0))
+  {
+    $start = 0;
+  }
+
+  // navigation bar useful only if more than one page to display !
+  if ($nb_element > $nb_element_page)
+  {
+    $cur_page = ceil($start / $nb_element_page) + 1;
+    $maximum = ceil($nb_element / $nb_element_page);
+    $previous = $start - $nb_element_page;
+    $next = $start + $nb_element_page;
+    $last = ($maximum - 1) * $nb_element_page;
+
+    $navbar['CURRENT_PAGE'] = $cur_page;
+
+    // link to first page and previous page?
+    if ($cur_page != 1)
+    {
+      $navbar['URL_FIRST'] = $url;
+      $navbar['URL_PREV'] = $url.($previous > 0 ? $start_str.$previous : '');
+    }
+    // link on next page and last page?
+    if ($cur_page != $maximum)
+    {
+      $navbar['URL_NEXT'] = $url.$start_str.$next;
+      $navbar['URL_LAST'] = $url.$start_str.$last;
+    }
+
+    // pages to display
+    $navbar['pages'] = array();
+    $navbar['pages'][1] = $url;
+    $navbar['pages'][$maximum] = $url.$start_str.$last;
+
+    for ($i = max($cur_page - $pages_around , 2), $stop = min($cur_page + $pages_around + 1, $maximum);
+         $i < $stop; $i++)
+    {
+      $navbar['pages'][$i] = $url.$start_str.(($i - 1) * $nb_element_page);
+    }
+    ksort($navbar['pages']);
+  }
+  return $navbar;
+}
+
+?>
Index: extensions/Comments_on_Albums/include/functions_comment.inc.php
===================================================================
--- extensions/Comments_on_Albums/include/functions_comment.inc.php	(revision 9624)
+++ extensions/Comments_on_Albums/include/functions_comment.inc.php	(revision 9624)
@@ -0,0 +1,330 @@
+<?php
+/* This is a copy of include/functions_comment.inc.php but adapted for Comments On Albums */
+
+//returns string action to perform on a new comment: validate, moderate, reject
+function user_comment_check($action, $comment)
+{
+  global $conf,$user;
+
+  if ($action=='reject')
+    return $action;
+
+  $my_action = $conf['comment_spam_reject'] ? 'reject':'moderate';
+
+  if ($action==$my_action)
+    return $action;
+
+  // we do here only BASIC spam check (plugins can do more)
+  if ( !is_a_guest() )
+    return $action;
+
+  $link_count = preg_match_all( '/https?:\/\//',
+    $comment['content'], $matches);
+
+  if ( strpos($comment['author'], 'http://')!==false )
+  {
+    $link_count++;
+  }
+
+  if ( $link_count>$conf['comment_spam_max_links'] )
+    return $my_action;
+
+  return $action;
+}
+
+
+add_event_handler('user_comment_check', 'user_comment_check',
+  EVENT_HANDLER_PRIORITY_NEUTRAL, 2);
+
+/**
+ * Tries to insert a user comment in the database and returns one of :
+ * validate, moderate, reject
+ * @param array comm contains author, content, image_id
+ * @param string key secret key sent back to the browser
+ * @param array infos out array of messages
+ */
+function insert_user_comment( &$comm, $key, &$infos )
+{
+  global $conf, $user;
+
+  $comm = array_merge( $comm,
+    array(
+      'ip' => $_SERVER['REMOTE_ADDR'],
+      'agent' => $_SERVER['HTTP_USER_AGENT']
+    )
+   );
+
+  $infos = array();
+  if (!$conf['comments_validation'] or is_admin())
+  {
+    $comment_action='validate'; //one of validate, moderate, reject
+  }
+  else
+  {
+    $comment_action='moderate'; //one of validate, moderate, reject
+  }
+
+  // display author field if the user status is guest or generic
+  if (!is_classic_user())
+  {
+    if ( empty($comm['author']) )
+    {
+      $comm['author'] = 'guest';
+    }
+    $comm['author_id'] = $conf['guest_id'];
+    // if a guest try to use the name of an already existing user, he must be
+    // rejected
+    if ( $comm['author'] != 'guest' )
+    {
+      $query = '
+SELECT COUNT(*) AS user_exists
+  FROM '.USERS_TABLE.'
+  WHERE '.$conf['user_fields']['username']." = '".addslashes($comm['author'])."'";
+      $row = pwg_db_fetch_assoc( pwg_query( $query ) );
+      if ( $row['user_exists'] == 1 )
+      {
+        array_push($infos, l10n('This login is already used by another user') );
+        $comment_action='reject';
+      }
+    }
+  }
+  else
+  {
+    $comm['author'] = addslashes($user['username']);
+    $comm['author_id'] = $user['id'];
+  }
+
+  if ( empty($comm['content']) )
+  { // empty comment content
+    $comment_action='reject';
+  }
+
+  if ( !verify_ephemeral_key(@$key, $comm['image_id']) )
+  {
+    $comment_action='reject';
+  }
+
+  if ($comment_action!='reject' and $conf['anti-flood_time']>0 and !is_admin())
+  { // anti-flood system
+    $reference_date = pwg_db_get_flood_period_expression($conf['anti-flood_time']);
+
+    $query = '
+SELECT count(1) FROM '.COA_TABLE.'
+  WHERE date > '.$reference_date.'
+    AND author_id = '.$comm['author_id'];
+    list($counter) = pwg_db_fetch_row(pwg_query($query));
+    if ( $counter > 0 )
+    {
+      array_push( $infos, l10n('Anti-flood system : please wait for a moment before trying to post another comment') );
+      $comment_action='reject';
+    }
+  }
+
+  // perform more spam check
+  $comment_action = trigger_event('user_comment_check',
+      $comment_action, $comm
+    );
+
+  if ( $comment_action!='reject' )
+  {
+    $query = '
+INSERT INTO '.COA_TABLE.'
+  (author, author_id, content, date, validated, validation_date, category_id)
+  VALUES (
+    \''.$comm['author'].'\',
+    '.$comm['author_id'].',
+    \''.$comm['content'].'\',
+    NOW(),
+    \''.($comment_action=='validate' ? 'true':'false').'\',
+    '.($comment_action=='validate' ? 'NOW()':'NULL').',
+    '.$comm['image_id'].'
+  )
+';
+
+    pwg_query($query);
+
+    $comm['id'] = pwg_db_insert_id(COA_TABLE);
+
+    if ($conf['email_admin_on_comment']
+        or ($conf['email_admin_on_comment_validation'] and 'moderate' == $comment_action))
+    {
+      include_once(PHPWG_ROOT_PATH.'include/functions_mail.inc.php');
+
+      $comment_url = get_absolute_root_url().'comments.php?comment_id='.$comm['id'];
+
+      $keyargs_content = array
+      (
+        get_l10n_args('Author: %s', stripslashes($comm['author']) ),
+        get_l10n_args('Comment: %s', stripslashes($comm['content']) ),
+        get_l10n_args('', ''),
+        get_l10n_args('Manage this user comment: %s', $comment_url)
+      );
+
+      if ('moderate' == $comment_action)
+      {
+        $keyargs_content[] = get_l10n_args('', '');
+        $keyargs_content[] = get_l10n_args('(!) This comment requires validation', '');
+      }
+
+      pwg_mail_notification_admins
+      (
+        get_l10n_args('Comment by %s', stripslashes($comm['author']) ),
+        $keyargs_content
+      );
+    }
+  }
+  return $comment_action;
+}
+
+/**
+ * Tries to delete a user comment in the database
+ * only admin can delete all comments
+ * other users can delete their own comments
+ * so to avoid a new sql request we add author in where clause
+ *
+ * @param comment_id
+ */
+function delete_user_comment($comment_id) {
+  $user_where_clause = '';
+  if (!is_admin())
+  {
+    $user_where_clause = '   AND author_id = \''.$GLOBALS['user']['id'].'\'';
+  }
+  $query = '
+DELETE FROM '.COA_TABLE.'
+  WHERE id = '.$comment_id.
+$user_where_clause.'
+;';
+  $result = pwg_query($query);
+  if ($result) {
+    email_admin('delete', 
+                array('author' => $GLOBALS['user']['username'],
+                      'comment_id' => $comment_id
+                  ));
+  }
+}
+
+/**
+ * Tries to update a user comment in the database
+ * only admin can update all comments
+ * users can edit their own comments if admin allow them
+ * so to avoid a new sql request we add author in where clause
+ *
+ * @param comment_id
+ * @param post_key
+ * @param content
+ */
+
+function update_user_comment($comment, $post_key)
+{
+  global $conf;
+
+  $comment_action = 'validate';
+
+  if ( !verify_ephemeral_key($post_key, $comment['image_id']) )
+  {
+    $comment_action='reject';
+  }
+
+  // perform more spam check
+  $comment_action =
+    trigger_event('user_comment_check',
+		  $comment_action,
+		  array_merge($comment,
+			      array('author' => $GLOBALS['user']['username'])
+			      )
+		  );
+
+  if ( $comment_action!='reject' )
+  {
+    $user_where_clause = '';
+    if (!is_admin())
+    {
+      $user_where_clause = '   AND author_id = \''.
+	$GLOBALS['user']['id'].'\'';
+    }
+    $query = '
+UPDATE '.COA_TABLE.'
+  SET content = \''.$comment['content'].'\',
+      validation_date = now()
+  WHERE id = '.$comment['comment_id'].
+$user_where_clause.'
+;';
+    $result = pwg_query($query);
+    if ($result) {
+      email_admin('edit', array('author' => $GLOBALS['user']['username'],
+				'content' => stripslashes($comment['content'])) );
+    }
+  }
+}
+
+function email_admin($action, $comment)
+{
+  global $conf;
+
+  if (!in_array($action, array('edit', 'delete'))
+      or (($action=='edit') and !$conf['email_admin_on_comment_edition'])
+      or (($action=='delete') and !$conf['email_admin_on_comment_deletion']))
+  {
+    return;
+  }
+
+  include_once(PHPWG_ROOT_PATH.'include/functions_mail.inc.php');
+
+  $keyargs_content = array();
+  $keyargs_content[] = get_l10n_args('Author: %s', $comment['author']);
+  if ($action=='delete')
+  {
+    $keyargs_content[] = get_l10n_args('This author removed the comment with id %d',
+				       $comment['comment_id']
+				       );
+  }
+  else
+  {
+    $keyargs_content[] = get_l10n_args('This author modified following comment:', '');
+    $keyargs_content[] = get_l10n_args('Comment: %s', $comment['content']);
+  }
+
+  pwg_mail_notification_admins(get_l10n_args('Comment by %s',
+					     $comment['author']),
+			       $keyargs_content
+			       );
+}
+
+function get_comment_author_id($comment_id, $die_on_error=true)
+{
+  $query = '
+SELECT
+    author_id
+  FROM '.COA_TABLE.'
+  WHERE id = '.$comment_id.'
+;';
+  $result = pwg_query($query);
+  if (pwg_db_num_rows($result) == 0)
+  {
+    if ($die_on_error)
+    {
+      fatal_error('Unknown comment identifier');
+    }
+    else
+    {
+      return false;
+    }
+  }
+  
+  list($author_id) = pwg_db_fetch_row($result);
+
+  return $author_id;
+}
+
+function validate_user_comment($comment_id)
+{
+  $query = '
+UPDATE '.COA_TABLE.'
+  SET validated = \'true\'
+    , validation_date = NOW()
+  WHERE id = '.$comment_id.'
+;';
+  pwg_query($query);
+}
+?>
Index: extensions/Comments_on_Albums/index.php
===================================================================
--- extensions/Comments_on_Albums/index.php	(revision 9624)
+++ extensions/Comments_on_Albums/index.php	(revision 9624)
@@ -0,0 +1,30 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based picture gallery                                  |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008-2010 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.                                                                  |
+// +-----------------------------------------------------------------------+
+
+// Recursive call
+$url = '../';
+header( 'Request-URI: '.$url );
+header( 'Content-Location: '.$url );
+header( 'Location: '.$url );
+exit();
+?>
Index: extensions/Comments_on_Albums/main.inc.php
===================================================================
--- extensions/Comments_on_Albums/main.inc.php	(revision 9624)
+++ extensions/Comments_on_Albums/main.inc.php	(revision 9624)
@@ -0,0 +1,54 @@
+<?php
+/*
+Plugin Name: Comment on Albums
+Version: 0.1.a
+Description: Allows users to comment albums
+Plugin URI: http://piwigo.org/ext/extension_view.php?eid=512
+Author: Mistic
+Author URI: http://www.strangeplanet.fr
+*/
+
+if (!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
+
+global $prefixeTable;
+
+// +-----------------------------------------------------------------------+
+//					Global variables
+// +-----------------------------------------------------------------------+
+define('COA_NAME' , 'Comment on Albums');
+define('COA_VERSION', '0.1.a');
+define('COA_DIR' , basename(dirname(__FILE__)));
+define('COA_PATH' , PHPWG_PLUGINS_PATH . COA_DIR . '/');
+define('COA_TABLE' , $prefixeTable . 'comments_categories');
+
+
+// +-----------------------------------------------------------------------+
+//					Triggers
+// +-----------------------------------------------------------------------+
+add_event_handler('loc_end_index', 'COA_index');
+add_event_handler('loc_end_admin', 'COA_admin');
+
+
+// +-----------------------------------------------------------------------+
+//					Fonctions
+// +-----------------------------------------------------------------------+
+
+function COA_index() {
+	global $template, $page, $conf;
+	
+	if ($page['section'] == 'categories' AND isset($page['category'])) {	
+		include(COA_PATH . 'include/coa_albums.php');
+	}
+}
+
+function COA_admin() {
+	global $template, $page, $conf;
+	
+	if ($page['page'] == 'comments') {	
+		include(COA_PATH . 'include/coa_admin_comments.php');
+	} else if ($page['page'] == 'intro') {	
+		include(COA_PATH . 'include/coa_admin_intro.php');
+	} 
+}
+
+?>
Index: extensions/Comments_on_Albums/maintain.inc.php
===================================================================
--- extensions/Comments_on_Albums/maintain.inc.php	(revision 9624)
+++ extensions/Comments_on_Albums/maintain.inc.php	(revision 9624)
@@ -0,0 +1,27 @@
+<?php
+if (!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
+
+// Installation
+function plugin_install() {
+	global $prefixeTable;
+
+	pwg_query("CREATE TABLE `" . $prefixeTable . "comments_categories` (
+		`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+		`category_id` mediumint(8) unsigned NOT NULL DEFAULT '0',
+		`date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+		`author` varchar(255) DEFAULT NULL,
+		`author_id` smallint(5) DEFAULT NULL,
+		`content` longtext,
+		`validated` enum('true','false') NOT NULL DEFAULT 'false',
+		`validation_date` datetime DEFAULT NULL,
+		PRIMARY KEY (`id`)
+	);");
+}
+
+// Uninstallation
+function plugin_uninstall() {
+	global $prefixeTable;
+
+	pwg_query("DROP TABLE `" . $prefixeTable . "comments_categories`;");
+}
+?>
Index: extensions/Comments_on_Albums/template/coa_admin.tpl
===================================================================
--- extensions/Comments_on_Albums/template/coa_admin.tpl	(revision 9624)
+++ extensions/Comments_on_Albums/template/coa_admin.tpl	(revision 9624)
@@ -0,0 +1,67 @@
+{literal}
+<script type="text/javascript">
+	$(document).ready(function(){
+		$(".checkComment").click(function(event) {
+			if (event.target.type !== 'checkbox') {
+				var checkbox = $(this).children("input[type=checkbox]");
+				$(checkbox).attr('checked', !$(checkbox).is(':checked'));
+			}
+		});
+		$("#commentSelectAll").click(function () {
+			$(".checkComment input[type=checkbox]").attr('checked', true);
+			return false;
+		});
+		$("#commentSelectNone").click(function () {
+			$(".checkComment input[type=checkbox]").attr('checked', false);
+			return false;
+		});
+		$("#commentSelectInvert").click(function () {
+			$(".checkComment input[type=checkbox]").each(function() {
+				$(this).attr('checked', !$(this).is(':checked'));
+			});
+			return false;
+		});
+	});
+</script>
+{/literal}
+
+<div class="titrePage">
+	<h2>{'Waiting'|@translate} {$TABSHEET_TITLE}</h2>
+</div>
+
+<h3>{'User comments validation'|@translate}</h3>
+
+{if !empty($comments) }
+<form method="post" action="{$F_ACTION}">
+
+	<table width="99%">
+	{foreach from=$comments item=comment name=comment}
+		<tr valign="top" class="{if $smarty.foreach.comment.index is odd}row2{else}row1{/if}">
+			<td style="width:50px;" class="checkComment">
+				<input type="checkbox" name="comments[]" value="{$comment.ID}">
+			</td>
+			<td>
+				<div class="comment">
+					<a class="illustration" href="{$comment.CAT_URL}">{$comment.CAT_NAME}</a>
+					<p class="commentHeader"><strong>{$comment.AUTHOR}</strong> - <em>{$comment.DATE}</em></p>
+					<blockquote>{$comment.CONTENT}</blockquote>
+				</div>
+			</td>
+		</tr>
+	{/foreach}
+	</table>
+
+	<p class="checkActions">
+		{'Select:'|@translate}
+		<a href="#" id="commentSelectAll">{'All'|@translate}</a>,
+		<a href="#" id="commentSelectNone">{'None'|@translate}</a>,
+		<a href="#" id="commentSelectInvert">{'Invert'|@translate}</a>
+	</p>
+
+	<p class="bottomButtons">
+		<input class="submit" type="submit" name="validate_albums" value="{'Validate'|@translate}">
+		<input class="submit" type="submit" name="reject_albums" value="{'Reject'|@translate}">
+	</p>
+
+</form>
+{/if}
Index: extensions/Comments_on_Albums/template/coa_albums.tpl
===================================================================
--- extensions/Comments_on_Albums/template/coa_albums.tpl	(revision 9624)
+++ extensions/Comments_on_Albums/template/coa_albums.tpl	(revision 9624)
@@ -0,0 +1,34 @@
+{combine_script id="jquery"}
+{combine_css path=$COA_PATH|@cat:'template/style.css'}
+
+{footer_script equire="jquery"}{literal}
+	// cancel all color changements and set default color to comments
+	jQuery('.thumbnailCategory').css('color', jQuery('body').css('color'));
+{/literal}{/footer_script}
+
+<a name="comments"></a>
+<div id="comments">
+	{if $COMMENT_COUNT > 0}
+		<h3>{$pwg->l10n_dec('%d comment', '%d comments',$COMMENT_COUNT)}</h3>
+	{/if}
+
+	{if isset($comments)}
+		{include file='comment_list.tpl'}
+	{/if}
+	
+	{if !empty($comment_navbar)}{include file=$COA_ABSOLUTE_PATH|@cat:'template/navigation_bar.tpl'}{/if}
+
+	{if isset($comment_add)}
+	<form method="post" action="{$comment_add.F_ACTION}" class="filter" id="addComment">
+		<fieldset>
+			<legend>{'Add a comment'|@translate}</legend>
+			{if $comment_add.SHOW_AUTHOR}
+				<label>{'Author'|@translate}<input type="text" name="author"></label>
+			{/if}
+			<label>{'Comment'|@translate}<textarea name="content" id="contentid" rows="5" cols="80">{$comment_add.CONTENT}</textarea></label>
+			<input type="hidden" name="key" value="{$comment_add.KEY}">
+			<input type="submit" value="{'Submit'|@translate}">
+		</fieldset>
+	</form>
+	{/if}
+</div>
Index: extensions/Comments_on_Albums/template/coa_intro.tpl
===================================================================
--- extensions/Comments_on_Albums/template/coa_intro.tpl	(revision 9624)
+++ extensions/Comments_on_Albums/template/coa_intro.tpl	(revision 9624)
@@ -0,0 +1,10 @@
+{footer_script}
+	// add element to database informations, non intrusive way
+	jQuery('<li>'+
+		'{$NB_COMMENTS}'+
+		{if isset($unvalidated_albums)}
+			' (<a href="{$unvalidated_albums.URL}">{$unvalidated_albums.INFO}</a>)'+
+		{/if}
+	'</li>')
+	.appendTo('dl[style="padding-top: 30px;"] dd:last-child ul');
+{/footer_script}
Index: extensions/Comments_on_Albums/template/coa_messages.tpl
===================================================================
--- extensions/Comments_on_Albums/template/coa_messages.tpl	(revision 9624)
+++ extensions/Comments_on_Albums/template/coa_messages.tpl	(revision 9624)
@@ -0,0 +1,18 @@
+{if isset($errors)}
+<div class="errors">
+	<ul>
+		{foreach from=$errors item=error}
+		<li>{$error}</li>
+		{/foreach}
+	</ul>
+</div>
+{/if}
+{if isset($infos)}
+<div class="infos">
+	<ul>
+		{foreach from=$infos item=info}
+		<li>{$info}</li>
+		{/foreach}
+	</ul>
+</div>
+{/if}
Index: extensions/Comments_on_Albums/template/navigation_bar.tpl
===================================================================
--- extensions/Comments_on_Albums/template/navigation_bar.tpl	(revision 9624)
+++ extensions/Comments_on_Albums/template/navigation_bar.tpl	(revision 9624)
@@ -0,0 +1,28 @@
+<div class="navigationBar">
+	{if isset($comment_navbar.URL_FIRST)}
+		<a href="{$comment_navbar.URL_FIRST}#comments" rel="first">{'First'|@translate}</a> |
+		<a href="{$comment_navbar.URL_PREV}#comments" rel="prev">{'Previous'|@translate}</a> |
+	{else}
+		{'First'|@translate} |
+		{'Previous'|@translate} |
+	{/if}
+
+	{assign var='prev_page' value=0}
+	{foreach from=$comment_navbar.pages key=page item=url}
+		{if $page > $prev_page+1}...{/if}
+		{if $page == $comment_navbar.CURRENT_PAGE}
+			<span class="pageNumberSelected">{$page}</span>
+		{else}
+			<a href="{$url}#comments">{$page}</a>
+		{/if}
+		{assign var='prev_page' value=$page}
+	{/foreach}
+
+	{if isset($comment_navbar.URL_NEXT)}
+		| <a href="{$comment_navbar.URL_NEXT}#comments" rel="next">{'Next'|@translate}</a>
+		| <a href="{$comment_navbar.URL_LAST}#comments" rel="last">{'Last'|@translate}</a>
+	{else}
+		| {'Next'|@translate}
+		| {'Last'|@translate}
+	{/if}
+</div>
Index: extensions/Comments_on_Albums/template/style.css
===================================================================
--- extensions/Comments_on_Albums/template/style.css	(revision 9624)
+++ extensions/Comments_on_Albums/template/style.css	(revision 9624)
@@ -0,0 +1,36 @@
+#comments .thumbnailCategories li,
+#comments .thumbnailCategory ,
+#comments .thumbnailCategory .description,
+#comments .thumbnailCategory div.illustration { 
+	background-image:none;
+	height:auto !important;
+}
+
+#comments .thumbnailCategory {
+	padding:0px 2px 0px 12px !important; 
+	border:1px solid grey;
+}
+#comments .thumbnailCategories {
+	margin-left:5px;
+}
+
+#comments .thumbnailCategories li:hover,
+#comments .thumbnailCategory:hover,
+#comments .thumbnailCategory:hover A { 
+	background:none; 
+}
+	
+#comments .thumbnailCategory  .date, #comments .thumbnailCategory  .author {
+	font-weight:bold;
+}
+
+#comments {
+	padding:0;
+}
+#comments h3 {
+	padding-left:10px;
+}
+
+.errors, .infos {
+	padding-left:20em;
+}
