Index: admin/blogs.php
===================================================================
--- admin/blogs.php	(revision 3400)
+++ admin/blogs.php	(revision 3402)
@@ -13,5 +13,5 @@
 require dirname(__FILE__).'/../inc/admin/prepend.php';
 
-dcPage::check('usage,contentadmin');
+dcPage::checkSuper();
 
 # Filters
@@ -22,15 +22,23 @@
 
 $sortby_combo = array(
-__('Last update') => 'blog_upddt',
-__('Blog name') => 'UPPER(blog_name)',
-__('Blog ID') => 'B.blog_id',
-__('Status') => 'blog_status'
+	__('Last update') => 'blog_upddt',
+	__('Blog name') => 'UPPER(blog_name)',
+	__('Blog ID') => 'B.blog_id',
+	__('Status') => 'blog_status'
 );
 
 $order_combo = array(
-__('Descending') => 'desc',
-__('Ascending') => 'asc'
+	__('Descending') => 'desc',
+	__('Ascending') => 'asc'
 );
 
+# Actions
+$blogs_actions_page = new dcBlogsActionsPage($core,$core->adminurl->get("admin.blogs"));
+
+if ($blogs_actions_page->process()) {
+	return;
+}
+
+# Requests
 $q = !empty($_GET['q']) ? $_GET['q'] : '';
 $status = isset($_GET['status']) ? $_GET['status'] : '';
@@ -88,4 +96,5 @@
 		$rsStatic->lexicalSort(($sortby == 'UPPER(blog_name)' ? 'blog_name' : 'blog_id'),$order);
 	}
+	$blog_list = new adminBlogList($core,$rs,$counter->f(0));
 } catch (Exception $e) {
 	$core->error->add($e->getMessage());
@@ -96,5 +105,5 @@
 
 dcPage::open(__('List of blogs'),
-	dcPage::jsFilterControl($show_filters),
+	dcPage::jsLoad('js/_blogs.js').dcPage::jsFilterControl($show_filters),
 	dcPage::breadcrumb(
 		array(
@@ -103,8 +112,4 @@
 		))
 );
-
-if (!empty($_GET['del'])) {
-	dcPage::success(__('Blog has been successfully deleted.'));
-}
 
 if (!$core->error->flag())
@@ -143,82 +148,34 @@
 
 	# Show blogs
-	if ($nb_blog == 0)
-	{
-		if( $show_filters ) {
-			echo '<p><strong>'.__('No blog matches the filter').'</strong></p>';
-		} else {
-			echo '<p><strong>'.__('No blog').'</strong></p>';
-		}
-	}
-	else
-	{
-		$pager = new dcPager($page,$nb_blog,$nb_per_page,10);
+	$blog_list->display($page,$nb_per_page,
+	'<form action="'.$core->adminurl->get("admin.blogs").'" method="post" id="form-blogs">'.
 
-		echo $pager->getLinks();
+	'%s'.
 
-		echo
-		'<div class="table-outer">'.
-		'<table class="clear">';
+	'<div class="two-cols">'.
+	'<p class="col checkboxes-helpers"></p>'.
 
-		if( $show_filters ) {
-			echo '<caption>'.sprintf(__('%d blog matches the filter.','%d blogs match the filter.',$nb_blog),$nb_blog).'</caption>';
-		} else {
-			echo '<caption class="hidden">'.__('Blogs list').'</caption>';
-		}
+	'<p class="col right"><label for="action" class="classic">'.__('Selected blogs action:').'</label> '.
+	form::combo('action',$blogs_actions_page->getCombo(),'online','','','','title="'.__('Actions').'"').
+	$core->formNonce().
+	'<input id="do-action" type="submit" value="'.__('ok').'" /></p>'.
+	form::hidden(array('sortby'),$sortby).
+	form::hidden(array('order'),$order).
+	form::hidden(array('status'),$status).
+	form::hidden(array('page'),$page).
+	form::hidden(array('nb'),$nb_per_page).
+	'</div>'.
 
-		echo
-		'<tr>'.
-		'<th scope="col" class="nowrap">'.__('Blog id').'</th>'.
-		'<th scope="col">'.__('Blog name').'</th>'.
-		'<th scope="col" class="nowrap">'.__('URL').'</th>'.
-		'<th scope="col" class="nowrap">'.__('Entries (all types)').'</th>'.
-		'<th scope="col" class="nowrap">'.__('Last update').'</th>'.
-		'<th scope="col" class="nowrap">'.__('Status').'</th>'.
-		'</tr>';
+	'<div>'.
+	'<p>'.__('Please give your password to confirm the blog deletion.').'</p>'.
+	'<p><label for="pwd">'.__('Your password:').'</label> '.
+	form::password('pwd',20,255).'</p>'.
+	'</div>'.
 
-		while ($rsStatic->fetch()) {
-			echo blogLine($rsStatic);
-		}
+	'</form>',
+	$show_filters
+	);
+}
 
-		echo '</table></div>';
-
-		echo $pager->getLinks();
-	}
-}
 dcPage::helpBlock('core_blogs');
 dcPage::close();
