diff --git a/ChangeLog b/ChangeLog
index a0ffcdd1e43bd32724483fc61e8d0120c320396e..31ddbd56bddfc7d8f2697712a32e317ce944cc84 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -11,6 +11,8 @@ Following changes may create regression for some external modules, but were nece
 * The trigger USER_SETINGROUP and USER_REMOVEFROMGROUP has been replaced with trigger USER_MODIFY.
 * The page societe/soc.php was renamed into societe/card.php to match page naming conventions.
 * The page compta/facture.php was renamed into compta/facture/card.php to match page naming conventions.
+* The signature of method ->delete() of class Product and PriceExpression was changed from 
+  ->delete($id, notrigger) to ->delete(User, notrigger) to match standard dev rules.
 
 
 ***** ChangeLog for 5.0.1 compared to 5.0.0 *****
diff --git a/htdocs/categories/viewcat.php b/htdocs/categories/viewcat.php
index a256b36236169bb67afe2b3adb331c4eda968986..e138107fd3fe018d320e0fc16de62f564d2cdacf 100644
--- a/htdocs/categories/viewcat.php
+++ b/htdocs/categories/viewcat.php
@@ -224,7 +224,7 @@ print '<div class="underbanner clearboth"></div>';
 print '<table width="100%" class="border">';
 
 // Description
-print '<tr><td class="titlefield notopnoleft">';
+print '<tr><td class="titlefield notopnoleft tdtop">';
 print $langs->trans("Description").'</td><td>';
 print dol_htmlentitiesbr($object->description);
 print '</td></tr>';
diff --git a/htdocs/commande/list.php b/htdocs/commande/list.php
index 140d1d379c742daaf0381e0b7ac5b2df4bbc570f..4185518b72110d62f88534082a8bf8abe18ddc2e 100644
--- a/htdocs/commande/list.php
+++ b/htdocs/commande/list.php
@@ -1389,7 +1389,7 @@ if ($resql)
 	}
 
 	// Show total line