-
-function blogLine($rs)
-{
-	global $core;
-
-	$blog_id = html::escapeHTML($rs->blog_id);
-	$edit_link = '';
-
-	if ($GLOBALS['core']->auth->isSuperAdmin()) {
-		$edit_link =
-		'<a href="'.$core->adminurl->get("admin.blog",array('id' => $blog_id)).'"  title="'.sprintf(__('Edit blog settings for %s'),$blog_id).'">'.
-		'<img src="images/edit-mini.png" alt="'.__('Edit blog settings').'" /> '.$blog_id.'</a> ';
-	} else {
-		$edit_link = $blog_id;
-	}
-
-	$img_status = $rs->blog_status == 1 ? 'check-on' : ($rs->blog_status == 0 ? 'check-off' : 'check-wrn');
-	$txt_status = $GLOBALS['core']->getBlogStatus($rs->blog_status);
-	$img_status = sprintf('<img src="images/%1$s.png" alt="%2$s" title="%2$s" />',$img_status,$txt_status);
-	$offset = dt::getTimeOffset($core->auth->getInfo('user_tz'));
-	$blog_upddt = dt::str(__('%Y-%m-%d %H:%M'),strtotime($rs->blog_upddt) + $offset);
-
-	return
-	'<tr class="line">'.
-	'<td class="nowrap">'.$edit_link.'</td>'.
-	'<td class="maximal"><a href="'.$core->adminurl->get("admin.home",array('switchblog' => $rs->blog_id)).'" '.
-	'title="'.sprintf(__('Switch to blog %s'),$rs->blog_id).'">'.
-	html::escapeHTML($rs->blog_name).'</a></td>'.
-	'<td class="nowrap"><a class="outgoing" href="'.html::escapeHTML($rs->blog_url).'">'.html::escapeHTML($rs->blog_url).
-	' <img src="images/outgoing-blue.png" alt="" /></a></td>'.
-	'<td class="nowrap count">'.$core->countBlogPosts($rs->blog_id).'</td>'.
-	'<td class="nowrap count">'.$blog_upddt.'</td>'.
-	'<td class="status">'.$img_status.'</td>'.
-	'</tr>';
-}
Index: admin/js/_blogs.js
===================================================================
--- admin/js/_blogs.js	(revision 3402)
+++ admin/js/_blogs.js	(revision 3402)
@@ -0,0 +1,7 @@
+$(function() {
+	$('.checkboxes-helpers').each(function() {
+		dotclear.checkboxesHelpers(this,undefined,'#form-blogs td input[type=checkbox]','#form-blogs #do-action');
+	});
+	$('#form-blogs td input[type=checkbox]').enableShiftClick();
+	dotclear.condSubmit('#form-blogs td input[type=checkbox]','#form-blogs #do-action');
+});
Index: admin/js/_blogs_actions.js
===================================================================
--- admin/js/_blogs_actions.js	(revision 3402)
+++ admin/js/_blogs_actions.js	(revision 3402)
@@ -0,0 +1,3 @@
+$(function() {
+	dotclear.condSubmit('table.posts-list td input[type=checkbox]','input[type=submit]');
+});
Index: inc/admin/actions/class.dcactionblogs.php
===================================================================
--- inc/admin/actions/class.dcactionblogs.php	(revision 3402)
+++ inc/admin/actions/class.dcactionblogs.php	(revision 3402)
@@ -0,0 +1,189 @@
+<?php
+# -- BEGIN LICENSE BLOCK ---------------------------------------
+#
+# This file is part of Dotclear 2.
+#
+# Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear
+# Licensed under the GPL version 2.0 license.
+# See LICENSE file or
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+#
+# -- END LICENSE BLOCK -----------------------------------------
+if (!defined('DC_RC_PATH')) { return; }
+
+class dcBlogsActionsPage extends dcActionsPage
+{
+	public function __construct($core,$uri,$redirect_args=array()) {
+		parent::__construct($core,$uri,$redirect_args);
+		$this->redirect_fields = array('status','sortby','order','page','nb');
+		$this->field_entries = 'blogs';
+		$this->title_cb = __('Blogs');
+		$this->loadDefaults();
+		$core->callBehavior('adminBlogsActionsPage',$core,$this);
+	}
+
+	protected function loadDefaults() {
+		// We could have added a behavior here, but we want default action
+		// to be setup first
+		dcDefaultBlogActions::adminBlogsActionsPage($this->core,$this);
+	}
+
+	public function beginPage($breadcrumb='',$head='') {
+		if ($this->in_plugin) {
+			echo '<html><head><title>'.__('Blogs').'</title>'.
+				dcPage::jsLoad('js/_blogs_actions.js').
+				$head.
+				'</script></head><body>'.
+				$breadcrumb;
+		} else {
+			dcPage::open(
+				__('Blogs'),
+				dcPage::jsLoad('js/_blogs_actions.js').
+				$head,
+				$breadcrumb
+			);
+		}
+		echo '<p><a class="back" href="'.$this->getRedirection(true).'">'.__('Back to blogs list').'</a></p>';
+	}
+
+	public function endPage() {
+		dcPage::close();
+	}
+
+	public function error(Exception $e) {
+		$this->core->error->add($e->getMessage());
+		$this->beginPage(dcPage::breadcrumb(
+			array(
+				html::escapeHTML($this->core->blog->name) => '',
+				__('Blogs') => $this->core->adminurl->get('admin.blogs'),
+				__('Blogs actions') => ''
+			))
+		);
+		$this->endPage();
+	}
+
+	public function getCheckboxes() {
+		$ret = '';
+		foreach ($this->entries as $id=>$res) {
+			$ret .=
+			'<tr>'.
+			'<td class="minimal">'.form::checkbox(array($this->field_entries.'[]'),$id,true,'','').'</td>'.
+			'<td>'.$res['blog'].'</td>'.
+			'<td>'.$res['name'].'</td>'.
+			'</tr>';
+		}
+
+		return 
+		'<table class="posts-list"><tr>'.
+		'<th colspan="2">'.__('Blog id').'</th><th>'.__('Blog name').'</th>'.
+		'</tr>'.$ret.'</table>';
+	}
+
+	protected function fetchEntries($from) {
+		$params=array();
+		if (!empty($from['blogs'])) {
+			$params['blog_id'] = $from['blogs'];
+		}
+
+		$bl = $this->core->getBlogs($params);
+		while ($bl->fetch())	{
+			$this->entries[$bl->blog_id] = array(
+				'blog' => $bl->blog_id,
+				'name' => $bl->blog_name
+			);
+		}
+		$this->rs = $bl;
+	}
+}
+
+class dcDefaultBlogActions
+{
+	public static function adminBlogsActionsPage($core, dcBlogsActionsPage $ap) {
+		if (!$core->auth->isSuperAdmin()) {
+			return;
+		}
+
+		$ap->addAction(
+			array(__('Status') => array(
+				__('Set online') => 'online',
+				__('Set offline') => 'offline',
+				__('Remove') => 'remove'
+			)),
+			array('dcDefaultBlogActions','doChangeBlogStatus')
+		);
+		$ap->addAction(
+			array(__('Delete') => array(
+				__('Delete') => 'delete')),
+			array('dcDefaultBlogActions','doDeleteBlog')
+		);
+	}
+
+	public static function doChangeBlogStatus($core, dcBlogsActionsPage $ap, $post) {
+		if (!$core->auth->isSuperAdmin()) {
+			return;
+		}
+
+		$action = $ap->getAction();
+		$ids = $ap->getIDs();
+		if (empty($ids)) {
+			throw new Exception(__('No blog selected'));
+		}
+		switch ($action) {
+			case 'online' : $status = 1; break;
+			case 'offline' : $status = 0; break;
+			case 'remove' : $status = -1; break;
+			default : $status = 1; break;
+		}
+
+		$cur = $core->con->openCursor($core->prefix.'blog');
+		$cur->blog_status = $status;
+		//$cur->blog_upddt = date('Y-m-d H:i:s');
+		$cur->update('WHERE blog_id '.$core->con->in($ids));
+
+		dcPage::addSuccessNotice(__('Selected blogs have been successfully updated.'));
+		$ap->redirect(true);
+	}
+
+	public static function doDeleteBlog($core, dcBlogsActionsPage $ap, $post) {
+		if (!$core->auth->isSuperAdmin()) {
+			return;
+		}
+
+		$ap_ids = $ap->getIDs();
+		if (empty($ap_ids)) {
+			throw new Exception(__('No blog selected'));
+		}
+
+		if (!$core->auth->checkPassword($core->auth->crypt($_POST['pwd']))) {
+			throw new Exception(__('Password verification failed'));
+		}
+
+		$ids = array();
+		foreach($ap_ids as $id) {
+			if ($id == $core->blog->id) {
+				dcPage::addWarningNotice(__('The current blog cannot be deleted.'));
+			} else {
+				$ids[] = $id;
+			}
+		}
+
+		if (!empty($ids)) {
+			# --BEHAVIOR-- adminBeforeBlogsDelete
+			$core->callBehavior('adminBeforeBlogsDelete',$ids);
+
+			foreach($ids as $id) {
+				$core->delBlog($id);
+			}
+
+			dcPage::addSuccessNotice(sprintf(
+				__(
+					'%d blog has been successfully deleted',
+					'%d blogs have been successfully deleted',
+					count($ids)
+				),
+				count($ids))
+			);
+		}
+		$ap->redirect(false);
+	}
+}
Index: inc/admin/lib.pager.php
===================================================================
--- inc/admin/lib.pager.php	(revision 3265)
+++ inc/admin/lib.pager.php	(revision 3402)
@@ -662,4 +662,124 @@
 }
 
+class adminBlogList extends adminGenericList
+{
+	public function display($page,$nb_per_page,$enclose_block='',$filter=false)
+	{
+		if ($this->rs->isEmpty())
+		{
+			if($filter) {
+				echo '<p><strong>'.__('No blog matches the filter').'</strong></p>';
+			} else {
+				echo '<p><strong>'.__('No blog').'</strong></p>';
+			}
+		}
+		else
+		{
+			$blogs = array();
+			if (isset($_REQUEST['blogs'])) {
+				foreach ($_REQUEST['blogs'] as $v) {
+					$blogs[$v]=true;
+				}
+			}
+
+			$pager = new dcPager($page,$this->rs_count,$nb_per_page,10);
+
+			$cols = array(
+				'blog' =>	'<th colspan="2" scope="col" abbr="comm" class="first nowrap">'.__('Blog id').'</th>',
+				'name' => '<th scope="col" abbr="name">'.__('Blog name').'</th>',
+				'url' => '<th scope="col" class="nowrap">'.__('URL').'</th>',
+				'posts' => '<th scope="col" class="nowrap">'.__('Entries (all types)').'</th>',
+				'upddt' => '<th scope="col" class="nowrap">'.__('Last update').'</th>',
+				'status' => '<th scope="col" class="txt-center">'.__('Status').'</th>'
+			);
+
+			$cols = new ArrayObject($cols);
+			$this->core->callBehavior('adminBlogListHeader',$this->core,$this->rs,$cols);
+
+			$html_block =
+			'<div class="table-outer"><table>'.
+			($filter ?
+				'<caption>'.
+				sprintf(__('%d blog matches the filter.','%d blogs match the filter.',$this->rs_count),$this->rs_count).
+				'</caption>'
+			:
+				'<caption class="hidden">'.__('Blogs list').'</caption>'
+			).
+			'<tr>'.implode(iterator_to_array($cols)).'</tr>%s</table></div>';
+
+			if ($enclose_block) {
+				$html_block = sprintf($enclose_block,$html_block);
+			}
+
+			$blocks = explode('%s',$html_block);
+
+			echo $pager->getLinks();
+
+			echo $blocks[0];
+
+			while ($this->rs->fetch()) {
+				echo $this->blogLine(isset($blogs[$this->rs->blog_id]));
+			}
+
+			echo $blocks[1];
+
+			echo $pager->getLinks();
+		}
+	}
+
+	private function blogLine($checked=false)
+	{
+		$blog_id = html::escapeHTML($this->rs->blog_id);
+
+		$cols = array(
+			'check' => 
+				'<td class="nowrap">'.
+				form::checkbox(array('blogs[]'),$this->rs->blog_id,$checked,'','',0).
+				'</td>',
+			'blog' => 
+				'<td class="nowrap">'.
+				'<a href="'.$this->core->adminurl->get("admin.blog",array('id' => $blog_id)).'"  '.
+				'title="'.sprintf(__('Edit blog settings for %s'),$blog_id).'">'.
+				'<img src="images/edit-mini.png" alt="'.__('Edit blog settings').'" /> '.$blog_id.'</a> '.
+				'</td>',
+			'name' => 
+				'<td class="maximal">'.
+				'<a href="'.$this->core->adminurl->get("admin.home",array('switchblog' => $this->rs->blog_id)).'" '.
+				'title="'.sprintf(__('Switch to blog %s'),$this->rs->blog_id).'">'.
+				html::escapeHTML($this->rs->blog_name).'</a>'.
+				'</td>',
+			'url' =>
+				'<td class="nowrap">'.
+				'<a class="outgoing" href="'.
+				html::escapeHTML($this->rs->blog_url).'">'.html::escapeHTML($this->rs->blog_url).
+				' <img src="images/outgoing-blue.png" alt="" /></a></td>',
+			'posts' => 
+				'<td class="nowrap count">'.
+				$this->core->countBlogPosts($this->rs->blog_id).
+				'</td>',
+			'upddt' => 
+				'<td class="nowrap count">'.
+				dt::str(__('%Y-%m-%d %H:%M'),strtotime($this->rs->blog_upddt) + dt::getTimeOffset($this->core->auth->getInfo('user_tz'))).
+				'</td>',
+			'status' => 
+				'<td class="nowrap status txt-center">'.
+				sprintf(
+					'<img src="images/%1$s.png" alt="%2$s" title="%2$s" />',
+					($this->rs->blog_status == 1 ? 'check-on' : ($this->rs->blog_status == 0 ? 'check-off' : 'check-wrn')),
+					$this->core->getBlogStatus($this->rs->blog_status)
+				).
+				'</td>'
+		);
+
+		$cols = new ArrayObject($cols);
+		$this->core->callBehavior('adminBlogListValue',$this->core,$this->rs,$cols);
+
+		return 
+		'<tr class="line" id="b'.$blog_id.'">'.
+		implode(iterator_to_array($cols)).
+		'</tr>';
+	}
+}
+
 class adminUserList extends adminGenericList
 {
Index: inc/core/class.dc.core.php
===================================================================
--- inc/core/class.dc.core.php	(revision 3400)
+++ inc/core/class.dc.core.php	(revision 3402)
@@ -1017,10 +1017,13 @@
 		}
 