-    	if (isset($totalarray['totalhtfield'])
+    if (isset($totalarray['totalhtfield'])
  	   || isset($totalarray['totalvatfield'])
  	   || isset($totalarray['totalttcfield'])
  	   || isset($totalarray['totalamfield'])
diff --git a/htdocs/product/card.php b/htdocs/product/card.php
index 0c6109ba64a9d603be9a943c12137a5d67708897..7797e6802d4afe659df3636e9feb1c5a663b02b5 100644
--- a/htdocs/product/card.php
+++ b/htdocs/product/card.php
@@ -562,7 +562,7 @@ if (empty($reshook))
     {
         if (($object->type == Product::TYPE_PRODUCT && $user->rights->produit->supprimer) || ($object->type == Product::TYPE_SERVICE && $user->rights->service->supprimer))
         {
-            $result = $object->delete($object->id);
+            $result = $object->delete(DolibarrApiAccess::$user);
         }
 
         if ($result > 0)
diff --git a/htdocs/product/class/api_deprecated_product.class.php b/htdocs/product/class/api_deprecated_product.class.php
index 49af8c73123a449f3f5138cea056779d6a10ae09..e2ee97e0100fc18cfca014e3b280cfcb63623d42 100644
--- a/htdocs/product/class/api_deprecated_product.class.php
+++ b/htdocs/product/class/api_deprecated_product.class.php
@@ -339,7 +339,7 @@ class ProductApi extends DolibarrApi
 			throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
 		}
         
-        return $this->product->delete($id);
+        return $this->product->delete(DolibarrApiAccess::$user);
     }
     
     /**
diff --git a/htdocs/product/class/api_products.class.php b/htdocs/product/class/api_products.class.php
index 38b65ddfb4b458fb273081ee1fcd91c5beeba9b0..14885532952bca3825e11aeb66ba1e76966220a6 100644
--- a/htdocs/product/class/api_products.class.php
+++ b/htdocs/product/class/api_products.class.php
@@ -246,7 +246,7 @@ class Products extends DolibarrApi
         global $user;
         $user = DolibarrApiAccess::$user;
 
-        return $this->product->delete($id);
+        return $this->product->delete(DolibarrApiAccess::$user);
     }
     
     /**
diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php
index 5f5b6f6eae988f2e81a1ecf4bf49c58a27abd862..44024025ec00d6f9b104a9480530eee586eb1cd3 100644
--- a/htdocs/product/class/product.class.php
+++ b/htdocs/product/class/product.class.php
@@ -1012,18 +1012,18 @@ class Product extends CommonObject
 	/**
 	 *  Delete a product from database (if not used)
 	 *
-	 *	@param      int		$id         Product id (usage of this is deprecated, delete should be called without parameters on a fetched object)
+	 *	@param      User	$user       Product id (usage of this is deprecated, delete should be called without parameters on a fetched object)
 	 *  @param      int     $notrigger  Do not execute trigger
 	 * 	@return		int					< 0 if KO, 0 = Not possible, > 0 if OK
 	 */
-	function delete($id=0, $notrigger=0)
+	function delete(User $user, $notrigger=0)
 	{
 		// Deprecation warning
 		if ($id > 0) {
 			dol_syslog(__METHOD__ . " with parameter is deprecated", LOG_WARNING);
 		}
 
-		global $conf,$user,$langs;
+		global $conf, $langs;
 		require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
 
 		$error=0;
diff --git a/htdocs/product/dynamic_price/class/price_expression.class.php b/htdocs/product/dynamic_price/class/price_expression.class.php
index bd4fa62692fa53a16c9d1b557615791c4a759380..cd5c8bb25de40e1d5457b379727fb3c3b47b1a6f 100644
--- a/htdocs/product/dynamic_price/class/price_expression.class.php
+++ b/htdocs/product/dynamic_price/class/price_expression.class.php
@@ -283,15 +283,16 @@ class PriceExpression
  	/**
 	 *  Delete object in database
 	 *
-     * 	@param	int		$rowid		 Row id of expression
      *	@param  User	$user        User that deletes
      *  @param  int		$notrigger	 0=launch triggers after, 1=disable triggers
 	 *  @return	int					 <0 if KO, >0 if OK
 	 */
-	function delete($rowid, $user, $notrigger=0)
+	function delete(User $user, $notrigger=0)
 	{
 		$error=0;
 
+		$rowid = $this->id;
+		
 		$this->db->begin();
 
 		if (! $error)
diff --git a/htdocs/product/dynamic_price/editor.php b/htdocs/product/dynamic_price/editor.php
index 055da4a3ad71d5bd3db4868e3b1faa6fe6e3e3fd..bcf18750981e9002368457e8a17cd911841163de 100644
--- a/htdocs/product/dynamic_price/editor.php
+++ b/htdocs/product/dynamic_price/editor.php
@@ -149,7 +149,8 @@ if ($action == 'delete')
 {
 	if ($eid != 0)
 	{
-		$result = $price_expression->delete($eid, $user);
+	    $price_expression->fetch($eid);
+		$result = $price_expression->delete($user);
 		if ($result < 0)
 		{
 			setEventMessages("delete: ".$price_expression->error, $price_expression->errors, 'errors');
diff --git a/htdocs/product/list.php b/htdocs/product/list.php
index 7789849da998a8ce68e0e6cf97aa467cfaca047b..10d819738ca1d442e1499ec71d32c78b097070a0 100644
--- a/htdocs/product/list.php
+++ b/htdocs/product/list.php
@@ -45,7 +45,12 @@ $langs->load("suppliers");
 $langs->load("companies");
 if (! empty($conf->productbatch->enabled)) $langs->load("productbatch");
 
-$action = GETPOST('action');
+$action=GETPOST('action','alpha');
+$massaction=GETPOST('massaction','alpha');
+$show_files=GETPOST('show_files','int');
+$confirm=GETPOST('confirm','alpha');
+$toselect = GETPOST('toselect', 'array');
+
 $sref=GETPOST("sref");
 $sbarcode=GETPOST("sbarcode");
 $snom=GETPOST("snom");
@@ -69,6 +74,8 @@ if (!$_POST) {
 	$search_hidechildproducts = GETPOST('search_hidechildproducts');
 }
 
+$diroutputmassaction=$conf->product->dir_output . '/temp/massgeneration/'.$user->id;
+
 $limit = GETPOST("limit")?GETPOST("limit","int"):$conf->liste_limit;
 $sortfield = GETPOST("sortfield",'alpha');
 $sortorder = GETPOST("sortorder",'alpha');
@@ -209,6 +216,16 @@ if (empty($reshook))
     	$search_accountancy_code_buy='';
     	$search_array_options=array();
     }
+    
+    // Mass actions
+    $objectclass='Product';
+    if ((string) $type == '1') { $objectlabel='Services'; }
+    if ((string) $type == '0') { $objectlabel='Products'; }
+    
+    $permtoread = $user->rights->produit->lire;
+    $permtodelete = $user->rights->produit->supprimer;
+    $uploaddir = $conf->product->dir_output;
+    include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php';    
 }
 
 
@@ -340,6 +357,8 @@ else
     {
     	$num = $db->num_rows($resql);
 
+    	$arrayofselected=is_array($toselect)?$toselect:array();
+    	 
     	if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $sall)
     	{
     	    $obj = $db->fetch_object($resql);
@@ -394,6 +413,15 @@ else
 	        if ($val != '') $param.='&search_options_'.$tmpkey.'='.urlencode($val);
 	    } 	
 		
+	    // List of mass actions available
+	    $arrayofmassactions =  array(
+	        //'presend'=>$langs->trans("SendByMail"),
+	        //'builddoc'=>$langs->trans("PDFMerge"),
+	    );
+	    if ($user->rights->produit->supprimer) $arrayofmassactions['delete']=$langs->trans("Delete");
+	    if ($massaction == 'presend' || $massaction == 'createbills') $arrayofmassactions=array();
+	    $massactionbutton=$form->selectMassAction('', $arrayofmassactions);
+	     
 		print '<form action="'.$_SERVER["PHP_SELF"].'" method="post" name="formulaire">';
         if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
 		print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
@@ -403,7 +431,7 @@ else
 		print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
 		print '<input type="hidden" name="type" value="'.$type.'">';
 
-	    print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_products.png', 0, '', '', $limit);
+	    print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_products.png', 0, '', '', $limit);
 
     	if (! empty($catid))
     	{
@@ -470,7 +498,8 @@ else
 
 			$varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage;
             $selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage);	// This also change content of $arrayfields
-
+            if ($massactionbutton) $selectedfields.=$form->showCheckAddButtons('checkforselect', 1);
+            
             print '<div class="div-table-responsive">';
             print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
     		
@@ -581,8 +610,8 @@ else
 	            print $form->selectarray('tobuy', array('0'=>$langs->trans('ProductStatusNotOnBuyShort'),'1'=>$langs->trans('ProductStatusOnBuyShort')),$tobuy,1);
 	            print '</td>';
     		}
-            print '<td class="liste_titre" align="right">';
-            $searchpitco=$form->showFilterAndCheckAddButtons(0);
+            print '<td class="liste_titre" align="middle">';
+	        $searchpitco=$form->showFilterButtons();
             print $searchpitco;
             print '</td>';
 
@@ -632,14 +661,14 @@ else
     	    $i = 0;
     		while ($i < min($num,$limit))
     		{
-    			$objp = $db->fetch_object($resql);
+    			$obj = $db->fetch_object($resql);
 
     			// Multilangs
     			if (! empty($conf->global->MAIN_MULTILANGS)) // si l'option est active
     			{
     				$sql = "SELECT label";
     				$sql.= " FROM ".MAIN_DB_PREFIX."product_lang";
-    				$sql.= " WHERE fk_product=".$objp->rowid;
+    				$sql.= " WHERE fk_product=".$obj->rowid;
     				$sql.= " AND lang='". $langs->getDefaultLang() ."'";
     				$sql.= " LIMIT 1";
 
@@ -647,22 +676,22 @@ else
     				if ($result)
     				{
     					$objtp = $db->fetch_object($result);
-    					if (! empty($objtp->label)) $objp->label = $objtp->label;
+    					if (! empty($objtp->label)) $obj->label = $objtp->label;
     				}
     			}
 
-    			$product_static->id = $objp->rowid;
-    			$product_static->ref = $objp->ref;
-    			$product_static->ref_fourn = $objp->ref_supplier;
-                $product_static->label = $objp->label;
-    			$product_static->type = $objp->fk_product_type;
-    			$product_static->status_buy = $objp->tobuy;
-                $product_static->status     = $objp->tosell;
-				$product_static->entity = $objp->entity;
+    			$product_static->id = $obj->rowid;
+    			$product_static->ref = $obj->ref;
+    			$product_static->ref_fourn = $obj->ref_supplier;
+                $product_static->label = $obj->label;
+    			$product_static->type = $obj->fk_product_type;
+    			$product_static->status_buy = $obj->tobuy;
+                $product_static->status     = $obj->tosell;
+				$product_static->entity = $obj->entity;
 
 				if (! empty($conf->stock->enabled) && $user->rights->stock->lire && $type != 1)	// To optimize call of load_stock
 				{
-				    if ($objp->fk_product_type != 1)    // Not a service
+				    if ($obj->fk_product_type != 1)    // Not a service
 				    {
 				        $product_static->load_stock('nobatch');             // Load stock_reel + stock_warehouse. This also call load_virtual_stock()
 				    }
@@ -688,27 +717,27 @@ else
     			// Label
 			    if (! empty($arrayfields['p.label']['checked']))
 			    {
-			    	print '<td>'.dol_trunc($objp->label,40).'</td>';
+			    	print '<td>'.dol_trunc($obj->label,40).'</td>';
 			    }
 			    
     			// Barcode
 			    if (! empty($arrayfields['p.barcode']['checked']))
 			    {
-    				print '<td>'.$objp->barcode.'</td>';
+    				print '<td>'.$obj->barcode.'</td>';
     			}
 
     			// Duration
  			    if (! empty($arrayfields['p.duration']['checked']))
     			{
     				print '<td align="center">';
-    				if (preg_match('/([0-9]+)[a-z]/i',$objp->duration))
+    				if (preg_match('/([0-9]+)[a-z]/i',$obj->duration))
     				{
-	    				if (preg_match('/([0-9]+)y/i',$objp->duration,$regs)) print $regs[1].' '.$langs->trans("DurationYear");
-	    				elseif (preg_match('/([0-9]+)m/i',$objp->duration,$regs)) print $regs[1].' '.$langs->trans("DurationMonth");
-	    				elseif (preg_match('/([0-9]+)w/i',$objp->duration,$regs)) print $regs[1].' '.$langs->trans("DurationWeek");
-	    				elseif (preg_match('/([0-9]+)d/i',$objp->duration,$regs)) print $regs[1].' '.$langs->trans("DurationDay");
-	    				//elseif (preg_match('/([0-9]+)h/i',$objp->duration,$regs)) print $regs[1].' '.$langs->trans("DurationHour");
-	    				else print $objp->duration;
+	    				if (preg_match('/([0-9]+)y/i',$obj->duration,$regs)) print $regs[1].' '.$langs->trans("DurationYear");
+	    				elseif (preg_match('/([0-9]+)m/i',$obj->duration,$regs)) print $regs[1].' '.$langs->trans("DurationMonth");
+	    				elseif (preg_match('/([0-9]+)w/i',$obj->duration,$regs)) print $regs[1].' '.$langs->trans("DurationWeek");
+	    				elseif (preg_match('/([0-9]+)d/i',$obj->duration,$regs)) print $regs[1].' '.$langs->trans("DurationDay");
+	    				//elseif (preg_match('/([0-9]+)h/i',$obj->duration,$regs)) print $regs[1].' '.$langs->trans("DurationHour");
+	    				else print $obj->duration;
     				}
     				print '</td>';
     			}
@@ -717,10 +746,10 @@ else
  			    if (! empty($arrayfields['p.sellprice']['checked']))
     			{
     			    print '<td align="right">';
-    			    if ($objp->tosell)
+    			    if ($obj->tosell)
     			    {
-        				if ($objp->price_base_type == 'TTC') print price($objp->price_ttc).' '.$langs->trans("TTC");
-        				else print price($objp->price).' '.$langs->trans("HT");
+        				if ($obj->price_base_type == 'TTC') print price($obj->price_ttc).' '.$langs->trans("TTC");
+        				else print price($obj->price).' '.$langs->trans("HT");
     			    }
         			print '</td>';
     			}
@@ -729,10 +758,10 @@ else
  			    if (! empty($arrayfields['p.minbuyprice']['checked']))
     			{
         			print  '<td align="right">';
-    			    if ($objp->tobuy && $objp->minsellprice != '')
+    			    if ($obj->tobuy && $obj->minsellprice != '')
         			{
-    					//print price($objp->minsellprice).' '.$langs->trans("HT");
-    					if ($product_fourn->find_min_price_product_fournisseur($objp->rowid) > 0)
+    					//print price($obj->minsellprice).' '.$langs->trans("HT");
+    					if ($product_fourn->find_min_price_product_fournisseur($obj->rowid) > 0)
     					{
     						if ($product_fourn->product_fourn_price_id > 0)
     						{
@@ -752,9 +781,9 @@ else
 		        if (! empty($arrayfields['p.seuil_stock_alerte']['checked']))
         		{
                     print '<td align="right">';
-    				if ($objp->fk_product_type != 1)
+    				if ($obj->fk_product_type != 1)
     				{
-                        print $objp->seuil_stock_alerte;
+                        print $obj->seuil_stock_alerte;
     				}
     				print '</td>';
         		}
@@ -762,9 +791,9 @@ else
 		        if (! empty($arrayfields['p.desiredstock']['checked']))
         		{
                     print '<td align="right">';
-    				if ($objp->fk_product_type != 1)
+    				if ($obj->fk_product_type != 1)
     				{
-                        print $objp->desiredstock;
+                        print $obj->desiredstock;
     				}
     				print '</td>';
         		}
@@ -772,9 +801,9 @@ else
 		        if (! empty($arrayfields['p.stock']['checked']))
         		{
    					print '<td align="right">';
-    				if ($objp->fk_product_type != 1)
+    				if ($obj->fk_product_type != 1)
     				{
-   						if ($objp->seuil_stock_alerte != '' && $product_static->stock_reel < (float) $objp->seuil_stock_alerte) print img_warning($langs->trans("StockTooLow")).' ';
+   						if ($obj->seuil_stock_alerte != '' && $product_static->stock_reel < (float) $obj->seuil_stock_alerte) print img_warning($langs->trans("StockTooLow")).' ';
       					print $product_static->stock_reel;
     				}
     				print '</td>';
@@ -783,9 +812,9 @@ else
 		        if (! empty($arrayfields['stock_virtual']['checked']))
         		{
    					print '<td align="right">';
-    				if ($objp->fk_product_type != 1)
+    				if ($obj->fk_product_type != 1)
     				{
-   						if ($objp->seuil_stock_alerte != '' && $product_static->stock_theorique < (float) $objp->seuil_stock_alerte) print img_warning($langs->trans("StockTooLow")).' ';
+   						if ($obj->seuil_stock_alerte != '' && $product_static->stock_theorique < (float) $obj->seuil_stock_alerte) print img_warning($langs->trans("StockTooLow")).' ';
       					print $product_static->stock_theorique;
     				}
     				print '</td>';
@@ -794,13 +823,13 @@ else
 		        if (! empty($arrayfields['p.tobatch']['checked']))
         		{
                     print '<td align="center">';
-    				print yn($objp->tobatch);
+    				print yn($obj->tobatch);
     				print '</td>';
         		}
     			// Accountancy code sell
-		        if (! empty($arrayfields['p.accountancy_code_sell']['checked'])) print '<td>'.$objp->accountancy_code_sell.'</td>';
+		        if (! empty($arrayfields['p.accountancy_code_sell']['checked'])) print '<td>'.$obj->accountancy_code_sell.'</td>';
     			// Accountancy code sell
-		        if (! empty($arrayfields['p.accountancy_code_buy']['checked'])) print '<td>'.$objp->accountancy_code_buy.'</td>';
+		        if (! empty($arrayfields['p.accountancy_code_buy']['checked'])) print '<td>'.$obj->accountancy_code_buy.'</td>';
 		        // Extra fields
 				if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
 				{
@@ -813,7 +842,7 @@ else
 							if ($align) print ' align="'.$align.'"';
 							print '>';
 							$tmpkey='options_'.$key;
-							print $extrafields->showOutputField($key, $objp->$tmpkey, '', 1);
+							print $extrafields->showOutputField($key, $obj->$tmpkey, '', 1);
 							print '</td>';
 						}
 				   }
@@ -826,14 +855,14 @@ else
 		        if (! empty($arrayfields['p.datec']['checked']))
 		        {
 		            print '<td align="center">';
-		            print dol_print_date($objp->date_creation, 'dayhour');
+		            print dol_print_date($obj->date_creation, 'dayhour');
 		            print '</td>';
 		        }
 		        // Date modification
 		        if (! empty($arrayfields['p.tms']['checked']))
 		        {
 		            print '<td align="center">';
-		            print dol_print_date($objp->date_update, 'dayhour');
+		            print dol_print_date($obj->date_update, 'dayhour');
 		            print '</td>';
 		        }    			
     			
@@ -844,7 +873,7 @@ else
 	                if (! empty($conf->use_javascript_ajax) && $user->rights->produit->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
 	                    print ajax_object_onoff($product_static, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
 	                } else {
-	                    print $product_static->LibStatut($objp->tosell,5,0);
+	                    print $product_static->LibStatut($obj->tosell,5,0);
 	                }
 	                print '</td>';
         		}
@@ -855,12 +884,19 @@ else
 	                if (! empty($conf->use_javascript_ajax) && $user->rights->produit->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
 	                    print ajax_object_onoff($product_static, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
 	                } else {
-	                    print $product_static->LibStatut($objp->tobuy,5,1);
+	                    print $product_static->LibStatut($obj->tobuy,5,1);
 	                }
 	                print '</td>';
         		}
-        		// Action	
-                print '<td>&nbsp;</td>';
+        		// Action
+        		print '<td class="nowrap" align="center">';
+        		if ($massactionbutton || $massaction)   // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
+        		{
+        		    $selected=0;
+        		    if (in_array($obj->rowid, $arrayofselected)) $selected=1;
+        		    print '<input id="cb'.$obj->rowid.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$obj->rowid.'"'.($selected?' checked="checked"':'').'>';
+        		}
+        		print '</td>';
 
                 print "</tr>\n";
     			$i++;