-		if (!empty($params['blog_status']) && $this->auth->isSuperAdmin()) {
-			$where = 'AND blog_status = '.(integer) $params['blog_status'].' ';
-		}
-
-		if (!empty($params['blog_id'])) {
-			$where .= "AND B.blog_id = '".$this->con->escape($params['blog_id'])."' ";
+		if (isset($params['blog_status']) && $params['blog_status'] !== '' && $this->auth->isSuperAdmin()) {
+			$where .= 'AND blog_status = '.(integer) $params['blog_status'].' ';
+		}
+
+		if (isset($params['blog_id']) && $params['blog_id'] !== '') {
+			if (!is_array($params['blog_id'])) {
+				$params['blog_id'] = array($params['blog_id']);
+			}
+			$where .= 'AND B.blog_id '.$this->con->in($params['blog_id']);
 		}
 
Index: inc/prepend.php
===================================================================
--- inc/prepend.php	(revision 3388)
+++ inc/prepend.php	(revision 3402)
@@ -68,4 +68,5 @@
 $__autoload['adminPostMiniList'] 	 = dirname(__FILE__).'/admin/lib.pager.php';
 $__autoload['adminCommentList']  	 = dirname(__FILE__).'/admin/lib.pager.php';
+$__autoload['adminBlogList']  	 = dirname(__FILE__).'/admin/lib.pager.php';
 $__autoload['adminUserList']     	 = dirname(__FILE__).'/admin/lib.pager.php';
 $__autoload['dcPager']           	 = dirname(__FILE__).'/admin/lib.pager.php';
@@ -77,4 +78,5 @@
 $__autoload['dcPostsActionsPage']    = dirname(__FILE__).'/admin/actions/class.dcactionposts.php';
 $__autoload['dcCommentsActionsPage'] = dirname(__FILE__).'/admin/actions/class.dcactioncomments.php';
+$__autoload['dcBlogsActionsPage'] = dirname(__FILE__).'/admin/actions/class.dcactionblogs.php';
 $__autoload['dcActionsPage']         = dirname(__FILE__).'/admin/actions/class.dcaction.php';
 
