Pārlūkot izejas kodu

Merge remote-tracking branch 'upstream/develop' into fieldlabel

Frédéric FRANCE 6 gadi atpakaļ
vecāks
revīzija
09efde4901
100 mainītis faili ar 3871 papildinājumiem un 2679 dzēšanām
  1. 84 2
      ChangeLog
  2. 7 7
      build/makepack-dolibarr.pl
  3. 1 1
      htdocs/accountancy/admin/accountmodel.php
  4. 1 1
      htdocs/accountancy/admin/categories_list.php
  5. 1 1
      htdocs/accountancy/admin/defaultaccounts.php
  6. 1 1
      htdocs/accountancy/admin/export.php
  7. 1 1
      htdocs/accountancy/admin/fiscalyear_card.php
  8. 1 1
      htdocs/accountancy/admin/index.php
  9. 1 1
      htdocs/accountancy/admin/journals_list.php
  10. 1 1
      htdocs/accountancy/admin/productaccount.php
  11. 2 1
      htdocs/accountancy/bookkeeping/balance.php
  12. 3 3
      htdocs/accountancy/bookkeeping/list.php
  13. 1 0
      htdocs/accountancy/class/accountancyexport.class.php
  14. 1 0
      htdocs/accountancy/journal/bankjournal.php
  15. 1 0
      htdocs/accountancy/journal/expensereportsjournal.php
  16. 1 0
      htdocs/accountancy/journal/purchasesjournal.php
  17. 1 0
      htdocs/accountancy/journal/sellsjournal.php
  18. 1 2
      htdocs/accountancy/tpl/export_journal.tpl.php
  19. 2 1
      htdocs/adherents/admin/adherent_emails.php
  20. 1 1
      htdocs/adherents/card.php
  21. 61 4
      htdocs/adherents/class/adherent.class.php
  22. 3 3
      htdocs/adherents/index.php
  23. 1 1
      htdocs/adherents/list.php
  24. 3 1
      htdocs/adherents/subscription/list.php
  25. 1 1
      htdocs/adherents/type.php
  26. 1 1
      htdocs/admin/dict.php
  27. 559 0
      htdocs/admin/emailcollector_card.php
  28. 551 0
      htdocs/admin/emailcollector_list.php
  29. 4 4
      htdocs/admin/holiday.php
  30. 1 1
      htdocs/admin/mails_senderprofile_list.php
  31. 33 1
      htdocs/admin/modules.php
  32. 2 3
      htdocs/api/class/api_documents.class.php
  33. 1 1
      htdocs/asset/card.php
  34. 8 5
      htdocs/asset/class/asset_type.class.php
  35. 1 1
      htdocs/asset/list.php
  36. 40 250
      htdocs/asset/type.php
  37. 8 2
      htdocs/comm/action/class/actioncomm.class.php
  38. 5 5
      htdocs/comm/action/list.php
  39. 1 1
      htdocs/comm/mailing/card.php
  40. 3 2
      htdocs/comm/mailing/list.php
  41. 1 1
      htdocs/comm/propal/list.php
  42. 44 36
      htdocs/comm/remx.php
  43. 4 2
      htdocs/commande/list.php
  44. 1 1
      htdocs/compta/bank/bankentries_list.php
  45. 1 1
      htdocs/compta/bank/list.php
  46. 2 0
      htdocs/compta/facture/card.php
  47. 1 1
      htdocs/compta/facture/fiche-rec.php
  48. 1 1
      htdocs/compta/facture/invoicetemplate_list.php
  49. 1 1
      htdocs/compta/facture/list.php
  50. 1 1
      htdocs/compta/index.php
  51. 1 1
      htdocs/compta/resultat/clientfourn.php
  52. 2 2
      htdocs/compta/salaries/card.php
  53. 57 8
      htdocs/compta/salaries/class/paymentsalary.class.php
  54. 1 1
      htdocs/compta/salaries/document.php
  55. 1 1
      htdocs/compta/salaries/info.php
  56. 2 1
      htdocs/compta/salaries/list.php
  57. 1 1
      htdocs/compta/sociales/card.php
  58. 1 1
      htdocs/compta/sociales/document.php
  59. 1 1
      htdocs/compta/sociales/info.php
  60. 17 12
      htdocs/compta/sociales/list.php
  61. 2 1
      htdocs/contact/list.php
  62. 1 1
      htdocs/contrat/class/contrat.class.php
  63. 1 1
      htdocs/contrat/list.php
  64. 1 1
      htdocs/contrat/services_list.php
  65. 4 0
      htdocs/core/actions_sendmails.inc.php
  66. 22 11
      htdocs/core/class/extrafields.class.php
  67. 9 4
      htdocs/core/class/html.formfile.class.php
  68. 11 5
      htdocs/core/class/html.formprojet.class.php
  69. 2 2
      htdocs/core/class/notify.class.php
  70. 5 5
      htdocs/core/class/translate.class.php
  71. 1 1
      htdocs/core/js/lib_head.js.php
  72. 17 6
      htdocs/core/lib/admin.lib.php
  73. 5 3
      htdocs/core/lib/company.lib.php
  74. 15 16
      htdocs/core/lib/functions.lib.php
  75. 4 4
      htdocs/core/menus/init_menu_auguria.sql
  76. 5 5
      htdocs/core/menus/standard/eldy.lib.php
  77. 2101 2126
      htdocs/core/modules/DolibarrModules.class.php
  78. 1 1
      htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php
  79. 1 1
      htdocs/core/modules/contract/doc/doc_generic_contract_odt.modules.php
  80. 55 38
      htdocs/core/modules/expedition/doc/pdf_rouget.modules.php
  81. 1 1
      htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php
  82. 3 3
      htdocs/core/modules/modAdherent.class.php
  83. 6 18
      htdocs/core/modules/modDataPolicy.class.php
  84. 7 7
      htdocs/core/modules/modEmailCollector.class.php
  85. 2 0
      htdocs/core/modules/modGravatar.class.php
  86. 2 0
      htdocs/core/modules/modLdap.class.php
  87. 6 1
      htdocs/core/modules/modMailing.class.php
  88. 4 0
      htdocs/core/modules/modMailmanSpip.class.php
  89. 4 0
      htdocs/core/modules/modNotification.class.php
  90. 1 1
      htdocs/core/modules/modOauth.class.php
  91. 14 14
      htdocs/core/modules/modPaypal.class.php
  92. 1 1
      htdocs/core/modules/modProduct.class.php
  93. 2 0
      htdocs/core/modules/modSocialNetworks.class.php
  94. 5 5
      htdocs/core/modules/modSociete.class.php
  95. 2 2
      htdocs/core/modules/modStripe.class.php
  96. 1 1
      htdocs/core/modules/product/doc/doc_generic_product_odt.modules.php
  97. 1 1
      htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php
  98. 1 1
      htdocs/core/modules/propale/doc/doc_generic_proposal_odt.modules.php
  99. 1 1
      htdocs/core/modules/stock/doc/pdf_stdmovement.modules.php
  100. 3 4
      htdocs/core/modules/user/doc/doc_generic_user_odt.modules.php

+ 84 - 2
ChangeLog

@@ -8,8 +8,9 @@ For Users:
 NEW: Stable module: Website
 NEW: Stable module: WebDAV
 NEW: Stable module: Module Builder
-NEW: Stable module "Skype" has been replaced with module "Social Networks" to support more tools.
+NEW: Stable module "Skype" has been replaced with module "Social Networks" to support more services.
 NEW: Experimental module "TakePos"
+NEW: Experimental module "Ticket"
 NEW: Dolibarr can provide information in page title when multicompany is enabled of not, making
      Android application like DoliDroid able to provide native features for multicompany module.
 NEW: Compatibility with PHP 7.3
@@ -22,11 +23,92 @@ For developers:
 WARNING:
 
 Following changes may create regressions for some external modules, but were necessary to make Dolibarr better:
-* If you use some links like viewimages.php?modulepart=mycompany&file=... in you external modules, you must
+* If you use some links like viewimages.php?modulepart=mycompany&file=... in your external modules, you must
   replace them with links like viewimages.php?modulepart=mycompany&file=logos/... (note that link change only for
   modulepart=mycompany that now works like others).
+* Hidden option MAIN_PDF_SHIPPING_DISPLAY_AMOUNT_HT has been renamed into SHIPPING_PDF_DISPLAY_AMOUNT_HT
 
 
+***** ChangeLog for 8.0.3 compared to 8.0.2 *****
+FIX: #9161
+FIX: #9432
+FIX: #9432 Assign yourself as a commercial when you don't have permission to see all thirds
+FIX: #9510
+FIX: #9567
+FIX: According to french law, if seller is in France and buyer isn't in UE and isn't a company, TVA used = TVA product
+FIX: Amount when using mutlicurrency on PDF
+FIX: Backup of database without mysqladmin available from cron.
+FIX: Bad label on delete button
+FIX: bad link in notification
+FIX: Bad position of hook formattachOptions call
+FIX: Can't create shipping if have shipping line's extrafields
+FIX: check !empty exclude select element
+FIX: content lost when editing a label with "
+FIX: correct migration of old postgresql unique key
+FIX: credit note progression
+FIX: default accounting accounts on loan creation #9643
+FIX: Delete of draft invoice
+FIX: deletion on draft is allowed if we are allwoed to create
+FIX: Do not show check box if not applicable
+FIX: exclude element of the select
+FIX: extrafields of taks not visible in creation
+FIX: filter on employee
+FIX: invoice stats: situation invoices were not counted
+FIX: keep external module element when adding resource
+FIX: langs fr
+FIX: Link template invoice to contract
+FIX: Look and feel v8. Missing button "Create category"
+FIX: Menu to show/edit Users categories was missing
+FIX: missing name alias field in societe import/export #9091
+FIX: missing symbol for indian rupies
+FIX: Missing transaction around action
+FIX: modify parenting before task deletion
+FIX: nb of session in title
+FIX: need to filter on current entity on replenish
+FIX: number mailing for a contact with multicompany
+FIX: Option for prof id mandatory not working with custom type of company
+FIX: Option MAIN_DISABLE_NOTES_TAB #9611
+FIX: Pagination stats
+FIX: pdf typhon: order reference duplicate
+FIX: position 0 for emails templates
+FIX: previous situation invoice selection
+FIX: Product marge tabs on product card
+FIX: Product margin tab and credit note
+FIX: propal pdf: missing parenthesis for customs code
+FIX: properties on proposal must not be modified if error
+FIX: qty not visible for a lot when making shipment on a dedicated stock
+FIX: Quick hack to solve pb of bad definition of public holidays
+FIX: remain to pay for credit note was wrong on invoice list
+FIX: replenish wasn't caring about supplier price min quantity #9561
+FIX: Required extrafield value numeric should accept '0'
+FIX: ressource list with extrafields
+FIX: restore last seach criteria
+FIX: Selection of addmaindocfile is lost on error
+FIX: Sending of reminder for expired subscriptions
+FIX: shared link ko on proposals
+FIX: showOptionals: column mismatches
+FIX: situation invoice total with credit note
+FIX: situation invoice prev percent
+FIX: special code on create supplier invoice from supplier order
+FIX: Symbol of currency in substitution variables
+FIX: The max size for upload file was not corectly shown
+FIX: the member e-mail on resign and validation.
+FIX: thirdparty property of object not loaded when only one record
+FIX: title
+FIX: Title problem on admin RSS module
+FIX: Tooltip on invoice widget
+FIX: Total of timespent
+FIX: trackid into email sent from member module.
+FIX: translation in select unit form
+FIX: use discount with multicurrency
+FIX: Variable name
+FIX: When we delete a product, llx_product_association rows are not deleted
+FIX: when we're just admin and not super admin, if we create new user with transverse mode, we don't see it then we can't add him in usergroup
+FIX: wrong function name
+FIX: wrong occurence number of contract on contact card, we must only count externals
+FIX: wrong value for module part and return access denied
+FIX: Wrong variable name
+FIX: XSS vulnerability reported by Mary Princy E
 
 ***** ChangeLog for 8.0.2 compared to 8.0.1 *****
 FIX: #8452

+ 7 - 7
build/makepack-dolibarr.pl

@@ -522,10 +522,17 @@ if ($nboftargetok) {
 		$ret=`rm -f  $BUILDROOT/$PROJECT/doc/images/dolibarr_screenshot12.png`;
 
 		# Security to avoid to package data files 
+        print "Remove documents dir\n";
 		$ret=`rm -fr $BUILDROOT/$PROJECT/document`;
 		$ret=`rm -fr $BUILDROOT/$PROJECT/documents`;
 		$ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/document`;
 		$ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/documents`;
+        
+        print "Remove subdir of custom dir\n";
+   	    print "find $BUILDROOT/$PROJECT/htdocs/custom/* -type d -exec rm -fr {} \\;\n";
+   	    $ret=`find $BUILDROOT/$PROJECT/htdocs/custom/* -type d -exec rm -fr {} \\; >/dev/null 2>&1`;	# For custom we want to remove all subdirs but not files
+   	    print "find $BUILDROOT/$PROJECT/htdocs/custom/* -type l -exec rm -fr {} \\;\n";
+   	    $ret=`find $BUILDROOT/$PROJECT/htdocs/custom/* -type l -exec rm -fr {} \\; >/dev/null 2>&1`;	# For custom we want to remove all subdirs, even symbolic links, but not files
 
 		# Removed known external modules to avoid any error when packaging from env where external modules are tested 
 		$ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/allscreens*`;
@@ -591,13 +598,6 @@ if ($nboftargetok) {
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/tecnickcom/tcpdf/tools`;
         $ret=`rm -f  $BUILDROOT/$PROJECT/htdocs/includes/tecnickcom/tcpdf/LICENSE.TXT`;
         $ret=`rm -f  $BUILDROOT/$PROJECT/htdocs/theme/common/octicons/LICENSE`;
-        
-        
-        print "Remove subdir of custom dir\n";
-   	    print "find $BUILDROOT/$PROJECT/htdocs/custom/* -type d -exec rm -fr {} \\;\n";
-   	    $ret=`find $BUILDROOT/$PROJECT/htdocs/custom/* -type d -exec rm -fr {} \\; >/dev/null 2>&1`;	# For custom we want to remove all subdirs but not files
-   	    print "find $BUILDROOT/$PROJECT/htdocs/custom/* -type l -exec rm -fr {} \\;\n";
-   	    $ret=`find $BUILDROOT/$PROJECT/htdocs/custom/* -type l -exec rm -fr {} \\; >/dev/null 2>&1`;	# For custom we want to remove all subdirs, even symbolic links, but not files
 	}
 
 	# Build package for each target

+ 1 - 1
htdocs/accountancy/admin/accountmodel.php

@@ -44,7 +44,7 @@ if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT . '/core
 // Load translation files required by the page
 $langs->loadLangs(array("errors","admin","companies","resource","holiday","compta","accountancy","hrm"));
 
-$action=GETPOST('action','alpha')?GETPOST('action','alpha'):'view';
+$action=GETPOST('action','aZ09')?GETPOST('action','aZ09'):'view';
 $confirm=GETPOST('confirm','alpha');
 $id=31;
 $rowid=GETPOST('rowid','alpha');

+ 1 - 1
htdocs/accountancy/admin/categories_list.php

@@ -34,7 +34,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
 // Load translation files required by the page
 $langs->loadLangs(array("errors","admin","companies","resource","holiday","accountancy","hrm"));
 
-$action=GETPOST('action','alpha')?GETPOST('action','alpha'):'view';
+$action=GETPOST('action','aZ09')?GETPOST('action','aZ09'):'view';
 $confirm=GETPOST('confirm','alpha');
 $id=32;
 $rowid=GETPOST('rowid','alpha');

+ 1 - 1
htdocs/accountancy/admin/defaultaccounts.php

@@ -43,7 +43,7 @@ if (empty($user->rights->accounting->chartofaccount))
 	accessforbidden();
 }
 
-$action = GETPOST('action', 'alpha');
+$action = GETPOST('action', 'aZ09');
 
 
 $list_account_main = array (

+ 1 - 1
htdocs/accountancy/admin/export.php

@@ -41,7 +41,7 @@ if (empty($user->rights->accounting->chartofaccount))
 	accessforbidden();
 }
 
-$action = GETPOST('action', 'alpha');
+$action = GETPOST('action', 'aZ09');
 
 // Parameters ACCOUNTING_EXPORT_*
 $main_option = array (

+ 1 - 1
htdocs/accountancy/admin/fiscalyear_card.php

@@ -38,7 +38,7 @@ if (empty($user->rights->accounting->fiscalyear))
 
 $error = 0;
 
-$action = GETPOST('action', 'alpha');
+$action = GETPOST('action', 'aZ09');
 $confirm = GETPOST('confirm', 'alpha');
 $id = GETPOST('id', 'int');
 

+ 1 - 1
htdocs/accountancy/admin/index.php

@@ -41,7 +41,7 @@ if (empty($user->rights->accounting->chartofaccount))
 	accessforbidden();
 }
 
-$action = GETPOST('action', 'alpha');
+$action = GETPOST('action', 'aZ09');
 
 // Parameters ACCOUNTING_* and others
 $list = array (

+ 1 - 1
htdocs/accountancy/admin/journals_list.php

@@ -34,7 +34,7 @@ require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
 // Load translation files required by the page
 $langs->loadLangs(array("admin","compta","accountancy"));
 
-$action=GETPOST('action','alpha')?GETPOST('action','alpha'):'view';
+$action=GETPOST('action','aZ09')?GETPOST('action','aZ09'):'view';
 $confirm=GETPOST('confirm','alpha');
 $id=35;
 $rowid=GETPOST('rowid','alpha');

+ 1 - 1
htdocs/accountancy/admin/productaccount.php

@@ -45,7 +45,7 @@ if (! $user->rights->accounting->bind->write)
     accessforbidden();
 
 // search & action GETPOST
-$action = GETPOST('action', 'alpha');
+$action = GETPOST('action', 'aZ09');
 $codeventil_buy = GETPOST('codeventil_buy', 'array');
 $codeventil_sell = GETPOST('codeventil_sell', 'array');
 $chk_prod = GETPOST('chk_prod', 'array');

+ 2 - 1
htdocs/accountancy/bookkeeping/balance.php

@@ -41,7 +41,7 @@ $langs->loadLangs(array("accountancy"));
 $page = GETPOST("page");
 $sortorder = GETPOST("sortorder", 'alpha');
 $sortfield = GETPOST("sortfield", 'alpha');
-$action = GETPOST('action', 'alpha');
+$action = GETPOST('action', 'aZ09');
 if (GETPOST("exportcsv",'alpha')) $action = 'export_csv';
 
 // Load variable for pagination
@@ -151,6 +151,7 @@ if ($action == 'export_csv')
 	$sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV;
 
 	$filename = 'balance';
+	$type_export = 'balance';
 	include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php';
 
 	$result = $object->fetchAllBalance($sortorder, $sortfield, 0, 0, $filter);

+ 3 - 3
htdocs/accountancy/bookkeeping/list.php

@@ -36,10 +36,10 @@ require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
 // Load translation files required by the page
 $langs->loadLangs(array("accountancy"));
 
-$action = GETPOST('action', 'alpha');
+$action = GETPOST('action', 'aZ09');
 $search_mvt_num = GETPOST('search_mvt_num', 'int');
-$search_doc_type = GETPOST("search_doc_type");
-$search_doc_ref = GETPOST("search_doc_ref");
+$search_doc_type = GETPOST("search_doc_type", 'alpha');
+$search_doc_ref = GETPOST("search_doc_ref", 'alpha');
 $search_date_start = dol_mktime(0, 0, 0, GETPOST('search_date_startmonth', 'int'), GETPOST('search_date_startday', 'int'), GETPOST('search_date_startyear', 'int'));
 $search_date_end = dol_mktime(0, 0, 0, GETPOST('search_date_endmonth', 'int'), GETPOST('search_date_endday', 'int'), GETPOST('search_date_endyear', 'int'));
 $search_doc_date = dol_mktime(0, 0, 0, GETPOST('doc_datemonth', 'int'), GETPOST('doc_dateday', 'int'), GETPOST('doc_dateyear', 'int'));

+ 1 - 0
htdocs/accountancy/class/accountancyexport.class.php

@@ -218,6 +218,7 @@ class AccountancyExport
 
 		// Define name of file to save
 		$filename = 'general_ledger-'.$this->getFormatCode($conf->global->ACCOUNTING_EXPORT_MODELCSV);
+		$type_export = 'general_ledger';
 
 		include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php';
 

+ 1 - 0
htdocs/accountancy/journal/bankjournal.php

@@ -750,6 +750,7 @@ if ($action == 'exportcsv') {		// ISO and not UTF8 !
 	$sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV;
 
 	$filename = 'journal';
+	$type_export = 'journal';
 	include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php';
 
 	// CSV header line

+ 1 - 0
htdocs/accountancy/journal/expensereportsjournal.php

@@ -429,6 +429,7 @@ $userstatic = new User($db);
 	$sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV;
 
 	$filename = 'journal';
+	$type_export = 'journal';
 	include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php';
 
 	// Model Cegid Expert Export

+ 1 - 0
htdocs/accountancy/journal/purchasesjournal.php

@@ -586,6 +586,7 @@ if ($action == 'exportcsv') {		// ISO and not UTF8 !
 	$sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV;
 
 	$filename = 'journal';
+	$type_export = 'journal';
 	include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php';
 
 	$companystatic = new Fournisseur($db);

+ 1 - 0
htdocs/accountancy/journal/sellsjournal.php

@@ -547,6 +547,7 @@ if ($action == 'exportcsv') {		// ISO and not UTF8 !
 	$sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV;
 
 	$filename = 'journal';
+	$type_export = 'journal';
 	include DOL_DOCUMENT_ROOT . '/accountancy/tpl/export_journal.tpl.php';
 
 	$companystatic = new Client($db);

+ 1 - 2
htdocs/accountancy/tpl/export_journal.tpl.php

@@ -35,9 +35,8 @@ $endaccountingperiod = dol_print_date(dol_now(), '%Y%m%d');
 header('Content-Type: text/csv');
 
 
-if ($conf->global->ACCOUNTING_EXPORT_MODELCSV == "11") // Specific filename for FEC model export
+if ($conf->global->ACCOUNTING_EXPORT_MODELCSV == "11" && $type_export == "general_ledger") // Specific filename for FEC model export into the general ledger
 {
-
 	// FEC format is defined here: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000027804775&cidTexte=LEGITEXT000006069583&dateTexte=20130802&oldAction=rechCodeArticle
 	if (empty($search_date_end))
 	{

+ 2 - 1
htdocs/adherents/admin/adherent_emails.php

@@ -147,8 +147,9 @@ $constantes=array(
 	'ADHERENT_EMAIL_TEMPLATE_AUTOREGISTER'		=>'emailtemplate:member',		/* old was ADHERENT_AUTOREGISTER_MAIL */
 	'ADHERENT_EMAIL_TEMPLATE_MEMBER_VALIDATION'	=>'emailtemplate:member',		/* old was ADHERENT_MAIL_VALID */
 	'ADHERENT_EMAIL_TEMPLATE_SUBSCRIPTION'		=>'emailtemplate:member',		/* old was ADHERENT_MAIL_COTIS */
-	'ADHERENT_EMAIL_TEMPLATE_REMIND_EXPIRATION' =>'emailtemplate:member',
 	'ADHERENT_EMAIL_TEMPLATE_CANCELATION'		=>'emailtemplate:member',		/* old was ADHERENT_MAIL_RESIL */
+	'MEMBER_REMINDER_EMAIL'=>array('type'=>'yesno', 'label'=>$langs->trans('MEMBER_REMINDER_EMAIL', $langs->transnoentities("Module2300Name"))),
+	'ADHERENT_EMAIL_TEMPLATE_REMIND_EXPIRATION' =>'emailtemplate:member',
 );
 
 $helptext='*'.$langs->trans("FollowingConstantsWillBeSubstituted").'<br>';

+ 1 - 1
htdocs/adherents/card.php

@@ -907,7 +907,7 @@ else
 		// Login
 		if (empty($conf->global->ADHERENT_LOGIN_NOT_REQUIRED))
 		{
-			print '<tr><td><span class="fieldrequired">'.$langs->trans("Login").' / '.$langs->trans("Id").'</span></td><td><input type="text" name="member_login" class="minwidth300" maxlength="50" value="'.(isset($_POST["member_login"])?GETPOST("member_login", 'alpha', 2):$object->login).'"></td></tr>';
+			print '<tr><td><span class="fieldrequired">'.$langs->trans("Login").' / '.$langs->trans("Id").'</span></td><td><input type="text" name="member_login" class="minwidth300" maxlength="50" value="'.(isset($_POST["member_login"])?GETPOST("member_login", 'alpha', 2):$object->login).'" autofocus="autofocus"></td></tr>';
 		}
 
 		// Password

+ 61 - 4
htdocs/adherents/class/adherent.class.php

@@ -2644,13 +2644,19 @@ class Adherent extends CommonObject
 
 		$blockingerrormsg = '';
 
-		/*if (empty($conf->global->MEMBER_REMINDER_EMAIL))
+		if (empty($conf->adherent->enabled))	// Should not happen. If module disabled, cron job should not be visible.
+		{
+			$langs->load("agenda");
+			$this->output = $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv("Adherent"));
+			return 0;
+		}
+		if (empty($conf->global->MEMBER_REMINDER_EMAIL))
 		{
 			$langs->load("agenda");
 			$this->output = $langs->trans('EventRemindersByEmailNotEnabled', $langs->transnoentitiesnoconv("Adherent"));
 			return 0;
-		}*/
-
+		}
+		
 		$now = dol_now();
 		$nbok = 0;
 		$nbko = 0;
@@ -2736,7 +2742,58 @@ class Adherent extends CommonObject
 							{
 								$nbok++;
 
-								// TODO Add event email sent for member
+								$message = $msg;
+								$sendto = $to;
+								$sendtocc = '';
+								$sendtobcc = '';
+								$actioncode='EMAIL';
+								$extraparams='';
+
+								$actionmsg='';
+								$actionmsg2=$langs->transnoentities('MailSentBy').' '.CMailFile::getValidAddress($from,4,0,1).' '.$langs->transnoentities('To').' '.CMailFile::getValidAddress($sendto,4,0,1);
+								if ($message)
+								{
+									$actionmsg=$langs->transnoentities('MailFrom').': '.dol_escape_htmltag($from);
+									$actionmsg=dol_concatdesc($actionmsg, $langs->transnoentities('MailTo').': '.dol_escape_htmltag($sendto));
+									if ($sendtocc) $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('Bcc') . ": " . dol_escape_htmltag($sendtocc));
+									$actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('MailTopic') . ": " . $subject);
+									$actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('TextUsedInTheMessageBody') . ":");
+									$actionmsg = dol_concatdesc($actionmsg, $message);
+								}
+								
+								require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
+								
+	    						// Insert record of emails sent
+	    						$actioncomm = new ActionComm($this->db);
+
+	    						$actioncomm->type_code   = 'AC_OTH_AUTO';		// Type of event ('AC_OTH', 'AC_OTH_AUTO', 'AC_XXX'...)
+	    						$actioncomm->code        = 'AC_'.$actioncode;
+	    						$actioncomm->label       = $actionmsg2;
+	    						$actioncomm->note        = $actionmsg;
+	    						$actioncomm->fk_project  = 0;
+	    						$actioncomm->datep       = $now;
+	    						$actioncomm->datef       = $now;
+	    						$actioncomm->percentage  = -1;   // Not applicable
+	    						$actioncomm->socid       = $adherent->thirdparty->id;
+	    						$actioncomm->contactid   = 0;
+	    						$actioncomm->authorid    = $user->id;   // User saving action
+	    						$actioncomm->userownerid = $user->id;	// Owner of action
+	    						// Fields when action is en email (content should be added into note)
+	    						$actioncomm->email_msgid = $cmail->msgid;
+	    						$actioncomm->email_from  = $from;
+	    						$actioncomm->email_sender= '';
+	    						$actioncomm->email_to    = $to;
+	    						$actioncomm->email_tocc  = $sendtocc;
+	    						$actioncomm->email_tobcc = $sendtobcc;
+	    						$actioncomm->email_subject = $subject;
+	    						$actioncomm->errors_to   = '';
+	    						
+	    						$actioncomm->fk_element  = $adherent->id;
+	    						$actioncomm->elementtype = $adherent->element;
+	    						
+	    						$actioncomm->extraparams = $extraparams;
+
+	    						$actioncomm->create($user);
 							}
 						}
 						else

+ 3 - 3
htdocs/adherents/index.php

@@ -55,7 +55,7 @@ $AdherentsResilies=array();
 
 $AdherentType=array();
 
-// Liste les adherents
+// Members list
 $sql = "SELECT t.rowid, t.libelle as label, t.subscription,";
 $sql.= " d.statut, count(d.rowid) as somme";
 $sql.= " FROM ".MAIN_DB_PREFIX."adherent_type as t";
@@ -92,7 +92,7 @@ if ($result)
 
 $now=dol_now();
 
-// List members up to date
+// Members up to date list
 // current rule: uptodate = the end date is in future whatever is type
 // old rule: uptodate = if type does not need payment, that end date is null, if type need payment that end date is in future)
 $sql = "SELECT count(*) as somme , d.fk_adherent_type";
@@ -140,7 +140,7 @@ if (! empty($conf->global->MAIN_SEARCH_FORM_ON_HOME_AREAS))     // This is usele
     	foreach($listofsearchfields as $key => $value)
     	{
     		if ($i == 0) print '<tr class="liste_titre"><td colspan="3">'.$langs->trans("Search").'</td></tr>';
-    		print '<tr '.$bc[false].'>';
+    		print '<tr class="oddeven">';
     		print '<td class="nowrap"><label for="'.$key.'">'.$langs->trans($value["text"]).'</label>:</td><td><input type="text" class="flat inputsearch" name="'.$key.'" id="'.$key.'" size="18"></td>';
     		if ($i == 0) print '<td rowspan="'.count($listofsearchfields).'"><input type="submit" value="'.$langs->trans("Search").'" class="button"></td>';
     		print '</tr>';

+ 1 - 1
htdocs/adherents/list.php

@@ -86,7 +86,7 @@ $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('adherent');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // List of fields to search into when doing a "search in all"
 $fieldstosearchall = array(

+ 3 - 1
htdocs/adherents/subscription/list.php

@@ -53,13 +53,15 @@ $pagenext = $page + 1;
 if (! $sortorder) {  $sortorder="DESC"; }
 if (! $sortfield) {  $sortfield="c.dateadh"; }
 
+$object = new Subscription($db);
+
 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
 $hookmanager->initHooks(array('subscriptionlist'));
 $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('subscription');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // List of fields to search into when doing a "search in all"
 $fieldstosearchall = array(

+ 1 - 1
htdocs/adherents/type.php

@@ -317,7 +317,7 @@ if ($action == 'create')
 	print '<table class="border" width="100%">';
 	print '<tbody>';
 
-	print '<tr><td class="titlefieldcreate fieldrequired">'.$langs->trans("Label").'</td><td><input type="text" name="label" size="40"></td></tr>';
+	print '<tr><td class="titlefieldcreate fieldrequired">'.$langs->trans("Label").'</td><td><input type="text" class="minwidth200" name="label" autofocus="autofocus"></td></tr>';
 
 	print '<tr><td>'.$langs->trans("SubscriptionRequired").'</td><td>';
 	print $form->selectyesno("subscription",1,1);

+ 1 - 1
htdocs/admin/dict.php

@@ -618,7 +618,7 @@ if (GETPOST('actionadd') || GETPOST('actionmodify'))
 		if ($value == 'formula' && empty($_POST['formula'])) continue;
 		if ($value == 'sortorder') continue;		// For a column name 'sortorder', we use the field name 'position'
 		if ((! isset($_POST[$value]) || $_POST[$value]=='')
-        	&& (! in_array($listfield[$f], array('decalage','module','accountancy_code','accountancy_code_sell','accountancy_code_buy'))  // Fields that are not mandatory
+        	&& (! in_array($listfield[$f], array('decalage','module','accountancy_code','accountancy_code_sell','accountancy_code_buy','tracking'))  // Fields that are not mandatory
         	&& (! ($id == 10 && $listfield[$f] == 'code')) // Code is mandatory fir table 10
         	)
 		)

+ 559 - 0
htdocs/admin/emailcollector_card.php

@@ -0,0 +1,559 @@
+<?php
+/* Copyright (C) 2018 Laurent Destailleur  <eldy@users.sourceforge.net>
+ *
+ * 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; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ *   	\file       htdocs/admin/emailcollector_card.php
+ *		\ingroup    emailcollector
+ *		\brief      Page to create/edit/view emailcollector
+ */
+
+require '../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/agenda.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/class/events.class.php';
+
+include_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
+include_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
+include_once DOL_DOCUMENT_ROOT.'/emailcollector/class/emailcollector.class.php';
+include_once DOL_DOCUMENT_ROOT.'/emailcollector/class/emailcollectorfilter.class.php';
+include_once DOL_DOCUMENT_ROOT.'/emailcollector/class/emailcollectoraction.class.php';
+include_once DOL_DOCUMENT_ROOT.'/emailcollector/lib/emailcollector.lib.php';
+
+if (!$user->admin)
+	accessforbidden();
+
+// Load traductions files requiredby by page
+$langs->loadLangs(array("admin", "mails", "other"));
+
+// Get parameters
+$id			= GETPOST('id', 'int');
+$ref        = GETPOST('ref', 'alpha');
+$action		= GETPOST('action', 'aZ09');
+$confirm    = GETPOST('confirm', 'alpha');
+$cancel     = GETPOST('cancel', 'aZ09');
+$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'myobjectcard';   // To manage different context of search
+$backtopage = GETPOST('backtopage', 'alpha');
+
+// Initialize technical objects
+$object = new EmailCollector($db);
+$extrafields = new ExtraFields($db);
+$diroutputmassaction = $conf->emailcollector->dir_output . '/temp/massgeneration/' . $user->id;
+$hookmanager->initHooks(array('emailcollectorcard')); // Note that conf->hooks_modules contains array
+// Fetch optionals attributes and labels
+$extralabels = $extrafields->fetch_name_optionals_label('emailcollector');
+$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
+
+// Initialize array of search criterias
+$search_all = trim(GETPOST("search_all", 'alpha'));
+$search = array();
+foreach ($object->fields as $key => $val) {
+	if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha');
+}
+
+if (empty($action) && empty($id) && empty($ref)) $action='view';
+
+// Load object
+include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php';  // Must be include, not include_once  // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals
+
+// Security check - Protection if external user
+//if ($user->societe_id > 0) access_forbidden();
+//if ($user->societe_id > 0) $socid = $user->societe_id;
+//$isdraft = (($object->statut == MyObject::STATUS_DRAFT) ? 1 : 0);
+//$result = restrictedArea($user, 'mymodule', $object->id, '', '', 'fk_soc', 'rowid', $isdraft);
+
+
+/*
+ * Actions
+ */
+
+$parameters = array();
+$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+
+if (empty($reshook))
+{
+	$error = 0;
+
+	$permissiontoadd=1;
+	$permissiontodelete=1;
+	if (empty($backtopage)) $backtopage = DOL_URL_ROOT.'/admin/emailcollector_card.php?id='.($id > 0 ? $id : '__ID__');
+	$backurlforlist = DOL_URL_ROOT.'/admin/emailcollector_list.php';
+
+	// Actions cancel, add, update, delete or clone
+	include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
+
+	// Actions when linking object each other
+	include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php';		// Must be include, not include_once
+
+	// Actions when printing a doc from card
+	include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
+}
+
+
+if ($action == 'confirm_collect')
+{
+	dol_include_once('/emailcollector/class/emailcollector.class.php');
+
+	$res = $object->doCollect();
+
+	if ($res == 0)
+	{
+		setEventMessages($object->output, null, 'mesgs');
+	}
+	else
+	{
+		setEventMessages($object->error, null, 'errors');
+	}
+
+	$action = '';
+}
+
+if (GETPOST('addfilter','alpha'))
+{
+	$emailcollectorfilter = new EmailCollectorFilter($db);
+	$emailcollectorfilter->type = GETPOST('filtertype','az09');
+	$emailcollectorfilter->rulevalue = GETPOST('rulevalue', 'alpha');
+	$emailcollectorfilter->fk_emailcollector = $object->id;
+	$emailcollectorfilter->status = 1;
+	$result = $emailcollectorfilter->create($user);
+
+	if ($result > 0)
+	{
+		$object->fetchFilters();
+	}
+	else
+	{
+		setEventMessages($emailcollectorfilter->errors, $emailcollectorfilter->error, 'errors');
+	}
+}
+
+if ($action == 'deletefilter')
+{
+	$emailcollectorfilter = new EmailCollectorFilter($db);
+	$emailcollectorfilter->fetch(GETPOST('filterid','int'));
+	$result = $emailcollectorfilter->delete($user);
+	if ($result > 0)
+	{
+		$object->fetchFilters();
+	}
+	else
+	{
+		setEventMessages($emailcollectorfilter->errors, $emailcollectorfilter->error, 'errors');
+	}
+}
+
+if (GETPOST('addoperation','alpha'))
+{
+	$emailcollectoroperation = new EmailCollectorAction($db);
+	$emailcollectoroperation->type = GETPOST('operationtype','az09');
+	$emailcollectoroperation->actionparam = GETPOST('actionparam', 'alpha');
+	$emailcollectoroperation->fk_emailcollector = $object->id;
+	$emailcollectoroperation->status = 1;
+	$result = $emailcollectoroperation->create($user);
+
+	if ($result > 0)
+	{
+		$object->fetchActions();
+	}
+	else
+	{
+		setEventMessages($emailcollectoroperation->errors, $emailcollectoroperation->error, 'errors');
+	}
+}
+
+if ($action == 'deleteoperation')
+{
+	$emailcollectoroperation = new EmailCollectorAction($db);
+	$emailcollectoroperation->fetch(GETPOST('operationid','int'));
+	$result = $emailcollectoroperation->delete($user);
+	if ($result > 0)
+	{
+		$object->fetchActions();
+	}
+	else
+	{
+		setEventMessages($emailcollectoroperation->errors, $emailcollectoroperation->error, 'errors');
+	}
+}
+
+
+
+/*
+ * View
+ */
+
+$form = new Form($db);
+$formfile = new FormFile($db);
+
+llxHeader('', 'EmailCollector', '');
+
+// Example : Adding jquery code
+print '<script type="text/javascript" language="javascript">
+jQuery(document).ready(function() {
+	function init_myfunc()
+	{
+		jQuery("#myid").removeAttr(\'disabled\');
+		jQuery("#myid").attr(\'disabled\',\'disabled\');
+	}
+	init_myfunc();
+	jQuery("#mybutton").click(function() {
+		init_myfunc();
+	});
+});
+</script>';
+
+// Part to create
+if ($action == 'create') {
+	print load_fiche_titre($langs->trans("NewEmailCollector", $langs->transnoentitiesnoconv("EmailCollector")));
+
+	print '<form method="POST" action="' . $_SERVER["PHP_SELF"] . '">';
+	print '<input type="hidden" name="token" value="' . $_SESSION['newtoken'] . '">';
+	print '<input type="hidden" name="action" value="add">';
+	print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
+
+	dol_fiche_head(array(), '');
+
+	print '<table class="border centpercent">'."\n";
+
+	//unset($fields[]);
+
+	// Common attributes
+	include DOL_DOCUMENT_ROOT . '/core/tpl/commonfields_add.tpl.php';
+
+	// Other attributes
+	include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_add.tpl.php';
+
+	print '</table>'."\n";
+
+	dol_fiche_end();
+
+	print '<div class="center">';
+	print '<input type="submit" class="button" name="add" value="' . dol_escape_htmltag($langs->trans("Create")) . '">';
+	print '&nbsp; ';
+	print '<input type="' . ($backtopage ? "submit" : "button") . '" class="button" name="cancel" value="' . dol_escape_htmltag($langs->trans("Cancel")) . '"' . ($backtopage ? '' : ' onclick="javascript:history.go(-1)"') . '>'; // Cancel for create does not post form if we don't know the backtopage
+	print '</div>';
+
+	print '</form>';
+}
+
+// Part to edit record
+if (($id || $ref) && $action == 'edit')
+{
+	print load_fiche_titre($langs->trans("EmailCollector"));
+
+	print '<form method="POST" action="' . $_SERVER["PHP_SELF"] . '">';
+	print '<input type="hidden" name="token" value="' . $_SESSION['newtoken'] . '">';
+	print '<input type="hidden" name="action" value="update">';
+	print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
+	print '<input type="hidden" name="id" value="' . $object->id . '">';
+
+	dol_fiche_head();
+
+	print '<table class="border centpercent">' . "\n";
+
+	// Common attributes
+	include DOL_DOCUMENT_ROOT . '/core/tpl/commonfields_edit.tpl.php';
+
+	// Other attributes
+	include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_edit.tpl.php';
+
+	print '</table>';
+
+	dol_fiche_end();
+
+	print '<div class="center"><input type="submit" class="button" name="save" value="' . $langs->trans("Save") . '">';
+	print ' &nbsp; <input type="submit" class="button" name="cancel" value="' . $langs->trans("Cancel") . '">';
+	print '</div>';
+
+	print '</form>';
+}
+
+// Part to show record
+if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create')))
+{
+	$res = $object->fetch_optionals();
+
+	$object->fetchFilters();
+	$object->fetchActions();
+
+	$head = emailcollectorPrepareHead($object);
+	dol_fiche_head($head, 'card', $langs->trans("EmailCollector"), -1, 'emailcollector');
+
+	$formconfirm = '';
+
+	// Confirmation to delete
+	if ($action == 'delete')
+	{
+		$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('DeleteEmailCollector'), $langs->trans('ConfirmDeleteEmailCollector'), 'confirm_delete', '', 0, 1);
+	}
+
+	// Clone confirmation
+	if ($action == 'clone') {
+		// Create an array for form
+		$formquestion = array();
+		$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('CloneMyObject'), $langs->trans('ConfirmCloneMyObject', $object->ref), 'confirm_clone', $formquestion, 'yes', 1);
+	}
+
+	// Confirmation of action process
+	if ($action == 'collect') {
+		$formquestion = array(
+			'text' => $langs->trans("EmailCollectorConfirmCollect"),
+		);
+		$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('EmailCollectorConfirmCollectTitle'), $text, 'confirm_collect', $formquestion, 0, 1, 220);
+	}
+
+	// Call Hook formConfirm
+	$parameters = array('lineid' => $lineid);
+	$reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
+	if (empty($reshook)) $formconfirm.=$hookmanager->resPrint;
+	elseif ($reshook > 0) $formconfirm=$hookmanager->resPrint;
+
+	// Print form confirm
+	print $formconfirm;
+
+	// Object card
+	// ------------------------------------------------------------
+	$linkback = '<a href="' . dol_buildpath('/admin/emailcollector_list.php', 1) . '?restore_lastsearch_values=1' . (!empty($socid) ? '&socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
+
+	$morehtmlref = '<div class="refidno">';
+	/*
+		// Ref bis
+		$morehtmlref.=$form->editfieldkey("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->emailcollector->creer, 'string', '', 0, 1);
+		$morehtmlref.=$form->editfieldval("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->emailcollector->creer, 'string', '', null, null, '', 1);
+		// Thirdparty
+		$morehtmlref.='<br>'.$langs->trans('ThirdParty') . ' : ' . $soc->getNomUrl(1);
+		// Project
+		if (! empty($conf->projet->enabled))
+		{
+		    $langs->load("projects");
+		    $morehtmlref.='<br>'.$langs->trans('Project') . ' ';
+		    if ($user->rights->emailcollector->creer)
+		    {
+		        if ($action != 'classify')
+		        {
+		            $morehtmlref.='<a href="' . $_SERVER['PHP_SELF'] . '?action=classify&amp;id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetProject')) . '</a> : ';
+		            if ($action == 'classify') {
+		                //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1);
+		                $morehtmlref.='<form method="post" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'">';
+		                $morehtmlref.='<input type="hidden" name="action" value="classin">';
+		                $morehtmlref.='<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+		                $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', $maxlength, 0, 1, 0, 1, 0, 0, '', 1);
+		                $morehtmlref.='<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
+		                $morehtmlref.='</form>';
+		            } else {
+		                $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1);
+		            }
+		        }
+		    } else {
+		        if (! empty($object->fk_project)) {
+		            $proj = new Project($db);
+		            $proj->fetch($object->fk_project);
+		            $morehtmlref.='<a href="'.DOL_URL_ROOT.'/projet/card.php?id=' . $object->fk_project . '" title="' . $langs->trans('ShowProject') . '">';
+		            $morehtmlref.=$proj->ref;
+		            $morehtmlref.='</a>';
+		        } else {
+		            $morehtmlref.='';
+		        }
+		    }
+		}
+	*/
+	$morehtmlref .= '</div>';
+
+	dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
+
+	print '<div class="fichecenter">';
+	print '<div class="fichehalfleft">';
+	print '<div class="underbanner clearboth"></div>';
+	print '<table class="border centpercent">'."\n";
+
+	// Common attributes
+	//$keyforbreak='fieldkeytoswithonsecondcolumn';
+	include DOL_DOCUMENT_ROOT . '/core/tpl/commonfields_view.tpl.php';
+
+	// Other attributes
+	include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php';
+
+	print '</table>';
+
+
+	print '<form method="POST" action="' . $_SERVER["PHP_SELF"] . '">';
+	print '<input type="hidden" name="token" value="' . $_SESSION['newtoken'] . '">';
+	print '<input type="hidden" name="action" value="updatefiltersactions">';
+	print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
+	print '<input type="hidden" name="id" value="' . $object->id . '">';
+
+	// Filters
+	print '<table class="border centpercent">';
+	print '<tr class="liste_titre">';
+	print '<td>'.$langs->trans("Filters").'</td><td></td><td></td>';
+	print '</tr>';
+	// Add filter
+	print '<tr class="oddeven">';
+	print '<td>';
+	$arrayoftypes=array('from'=>'MailFrom', 'to'=>'MailTo', 'cc'=>'Cc', 'bcc'=>'Bcc', 'subject'=>'Subject', 'body'=>'Body', 'seen'=>'AlreadyRead', 'unseen'=>'NotRead');
+	print $form->selectarray('filtertype', $arrayoftypes, '', 1, 0, 0, '', 1);
+	print '</td><td>';
+	print '<input type="text" name="rulevalue">';
+	print '</td>';
+	print '<td align="right"><input type="submit" name="addfilter" id="addfilter" class="flat button" value="'.$langs->trans("Add").'"></td>';
+	print '</tr>';
+	// List filters
+	foreach($object->filters as $rulefilter)
+	{
+		$rulefilterobj=new EmailCollectorFilter($db);
+		$rulefilterobj->fetch($rulefilter['id']);
+
+		print '<tr class="oddeven">';
+		print '<td>';
+		print $langs->trans($arrayoftypes[$rulefilter['type']]);
+		print '</td>';
+		print '<td>'.$rulefilter['rulevalue'].'</td>';
+		print '<td align="right">';
+		//print $rulefilterobj->getLibStatut(3);
+		print ' <a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=deletefilter&filterid='.$rulefilter['id'].'">'.img_delete().'</a>';
+		print '</td>';
+		print '</tr>';
+	}
+
+	print '</tr>';
+	print '</table>';
+
+	print '<div class="clearboth"></div><br>';
+
+	// Operations
+	print '<table class="border centpercent">';
+	print '<tr class="liste_titre">';
+	print '<td>'.$langs->trans("EmailcollectorOperations").'</td><td></td><td></td>';
+	print '</tr>';
+	// Add operation
+	print '<tr class="oddeven">';
+	print '<td>';
+	$arrayoftypes=array('recordevent'=>'RecordEvent');
+	if ($conf->projet->enabled) $arrayoftypes['project']='CreateLeadAndThirdParty';
+	print $form->selectarray('operationtype', $arrayoftypes, '', 0, 0, 0, '', 1);
+	print '</td><td>';
+	print '<input type="text" name="operationparam">';
+	print '</td>';
+	print '<td align="right"><input type="submit" name="addoperation" id="addoperation" class="flat button" value="'.$langs->trans("Add").'"></td>';
+	print '</tr>';
+	// List operations
+	foreach($object->actions as $ruleaction)
+	{
+		$ruleactionobj=new EmailcollectorAction($db);
+		$ruleactionobj->fetch($ruleaction['id']);
+
+		print '<tr class="oddeven">';
+		print '<td>';
+		print $langs->trans($arrayoftypes[$ruleaction['type']]);
+		print '</td>';
+		print '<td>'.$ruleaction['actionparam'].'</td>';
+		print '<td align="right">';
+		//print $ruleactionobj->getLibStatut(3);
+		print ' <a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=deleteoperation&operationid='.$ruleaction['id'].'">'.img_delete().'</a>';
+		print '</td>';
+		print '</tr>';
+	}
+
+	print '</tr>';
+	print '</table>';
+
+	print '</form>';
+
+	print '</div>';
+	print '</div>';
+
+
+	print '<div class="clearboth"></div><br>';
+
+	dol_fiche_end();
+
+	// Buttons for actions
+	if ($action != 'presend' && $action != 'editline') {
+		print '<div class="tabsAction">' . "\n";
+		$parameters = array();
+		$reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
+		if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+
+		if (empty($reshook))
+		{
+			print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&amp;action=edit">' . $langs->trans("Edit") . '</a>' . "\n";
+
+			print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&amp;action=collect">' . $langs->trans("CollectNow") . '</a>' . "\n";
+
+			print '<a class="butActionDelete" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&amp;action=delete">' . $langs->trans('Delete') . '</a>' . "\n";
+		}
+		print '</div>' . "\n";
+	}
+
+	// Select mail models is same action as presend
+	if (GETPOST('modelselected')) {
+		$action = 'presend';
+	}
+
+	/*
+	if ($action != 'presend') {
+		print '<div class="fichecenter"><div class="fichehalfleft">';
+		print '<a name="builddoc"></a>'; // ancre
+	*/
+		// Documents
+		/*$objref = dol_sanitizeFileName($object->ref);
+	    $relativepath = $comref . '/' . $comref . '.pdf';
+	    $filedir = $conf->emailcollector->dir_output . '/' . $objref;
+	    $urlsource = $_SERVER["PHP_SELF"] . "?id=" . $object->id;
+	    $genallowed = $user->rights->emailcollector->read;	// If you can read, you can build the PDF to read content
+	    $delallowed = $user->rights->emailcollector->create;	// If you can create/edit, you can remove a file on card
+	    print $formfile->showdocuments('emailcollector', $objref, $filedir, $urlsource, $genallowed, $delallowed, $object->modelpdf, 1, 0, 0, 28, 0, '', '', '', $soc->default_lang);
+		*/
+	/*
+		// Show links to link elements
+		$linktoelem = $form->showLinkToObjectBlock($object, null, array('emailcollector'));
+		$somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
+
+		print '</div><div class="fichehalfright"><div class="ficheaddleft">';
+
+		$MAXEVENT = 10;
+
+		$morehtmlright = '<a href="' . dol_buildpath('/emailcollector/emailcollector_info.php', 1) . '?id=' . $object->id . '">';
+		$morehtmlright .= $langs->trans("SeeAll");
+		$morehtmlright .= '</a>';
+
+		// List of actions on element
+		include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
+		$formactions = new FormActions($db);
+		$somethingshown = $formactions->showactions($object, 'emailcollector_emailcollector', $socid, 1, '', $MAXEVENT, '', $morehtmlright);
+
+		print '</div></div></div>';
+	}
+	*/
+
+	//Select mail models is same action as presend
+	/*
+	 if (GETPOST('modelselected')) $action = 'presend';
+
+	 // Presend form
+	 $modelmail='inventory';
+	 $defaulttopic='InformationMessage';
+	 $diroutput = $conf->product->dir_output.'/inventory';
+	 $trackid = 'stockinv'.$object->id;
+
+	 include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php';
+	 */
+}
+
+// End of page
+llxFooter();
+$db->close();

+ 551 - 0
htdocs/admin/emailcollector_list.php

@@ -0,0 +1,551 @@
+<?php
+/* Copyright (C) 2007-2017 Laurent Destailleur  <eldy@users.sourceforge.net>
+ *
+ * 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; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ *      \file       htdocs/admin/emailcollector_list.php
+ *      \ingroup    emailcollector
+ *      \brief      List page for emailcollector
+ */
+
+require '../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/agenda.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/class/events.class.php';
+
+require_once DOL_DOCUMENT_ROOT . '/core/class/html.formcompany.class.php';
+require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
+require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
+dol_include_once('/emailcollector/class/emailcollector.class.php');
+
+if (!$user->admin) accessforbidden();
+
+// Load traductions files requiredby by page
+$langs->loadLangs(array("admin", "other"));
+
+$action     = GETPOST('action','aZ09')?GETPOST('action','aZ09'):'view';				// The action 'add', 'create', 'edit', 'update', 'view', ...
+$massaction = GETPOST('massaction','alpha');											// The bulk action (combo box choice into lists)
+$show_files = GETPOST('show_files','int');												// Show files area generated by bulk actions ?
+$confirm    = GETPOST('confirm','alpha');												// Result of a confirmation
+$cancel     = GETPOST('cancel', 'alpha');												// We click on a Cancel button
+$toselect   = GETPOST('toselect', 'array');												// Array of ids of elements selected into a list
+$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'emailcollectorlist';   // To manage different context of search
+$backtopage = GETPOST('backtopage','alpha');											// Go back to a dedicated page
+$optioncss  = GETPOST('optioncss','aZ');												// Option for the css output (always '' except when 'print')
+
+$id			= GETPOST('id','int');
+
+// Load variable for pagination
+$limit = GETPOST('limit','int')?GETPOST('limit','int'):$conf->liste_limit;
+$sortfield = GETPOST('sortfield','alpha');
+$sortorder = GETPOST('sortorder','alpha');
+$page = GETPOST('page','int');
+if (empty($page) || $page == -1 || GETPOST('button_search','alpha') || GETPOST('button_removefilter','alpha') || (empty($toselect) && $massaction === '0')) { $page = 0; }     // If $page is not defined, or '' or -1 or if we click on clear filters or if we select empty mass action
+$offset = $limit * $page;
+$pageprev = $page - 1;
+$pagenext = $page + 1;
+//if (! $sortfield) $sortfield="p.date_fin";
+//if (! $sortorder) $sortorder="DESC";
+
+// Initialize technical objects
+$object = new EmailCollector($db);
+$extrafields = new ExtraFields($db);
+$diroutputmassaction = $conf->emailcollector->dir_output . '/temp/massgeneration/' . $user->id;
+$hookmanager->initHooks(array('emailcollectorlist')); // Note that conf->hooks_modules contains array
+// Fetch optionals attributes and labels
+$extralabels = $extrafields->fetch_name_optionals_label('emailcollector');
+$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
+
+// Default sort order (if not yet defined by previous GETPOST)
+if (! $sortfield) $sortfield="t.".key($object->fields);   // Set here default search field. By default 1st field in definition.
+if (! $sortorder) $sortorder="ASC";
+
+// Security check
+$socid=0;
+if ($user->societe_id > 0)	// Protection if external user
+{
+	//$socid = $user->societe_id;
+	accessforbidden();
+}
+//$result = restrictedArea($user, 'emailcollector', $id, '');
+
+// Initialize array of search criterias
+$search_all=trim(GETPOST("search_all",'alpha'));
+$search=array();
+foreach($object->fields as $key => $val)
+{
+	if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha');
+}
+
+// List of fields to search into when doing a "search in all"
+$fieldstosearchall = array();
+foreach($object->fields as $key => $val)
+{
+	if ($val['searchall']) $fieldstosearchall['t.'.$key]=$val['label'];
+}
+
+// Definition of fields for list
+$arrayfields=array();
+foreach($object->fields as $key => $val)
+{
+	// If $val['visible']==0, then we never show the field
+	if (! empty($val['visible'])) $arrayfields['t.'.$key]=array('label'=>$val['label'], 'checked'=>(($val['visible']<0)?0:1), 'enabled'=>$val['enabled'], 'position'=>$val['position']);
+}
+// Extra fields
+if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0)
+{
+	foreach($extrafields->attributes[$object->table_element]['label'] as $key => $val)
+	{
+		if (! empty($extrafields->attributes[$object->table_element]['list'][$key]))
+			$arrayfields["ef.".$key]=array('label'=>$extrafields->attributes[$object->table_element]['label'][$key], 'checked'=>(($extrafields->attributes[$object->table_element]['list'][$key]<0)?0:1), 'position'=>$extrafields->attributes[$object->table_element]['pos'][$key], 'enabled'=>(abs($extrafields->attributes[$object->table_element]['list'][$key])!=3 && $extrafields->attributes[$object->table_element]['perms'][$key]));
+	}
+}
+$object->fields = dol_sort_array($object->fields, 'position');
+$arrayfields = dol_sort_array($arrayfields, 'position');
+
+
+
+/*
+ * Actions
+ *
+ * Put here all code to do according to value of "$action" parameter
+ */
+
+if (GETPOST('cancel','alpha')) { $action='list'; $massaction=''; }
+if (! GETPOST('confirmmassaction','alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction=''; }
+
+$parameters=array();
+$reshook=$hookmanager->executeHooks('doActions', $parameters, $object, $action);    // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+
+if (empty($reshook))
+{
+	// Selection of new fields
+	include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
+
+	// Purge search criteria
+	if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') ||GETPOST('button_removefilter','alpha')) // All tests are required to be compatible with all browsers
+	{
+		foreach($object->fields as $key => $val)
+		{
+			$search[$key]='';
+		}
+		$toselect='';
+		$search_array_options=array();
+	}
+	if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') || GETPOST('button_removefilter','alpha')
+		|| GETPOST('button_search_x','alpha') || GETPOST('button_search.x','alpha') || GETPOST('button_search','alpha'))
+	{
+		$massaction='';     // Protection to avoid mass action if we force a new search during a mass action confirmation
+	}
+
+	// Mass actions
+	$objectclass='EmailCollector';
+	$objectlabel='EmailCollector';
+	$permtoread = $user->rights->emailcollector->read;
+	$permtodelete = $user->rights->emailcollector->delete;
+	$uploaddir = $conf->emailcollector->dir_output;
+	include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php';
+}
+
+
+
+/*
+ * View
+ *
+ * Put here all code to render page
+ */
+
+$form=new Form($db);
+
+$now=dol_now();
+
+//$help_url="EN:Module_EmailCollector|FR:Module_EmailCollector_FR|ES:Módulo_EmailCollector";
+$help_url='';
+$title = $langs->trans('ListOf', $langs->transnoentitiesnoconv("EmailCollector"));
+
+
+// Build and execute select
+// --------------------------------------------------------------------
+$sql = 'SELECT ';
+foreach($object->fields as $key => $val)
+{
+	$sql.='t.'.$key.', ';
+}
+// Add fields from extrafields
+if (! empty($extrafields->attributes[$object->table_element]['label']))
+	foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? "ef.".$key.' as options_'.$key.', ' : '');
+// Add fields from hooks
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldListSelect', $parameters, $object);    // Note that $action and $object may have been modified by hook
+$sql.=$hookmanager->resPrint;
+$sql=preg_replace('/, $/','', $sql);
+$sql.= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t";
+if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (t.rowid = ef.fk_object)";
+if ($object->ismultientitymanaged == 1) $sql.= " WHERE t.entity IN (".getEntity($object->element).")";
+else $sql.=" WHERE 1 = 1";
+foreach($search as $key => $val)
+{
+	if ($key == 'status' && $search[$key] == -1) continue;
+	$mode_search=(($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key]))?1:0);
+	if ($search[$key] != '') $sql.=natural_search($key, $search[$key], (($key == 'status')?2:$mode_search));
+}
+if ($search_all) $sql.= natural_search(array_keys($fieldstosearchall), $search_all);
+// Add where from extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
+// Add where from hooks
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldListWhere', $parameters, $object);    // Note that $action and $object may have been modified by hook
+$sql.=$hookmanager->resPrint;
+
+/* If a group by is required
+ $sql.= " GROUP BY "
+ foreach($object->fields as $key => $val)
+ {
+ $sql.='t.'.$key.', ';
+ }
+ // Add fields from extrafields
+ if (! empty($extrafields->attributes[$object->table_element]['label'])) {
+ foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? "ef.".$key.', ' : '');
+ // Add where from hooks
+ $parameters=array();
+ $reshook=$hookmanager->executeHooks('printFieldListGroupBy',$parameters);    // Note that $action and $object may have been modified by hook
+ $sql.=$hookmanager->resPrint;
+ $sql=preg_replace('/, $/','', $sql);
+ */
+
+$sql.=$db->order($sortfield,$sortorder);
+
+// Count total nb of records
+$nbtotalofrecords = '';
+if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
+{
+	$resql = $db->query($sql);
+	$nbtotalofrecords = $db->num_rows($resql);
+	if (($page * $limit) > $nbtotalofrecords)	// if total of record found is smaller than page * limit, goto and load page 0
+	{
+		$page = 0;
+		$offset = 0;
+	}
+}
+// if total of record found is smaller than limit, no need to do paging and to restart another select with limits set.
+if (is_numeric($nbtotalofrecords) && $limit > $nbtotalofrecords)
+{
+	$num = $nbtotalofrecords;
+}
+else
+{
+	$sql.= $db->plimit($limit+1, $offset);
+
+	$resql=$db->query($sql);
+	if (! $resql)
+	{
+		dol_print_error($db);
+		exit;
+	}
+
+	$num = $db->num_rows($resql);
+}
+
+// Direct jump if only one record found
+if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $search_all)
+{
+	$obj = $db->fetch_object($resql);
+	$id = $obj->rowid;
+	header("Location: ".DOL_URL_ROOT.'/emailcollector/emailcollector_card.php?id='.$id);
+	exit;
+}
+
+
+// Output page
+// --------------------------------------------------------------------
+
+llxHeader('', $title, $help_url);
+
+// Example : Adding jquery code
+print '<script type="text/javascript" language="javascript">
+jQuery(document).ready(function() {
+	function init_myfunc()
+	{
+		jQuery("#myid").removeAttr(\'disabled\');
+		jQuery("#myid").attr(\'disabled\',\'disabled\');
+	}
+	init_myfunc();
+	jQuery("#mybutton").click(function() {
+		init_myfunc();
+	});
+});
+</script>';
+
+$arrayofselected=is_array($toselect)?$toselect:array();
+
+$param='';
+if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage);
+if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit);
+foreach($search as $key => $val)
+{
+	$param.= '&search_'.$key.'='.urlencode($search[$key]);
+}
+if ($optioncss != '')     $param.='&optioncss='.urlencode($optioncss);
+// Add $param from extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
+
+// List of mass actions available
+$arrayofmassactions =  array(
+//'presend'=>$langs->trans("SendByMail"),
+//'builddoc'=>$langs->trans("PDFMerge"),
+);
+if ($user->rights->emailcollector->delete) $arrayofmassactions['predelete']=$langs->trans("Delete");
+if (GETPOST('nomassaction','int') || in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array();
+$massactionbutton=$form->selectMassAction('', $arrayofmassactions);
+
+print '<form method="POST" id="searchFormList" action="'.$_SERVER["PHP_SELF"].'">';
+if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
+print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
+print '<input type="hidden" name="action" value="list">';
+print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
+print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
+print '<input type="hidden" name="page" value="'.$page.'">';
+print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
+
+$newcardbutton='';
+//if ($user->rights->emailcollector->creer)
+	//{
+$newcardbutton='<a class="butActionNew" href="emailcollector_card.php?action=create&backtopage='.urlencode($_SERVER['PHP_SELF']).'"><span class="valignmiddle">'.$langs->trans('New').'</span>';
+$newcardbutton.= '<span class="fa fa-plus-circle valignmiddle"></span>';
+$newcardbutton.= '</a>';
+//}
+
+print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_companies', 0, $newcardbutton, '', $limit);
+
+// Add code for pre mass action (confirmation or email presend form)
+include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php';
+
+if ($sall)
+{
+	foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val);
+	print '<div class="divsearchfieldfilter">'.$langs->trans("FilterOnInto", $sall) . join(', ',$fieldstosearchall).'</div>';
+}
+
+$moreforfilter = '';
+/*$moreforfilter.='<div class="divsearchfield">';
+ $moreforfilter.= $langs->trans('MyFilter') . ': <input type="text" name="search_myfield" value="'.dol_escape_htmltag($search_myfield).'">';
+ $moreforfilter.= '</div>';*/
+
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object);    // Note that $action and $object may have been modified by hook
+if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint;
+else $moreforfilter = $hookmanager->resPrint;
+
+if (! empty($moreforfilter))
+{
+	print '<div class="liste_titre liste_titre_bydiv centpercent">';
+	print $moreforfilter;
+	print '</div>';
+}
+
+$varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage;
+$selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage);	// This also change content of $arrayfields
+$selectedfields.=(count($arrayofmassactions) ? $form->showCheckAddButtons('checkforselect', 1) : '');
+
+print '<div class="div-table-responsive">';		// You can use div-table-responsive-no-min if you dont need reserved height for your table
+print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
+
+
+// Fields title search
+// --------------------------------------------------------------------
+print '<tr class="liste_titre">';
+foreach($object->fields as $key => $val)
+{
+	$align='';
+	if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center';
+	if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap';
+	if ($key == 'status') $align.=($align?' ':'').'center';
+	if (! empty($arrayfields['t.'.$key]['checked'])) print '<td class="liste_titre'.($align?' '.$align:'').'"><input type="text" class="flat maxwidth75" name="search_'.$key.'" value="'.dol_escape_htmltag($search[$key]).'"></td>';
+}
+// Extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php';
+
+// Fields from hook
+$parameters=array('arrayfields'=>$arrayfields);
+$reshook=$hookmanager->executeHooks('printFieldListOption', $parameters, $object);    // Note that $action and $object may have been modified by hook
+print $hookmanager->resPrint;
+// Action column
+print '<td class="liste_titre" align="right">';
+$searchpicto=$form->showFilterButtons();
+print $searchpicto;
+print '</td>';
+print '</tr>'."\n";
+
+
+// Fields title label
+// --------------------------------------------------------------------
+print '<tr class="liste_titre">';
+foreach($object->fields as $key => $val)
+{
+	$align='';
+	if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center';
+	if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap';
+	if ($key == 'status') $align.=($align?' ':'').'center';
+	if (! empty($arrayfields['t.'.$key]['checked'])) print getTitleFieldOfList($arrayfields['t.'.$key]['label'], 0, $_SERVER['PHP_SELF'], 't.'.$key, '', $param, ($align?'class="'.$align.'"':''), $sortfield, $sortorder, $align.' ')."\n";
+}
+// Extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php';
+// Hook fields
+$parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder);
+$reshook=$hookmanager->executeHooks('printFieldListTitle', $parameters, $object);    // Note that $action and $object may have been modified by hook
+print $hookmanager->resPrint;
+print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"],'','','','align="center"',$sortfield,$sortorder,'maxwidthsearch ')."\n";
+print '</tr>'."\n";
+
+
+// Detect if we need a fetch on each output line
+$needToFetchEachLine=0;
+if (is_array($extrafields->attributes[$object->table_element]['computed']) && count($extrafields->attributes[$object->table_element]['computed']) > 0)
+{
+	foreach ($extrafields->attributes[$object->table_element]['computed'] as $key => $val)
+	{
+		if (preg_match('/\$object/',$val)) $needToFetchEachLine++;  // There is at least one compute field that use $object
+	}
+}
+
+
+// Loop on record
+// --------------------------------------------------------------------
+$i=0;
+$totalarray=array();
+while ($i < min($num, $limit))
+{
+	$obj = $db->fetch_object($resql);
+	if (empty($obj)) break;		// Should not happen
+
+	// Store properties in $object
+	$object->id = $obj->rowid;
+	foreach($object->fields as $key => $val)
+	{
+		if (isset($obj->$key)) $object->$key = $obj->$key;
+	}
+
+	// Show here line of result
+	print '<tr class="oddeven">';
+	foreach($object->fields as $key => $val)
+	{
+		$align='';
+		if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center';
+		if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap';
+		if ($key == 'status') $align.=($align?' ':'').'center';
+		if (! empty($arrayfields['t.'.$key]['checked']))
+		{
+			print '<td';
+			if ($align) print ' class="'.$align.'"';
+			print '>';
+			print $object->showOutputField($val, $key, $obj->$key, '');
+			print '</td>';
+			if (! $i) $totalarray['nbfield']++;
+			if (! empty($val['isameasure']))
+			{
+				if (! $i) $totalarray['pos'][$totalarray['nbfield']]='t.'.$key;
+				$totalarray['val']['t.'.$key] += $obj->$key;
+			}
+		}
+	}
+	// Extra fields
+	include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php';
+	// Fields from hook
+	$parameters=array('arrayfields'=>$arrayfields, 'obj'=>$obj);
+	$reshook=$hookmanager->executeHooks('printFieldListValue', $parameters, $object);    // Note that $action and $object may have been modified by hook
+	print $hookmanager->resPrint;
+	// Action column
+	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>';
+	if (! $i) $totalarray['nbfield']++;
+
+	print '</tr>';
+
+	$i++;
+}
+
+// Show total line
+if (isset($totalarray['pos']))
+{
+	print '<tr class="liste_total">';
+	$i=0;
+	while ($i < $totalarray['nbfield'])
+	{
+		$i++;
+		if (! empty($totalarray['pos'][$i]))  print '<td align="right">'.price($totalarray['val'][$totalarray['pos'][$i]]).'</td>';
+		else
+		{
+			if ($i == 1)
+			{
+				if ($num < $limit) print '<td align="left">'.$langs->trans("Total").'</td>';
+				else print '<td align="left">'.$langs->trans("Totalforthispage").'</td>';
+			}
+			else print '<td></td>';
+		}
+	}
+	print '</tr>';
+}
+
+// If no record found
+if ($num == 0)
+{
+	$colspan=1;
+	foreach($arrayfields as $key => $val) { if (! empty($val['checked'])) $colspan++; }
+	print '<tr><td colspan="'.$colspan.'" class="opacitymedium">'.$langs->trans("NoRecordFound").'</td></tr>';
+}
+
+
+$db->free($resql);
+
+$parameters=array('arrayfields'=>$arrayfields, 'sql'=>$sql);
+$reshook=$hookmanager->executeHooks('printFieldListFooter', $parameters, $object);    // Note that $action and $object may have been modified by hook
+print $hookmanager->resPrint;
+
+print '</table>'."\n";
+print '</div>'."\n";
+
+print '</form>'."\n";
+
+if (in_array('builddoc',$arrayofmassactions) && ($nbtotalofrecords === '' || $nbtotalofrecords))
+{
+	$hidegeneratedfilelistifempty=1;
+	if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files) $hidegeneratedfilelistifempty=0;
+
+	require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
+	$formfile = new FormFile($db);
+
+	// Show list of available documents
+	$urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder;
+	$urlsource.=str_replace('&amp;','&',$param);
+
+	$filedir=$diroutputmassaction;
+	$genallowed=$user->rights->emailcollector->read;
+	$delallowed=$user->rights->emailcollector->create;
+
+	print $formfile->showdocuments('massfilesarea_emailcollector','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,'','','',null,$hidegeneratedfilelistifempty);
+}
+
+// End of page
+llxFooter();
+$db->close();

+ 4 - 4
htdocs/admin/holiday.php

@@ -75,8 +75,8 @@ else if ($action == 'specimen') // For contract
 {
 	$modele= GETPOST('module','alpha');
 
-	$contract = new Contrat($db);
-	$contract->initAsSpecimen();
+	$holiday = new Holiday($db);
+	$holiday->initAsSpecimen();
 
 	// Search template files
 	$file=''; $classname=''; $filefound=0;
@@ -98,7 +98,7 @@ else if ($action == 'specimen') // For contract
 
 		$module = new $classname($db);
 
-		if ($module->write_file($contract,$langs) > 0)
+		if ($module->write_file($holiday,$langs) > 0)
 		{
 			header("Location: ".DOL_URL_ROOT."/document.php?modulepart=holiday&file=SPECIMEN.pdf");
 			return;
@@ -271,7 +271,7 @@ foreach ($dirmodels as $reldir)
 						// Info
 						$htmltooltip='';
 						$htmltooltip.=''.$langs->trans("Version").': <b>'.$module->getVersion().'</b><br>';
-						$nextval=$module->getNextValue($mysoc,$contract);
+						$nextval=$module->getNextValue($mysoc,$holiday);
                         if ("$nextval" != $langs->trans("NotAvailable")) {  // Keep " on nextval
                             $htmltooltip.=''.$langs->trans("NextValue").': ';
                             if ($nextval) {

+ 1 - 1
htdocs/admin/mails_senderprofile_list.php

@@ -61,7 +61,7 @@ $diroutputmassaction=$conf->admin->dir_output . '/temp/massgeneration/'.$user->i
 $hookmanager->initHooks(array('emailsenderprofilelist'));     // Note that conf->hooks_modules contains array
 // Fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('emailsenderprofile');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // Default sort order (if not yet defined by previous GETPOST)
 if (! $sortfield) $sortfield="t.".key($object->fields);   // Set here default search field. By default 1st field in definition.

+ 33 - 1
htdocs/admin/modules.php

@@ -41,6 +41,7 @@ $langs->loadLangs(array("errors","admin","modulebuilder"));
 $mode=GETPOST('mode', 'alpha');
 if (empty($mode)) $mode='common';
 $action=GETPOST('action','alpha');
+//var_dump($_POST);exit;
 $value=GETPOST('value', 'alpha');
 $page_y=GETPOST('page_y','int');
 $search_keyword=GETPOST('search_keyword','alpha');
@@ -1010,8 +1011,39 @@ if ($mode == 'deploy')
 			print '<form enctype="multipart/form-data" method="POST" class="noborder" action="'.$_SERVER["PHP_SELF"].'" name="forminstall">';
 			print '<input type="hidden" name="action" value="install">';
 			print '<input type="hidden" name="mode" value="deploy">';
-			print $langs->trans("YouCanSubmitFile").' <input type="file" name="fileinstall"> ';
+
+			print $langs->trans("YouCanSubmitFile");
+
+			$max=$conf->global->MAIN_UPLOAD_DOC;		// En Kb
+			$maxphp=@ini_get('upload_max_filesize');	// En inconnu
+			if (preg_match('/k$/i',$maxphp)) $maxphp=$maxphp*1;
+			if (preg_match('/m$/i',$maxphp)) $maxphp=$maxphp*1024;
+			if (preg_match('/g$/i',$maxphp)) $maxphp=$maxphp*1024*1024;
+			if (preg_match('/t$/i',$maxphp)) $maxphp=$maxphp*1024*1024*1024;
+			// Now $max and $maxphp are in Kb
+			$maxmin = $max;
+			if ($maxphp > 0) $maxmin=min($max,$maxphp);
+
+			if ($maxmin > 0)
+			{
+				print '<script type="text/javascript">
+				$(document).ready(function() {
+					jQuery("#fileinstall").on("change", function() {
+						if(this.files[0].size > '.($maxmin*1024).'){
+							alert("'.dol_escape_js($langs->trans("ErrorFileSizeTooLarge")).'");
+							this.value = "";
+						};
+					});
+				});
+				</script>'."\n";
+				// MAX_FILE_SIZE doit précéder le champ input de type file
+				print '<input type="hidden" name="max_file_size" value="'.($maxmin*1024).'">';
+			}
+
+			print '<input class="flat minwidth400" type="file" name="fileinstall" id="fileinstall"> ';
+
 			print '<input type="submit" name="send" value="'.dol_escape_htmltag($langs->trans("Send")).'" class="button">';
+
 			print '</form>';
 
 			print '<br>';

+ 2 - 3
htdocs/api/class/api_documents.class.php

@@ -103,7 +103,7 @@ class Documents extends DolibarrApi
 		}
 
 		$file_content=file_get_contents($original_file_osencoded);
-		return array('filename'=>$filename, 'content'=>base64_encode($file_content), 'encoding'=>'MIME base64 (base64_encode php function, http://php.net/manual/en/function.base64-encode.php)' );
+		return array('filename'=>$filename, 'content-type' => dol_mimetype($filename), 'filesize'=>filesize($original_file), 'content'=>base64_encode($file_content), 'encoding'=>'base64' );
 	}
 
 
@@ -224,10 +224,9 @@ class Documents extends DolibarrApi
 		}
 
 		$file_content=file_get_contents($original_file_osencoded);
-		return array('filename'=>$filename, 'content'=>base64_encode($file_content), 'langcode'=>$outputlangs->defaultlang, 'template'=>$templateused, 'encoding'=>'MIME base64 (base64_encode php function, http://php.net/manual/en/function.base64-encode.php)' );
+		return array('filename'=>$filename, 'content-type' => dol_mimetype($filename), 'filesize'=>filesize($original_file), 'content'=>base64_encode($file_content), 'langcode'=>$outputlangs->defaultlang, 'template'=>$templateused, 'encoding'=>'base64' );
 	}
 
-
 	/**
 	 * Return the list of documents of a dedicated element (from its ID or Ref)
 	 *

+ 1 - 1
htdocs/asset/card.php

@@ -45,7 +45,7 @@ $diroutputmassaction=$conf->asset->dir_output . '/temp/massgeneration/'.$user->i
 $hookmanager->initHooks(array('assetcard'));     // Note that conf->hooks_modules contains array
 // Fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('asset');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // Initialize array of search criterias
 $search_all=trim(GETPOST("search_all",'alpha'));

+ 8 - 5
htdocs/asset/class/asset_type.class.php

@@ -42,7 +42,7 @@ class AssetType extends CommonObject
 	/**
 	 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
 	 */
-	public $picto = 'group';
+	public $picto = 'invoice';
 
 	/**
 	 * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
@@ -51,9 +51,9 @@ class AssetType extends CommonObject
 	public $ismultientitymanaged = 1;
 
 	/**
-     * @var string Asset type label
-     */
-    public $label;
+	 * @var string Asset type label
+	 */
+	public $label;
 
 	/** @var string Accountancy code asset */
 	public $accountancy_code_asset;
@@ -107,12 +107,14 @@ class AssetType extends CommonObject
 		$sql.= ", accountancy_code_asset";
 		$sql.= ", accountancy_code_depreciation_asset";
 		$sql.= ", accountancy_code_depreciation_expense";
+		$sql.= ", note";
 		$sql.= ", entity";
 		$sql.= ") VALUES (";
 		$sql.= "'".$this->db->escape($this->label)."'";
 		$sql.= ", '".$this->db->escape($this->accountancy_code_asset)."'";
 		$sql.= ", '".$this->db->escape($this->accountancy_code_depreciation_asset)."'";
 		$sql.= ", '".$this->db->escape($this->accountancy_code_depreciation_expense)."'";
+		$sql.= ", '".$this->db->escape($this->note)."'";
 		$sql.= ", ".$conf->entity;
 		$sql.= ")";
 
@@ -179,7 +181,8 @@ class AssetType extends CommonObject
 		$sql.= "label = '".$this->db->escape($this->label) ."',";
 		$sql.= "accountancy_code_asset = '".$this->db->escape($this->accountancy_code_asset)."',";
 		$sql.= "accountancy_code_depreciation_asset = '".$this->db->escape($this->accountancy_code_depreciation_asset)."',";
-		$sql.= "accountancy_code_depreciation_expense = '".$this->db->escape($this->accountancy_code_depreciation_expense)."'";
+		$sql.= "accountancy_code_depreciation_expense = '".$this->db->escape($this->accountancy_code_depreciation_expense)."',";
+		$sql.= "note = '".$this->db->escape($this->note) ."'";
 		$sql.= " WHERE rowid =".$this->id;
 
 		$result = $this->db->query($sql);

+ 1 - 1
htdocs/asset/list.php

@@ -62,7 +62,7 @@ $diroutputmassaction=$conf->asset->dir_output . '/temp/massgeneration/'.$user->i
 $hookmanager->initHooks(array('assetlist'));     // Note that conf->hooks_modules contains array
 // Fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('asset');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // Default sort order (if not yet defined by previous GETPOST)
 if (! $sortfield) $sortfield="t.".key($object->fields);   // Set here default search field. By default 1st field in definition.

+ 40 - 250
htdocs/asset/type.php

@@ -49,10 +49,13 @@ $offset = $limit * $page ;
 $pageprev = $page - 1;
 $pagenext = $page + 1;
 if (! $sortorder) {  $sortorder="DESC"; }
-if (! $sortfield) {  $sortfield="d.lastname"; }
+if (! $sortfield) {  $sortfield="a.label"; }
 
 $label=GETPOST("label","alpha");
-$comment=GETPOST("comment");
+$accountancy_code_asset=GETPOST('accountancy_code_asset','string');
+$accountancy_code_depreciation_asset=GETPOST('accountancy_code_depreciation_asset','string');
+$accountancy_code_depreciation_expense=GETPOST('accountancy_code_depreciation_expense','string');
+$comment=GETPOST('comment','string');
 
 // Security check
 $result=restrictedArea($user,'asset',$rowid,'asset_type');
@@ -259,39 +262,39 @@ if (! $rowid && $action != 'create' && $action != 'edit')
 			print '</td>';
 			print '<td>'.dol_escape_htmltag($objp->label).'</td>';
 
-			print '<td>';
+			print '<td class="center">';
 			if (! empty($conf->accounting->enabled))
 			{
 				$accountingaccount = new AccountingAccount($db);
-				$accountingaccount->fetch('',$object->accountancy_code_asset,1);
+				$accountingaccount->fetch('',$objp->accountancy_code_asset,1);
 
-				print $accountingaccount->getNomUrl(0,1,1,'',1);
+				print $accountingaccount->getNomUrl(0,0,0,'',0);
 			} else {
-				print $object->accountancy_code_asset;
+				print $objp->accountancy_code_asset;
 			}
 			print '</td>';
 
-			print '<td>';
+			print '<td class="center">';
 			if (! empty($conf->accounting->enabled))
 			{
 				$accountingaccount2 = new AccountingAccount($db);
-				$accountingaccount2->fetch('',$object->accountancy_code_depreciation_asset,1);
+				$accountingaccount2->fetch('',$objp->accountancy_code_depreciation_asset,1);
 
-				print $accountingaccount2->getNomUrl(0,1,1,'',1);
+				print $accountingaccount2->getNomUrl(0,0,0,'',0);
 			} else {
-				print $object->accountancy_code_depreciation_asset;
+				print $objp->accountancy_code_depreciation_asset;
 			}
 			print '</td>';
 
-			print '<td>';
+			print '<td class="center">';
 			if (! empty($conf->accounting->enabled))
 			{
 				$accountingaccount3 = new AccountingAccount($db);
-				$accountingaccount3->fetch('',$object->accountancy_code_depreciation_expense,1);
+				$accountingaccount3->fetch('',$objp->accountancy_code_depreciation_expense,1);
 
-				print $accountingaccount3->getNomUrl(0,1,1,'',1);
+				print $accountingaccount3->getNomUrl(0,0,0,'',0);
 			} else {
-				print $object->accountancy_code_depreciation_expense;
+				print $objp->accountancy_code_depreciation_expense;
 			}
 			print '</td>';
 
@@ -418,7 +421,7 @@ if ($rowid > 0)
 		 */
 		if ($action == 'delete')
 		{
-			print $form->formconfirm($_SERVER['PHP_SELF']."?rowid=".$object->id,$langs->trans("DeleteAMemberType"),$langs->trans("ConfirmDeleteMemberType",$object->label),"confirm_delete", '',0,1);
+			print $form->formconfirm($_SERVER['PHP_SELF']."?rowid=".$object->id,$langs->trans("DeleteAnAssetType"),$langs->trans("ConfirmDeleteAssetType",$object->label),"confirm_delete", '',0,1);
 		}
 
 		$head = asset_type_prepare_head($object);
@@ -427,14 +430,23 @@ if ($rowid > 0)
 
 		$linkback = '<a href="'.DOL_URL_ROOT.'/asset/type.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
 
-		dol_banner_tab($object, 'rowid', $linkback);
+		$morehtmlref='<div class="refidno">';
+		// Ref asset type
+		$morehtmlref.=$form->editfieldkey("Label", 'label', $object->label, $object, $user->rights->asset->write, 'string', '', 0, 1);
+		$morehtmlref.=$form->editfieldval("Label", 'label', $object->label, $object, $user->rights->asset->write, 'string', '', null, null, '', 1);
+		$morehtmlref.='</div>';
+
+		dol_banner_tab($object, 'rowid', $linkback, 1, 'rowid', 'ref', $morehtmlref, '', 0, '', $morehtmlright);
 
 		print '<div class="fichecenter">';
 		print '<div class="underbanner clearboth"></div>';
 
 		print '<table class="border" width="100%">';
 
-		print '</tr>';
+		print '<tr>';
+		print '<td class="nowrap">';
+		print $langs->trans("AccountancyCodeAsset");
+		print '</td><td>';
 		if (! empty($conf->accounting->enabled))
 		{
 			$accountingaccount = new AccountingAccount($db);
@@ -445,8 +457,12 @@ if ($rowid > 0)
 			print $object->accountancy_code_asset;
 		}
 		print '</td>';
+		print '</tr>';
 
-		print '<td>';
+		print '<tr>';
+		print '<td class="nowrap">';
+		print $langs->trans("AccountancyCodeDepreciationAsset");
+		print '</td><td>';
 		if (! empty($conf->accounting->enabled))
 		{
 			$accountingaccount2 = new AccountingAccount($db);
@@ -457,8 +473,12 @@ if ($rowid > 0)
 			print $object->accountancy_code_depreciation_asset;
 		}
 		print '</td>';
+		print '</tr>';
 
-		print '<td>';
+		print '<tr>';
+		print '<td class="nowrap">';
+		print $langs->trans("AccountancyCodeDepreciationExpense");
+		print '</td><td>';
 		if (! empty($conf->accounting->enabled))
 		{
 			$accountingaccount3 = new AccountingAccount($db);
@@ -468,6 +488,7 @@ if ($rowid > 0)
 		} else {
 			print $object->accountancy_code_depreciation_expense;
 		}
+		print '</td>';
 		print '</tr>';
 
 		print '<tr><td class="tdtop">'.$langs->trans("Description").'</td><td>';
@@ -493,9 +514,6 @@ if ($rowid > 0)
 			print '<div class="inline-block divButAction"><a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=edit&amp;rowid='.$object->id.'">'.$langs->trans("Modify").'</a></div>';
 		}
 
-		// Add
-		print '<div class="inline-block divButAction"><a class="butAction" href="card.php?action=create&typeid='.$object->id.'&backtopage='.urlencode($_SERVER["PHP_SELF"].'?rowid='.$object->id).'">'.$langs->trans("AddMember").'</a></div>';
-
 		// Delete
 		if ($user->rights->asset->write)
 		{
@@ -503,234 +521,6 @@ if ($rowid > 0)
 		}
 
 		print "</div>";
-
-
-		// Show list of assets (nearly same code than in page list.php)
-		$assettypestatic=new AssetType($db);
-
-		$now=dol_now();
-
-		$sql = "SELECT a.rowid, d.login, d.firstname, d.lastname, d.societe, ";
-		$sql.= " d.datefin,";
-		$sql.= " a.fk_asset_type as type_id,";
-		$sql.= " t.label as type";
-		$sql.= " FROM ".MAIN_DB_PREFIX."asset as a, ".MAIN_DB_PREFIX."asset_type as t";
-		$sql.= " WHERE a.fk_asset_type = t.rowid";
-		$sql.= " AND a.entity IN (".getEntity('asset').")";
-		$sql.= " AND t.rowid = ".$object->id;
-		if ($sall)
-		{
-			$sql.=natural_search(array("f.firstname","d.lastname","d.societe","d.email","d.login","d.address","d.town","d.note_public","d.note_private"), $sall);
-		}
-		if ($status != '')
-		{
-			$sql.= natural_search('d.statut', $status, 2);
-		}
-		if ($action == 'search')
-		{
-			if (GETPOST('search','alpha'))
-			{
-				$sql.= natural_search(array("d.firstname","d.lastname"), GETPOST('search','alpha'));
-			}
-		}
-		if (! empty($search_lastname))
-		{
-			$sql.= natural_search(array("d.firstname","d.lastname"), $search_lastname);
-		}
-		if (! empty($search_login))
-		{
-			$sql.= natural_search("d.login", $search_login);
-		}
-		if (! empty($search_email))
-		{
-			$sql.= natural_search("d.email", $search_email);
-		}
-		if ($filter == 'uptodate')
-		{
-			$sql.=" AND datefin >= '".$db->idate($now)."'";
-		}
-		if ($filter == 'outofdate')
-		{
-			$sql.=" AND datefin < '".$db->idate($now)."'";
-		}
-
-		$sql.= " ".$db->order($sortfield,$sortorder);
-
-		// Count total nb of records
-		$nbtotalofrecords = '';
-		if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
-		{
-			$resql = $db->query($sql);
-			if ($resql) $nbtotalofrecords = $db->num_rows($result);
-			else dol_print_error($db);
-			if (($page * $limit) > $nbtotalofrecords)	// if total resultset is smaller then paging size (filtering), goto and load page 0
-			{
-				$page = 0;
-				$offset = 0;
-			}
-		}
-
-		$sql.= " ".$db->plimit($conf->liste_limit+1, $offset);
-
-		$resql = $db->query($sql);
-		if ($resql)
-		{
-			$num = $db->num_rows($resql);
-			$i = 0;
-
-			$titre=$langs->trans("AssetsList");
-			if ($status != '')
-			{
-				if ($status == '-1,1')								{ $titre=$langs->trans("MembersListQualified"); }
-				else if ($status == '-1')							{ $titre=$langs->trans("MembersListToValid"); }
-				else if ($status == '1' && ! $filter)				{ $titre=$langs->trans("MembersListValid"); }
-				else if ($status == '1' && $filter=='uptodate')		{ $titre=$langs->trans("MembersListUpToDate"); }
-				else if ($status == '1' && $filter=='outofdate')	{ $titre=$langs->trans("MembersListNotUpToDate"); }
-				else if ($status == '0')							{ $titre=$langs->trans("MembersListResiliated"); }
-			}
-			elseif ($action == 'search')
-			{
-				$titre=$langs->trans("MembersListQualified");
-			}
-
-			if ($type > 0)
-			{
-				$assettype=new AssetType($db);
-				$result=$assettype->fetch($type);
-				$titre.=" (".$assettype->label.")";
-			}
-
-			$param="&rowid=".$object->id;
-			if (! empty($status))			$param.="&status=".$status;
-			if (! empty($search_lastname))	$param.="&search_lastname=".$search_lastname;
-			if (! empty($search_firstname))	$param.="&search_firstname=".$search_firstname;
-			if (! empty($search_login))		$param.="&search_login=".$search_login;
-			if (! empty($search_email))		$param.="&search_email=".$search_email;
-			if (! empty($filter))			$param.="&filter=".$filter;
-
-			if ($sall)
-			{
-				print $langs->trans("Filter")." (".$langs->trans("Lastname").", ".$langs->trans("Firstname").", ".$langs->trans("EMail").", ".$langs->trans("Address")." ".$langs->trans("or")." ".$langs->trans("Town")."): ".$sall;
-			}
-
-			print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
-			print '<input class="flat" type="hidden" name="rowid" value="'.$object->id.'" size="12"></td>';
-
-			print '<br>';
-			print_barre_liste('',$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num,$nbtotalofrecords);
-
-			$moreforfilter = '';
-
-			print '<div class="div-table-responsive">';
-			print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
-
-			// Lignes des champs de filtre
-			print '<tr class="liste_titre_filter">';
-
-			print '<td class="liste_titre" align="left">';
-			print '<input class="flat" type="text" name="search_lastname" value="'.dol_escape_htmltag($search_lastname).'" size="12"></td>';
-
-			print '<td class="liste_titre" align="left">';
-			print '<input class="flat" type="text" name="search_login" value="'.dol_escape_htmltag($search_login).'" size="7"></td>';
-
-			print '<td class="liste_titre">&nbsp;</td>';
-
-			print '<td class="liste_titre" align="left">';
-			print '<input class="flat" type="text" name="search_email" value="'.dol_escape_htmltag($search_email).'" size="12"></td>';
-
-			print '<td class="liste_titre">&nbsp;</td>';
-
-			print '<td align="right" colspan="2" class="liste_titre">';
-			print '<input type="image" class="liste_titre" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/search.png" name="button_search" value="'.dol_escape_htmltag($langs->trans("Search")).'" title="'.dol_escape_htmltag($langs->trans("Search")).'">';
-			print '&nbsp; ';
-			print '<input type="image" class="liste_titre" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/searchclear.png" name="button_removefilter" value="'.dol_escape_htmltag($langs->trans("RemoveFilter")).'" title="'.dol_escape_htmltag($langs->trans("RemoveFilter")).'">';
-			print '</td>';
-
-			print "</tr>\n";
-
-			print '<tr class="liste_titre">';
-			print_liste_field_titre( $langs->trans("Name")." / ".$langs->trans("Company"),$_SERVER["PHP_SELF"],"d.lastname",$param,"","",$sortfield,$sortorder);
-			print_liste_field_titre("Login",$_SERVER["PHP_SELF"],"d.login",$param,"","",$sortfield,$sortorder);
-			print_liste_field_titre("Nature",$_SERVER["PHP_SELF"],"d.morphy",$param,"","",$sortfield,$sortorder);
-			print_liste_field_titre("EMail",$_SERVER["PHP_SELF"],"d.email",$param,"","",$sortfield,$sortorder);
-			print_liste_field_titre("Status",$_SERVER["PHP_SELF"],"d.statut,d.datefin",$param,"","",$sortfield,$sortorder);
-			print_liste_field_titre("EndSubscription",$_SERVER["PHP_SELF"],"d.datefin",$param,"",'align="center"',$sortfield,$sortorder);
-			print_liste_field_titre("Action",$_SERVER["PHP_SELF"],"",$param,"",'width="60" align="center"',$sortfield,$sortorder);
-			print "</tr>\n";
-
-			while ($i < $num && $i < $conf->liste_limit)
-			{
-				$objp = $db->fetch_object($resql);
-
-				$datefin=$db->jdate($objp->datefin);
-
-				$adh=new Asset($db);
-				$adh->lastname=$objp->lastname;
-				$adh->firstname=$objp->firstname;
-
-				// Lastname
-				print '<tr class="oddeven">';
-				if ($objp->societe != '')
-				{
-					print '<td><a href="card.php?rowid='.$objp->rowid.'">'.img_object($langs->trans("ShowMember"),"user").' '.$adh->getFullName($langs,0,-1,20).' / '.dol_trunc($objp->societe,12).'</a></td>'."\n";
-				}
-				else
-				{
-					print '<td><a href="card.php?rowid='.$objp->rowid.'">'.img_object($langs->trans("ShowMember"),"user").' '.$adh->getFullName($langs,0,-1,32).'</a></td>'."\n";
-				}
-
-				// Login
-				print "<td>".$objp->login."</td>\n";
-
-				// Type
-				/*print '<td class="nowrap">';
-				$assettypestatic->id=$objp->type_id;
-				$assettypestatic->label=$objp->type;
-				print $assettypestatic->getNomUrl(1,12);
-				print '</td>';
-				*/
-
-				// Moral/Physique
-				print "<td>".$adh->getmorphylib($objp->morphy)."</td>\n";
-
-				// EMail
-				print "<td>".dol_print_email($objp->email,0,0,1)."</td>\n";
-
-				// Statut
-				print '<td class="nowrap">';
-				print $adh->LibStatut($objp->statut,$objp->subscription,$datefin,2);
-				print "</td>";
-
-				// Actions
-				print '<td align="center">';
-				if ($user->rights->asset->creer)
-				{
-					print '<a href="card.php?rowid='.$objp->rowid.'&action=edit&backtopage='.urlencode($_SERVER["PHP_SELF"].'?rowid='.$object->id).'">'.img_edit().'</a>';
-				}
-				print '&nbsp;';
-				if ($user->rights->asset->supprimer)
-				{
-					print '<a href="card.php?rowid='.$objp->rowid.'&action=resign">'.img_picto($langs->trans("Resiliate"),'disable.png').'</a>';
-				}
-				print "</td>";
-
-				print "</tr>\n";
-				$i++;
-			}
-
-			print "</table>\n";
-			print '</div>';
-			print '</form>';
-
-			if ($num > $conf->liste_limit)
-			{
-				print_barre_liste('',$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num,$nbtotalofrecords,'');
-			}
-		}
-		else
-		{
-			dol_print_error($db);
-		}
 	}
 
 	/* ************************************************************************** */

+ 8 - 2
htdocs/comm/action/class/actioncomm.class.php

@@ -1679,13 +1679,19 @@ class ActionComm extends CommonObject
     	$this->output = '';
 		$this->error='';
 
-    	if (empty($conf->global->AGENDA_REMINDER_EMAIL))
+    	if (empty($conf->agenda->enabled))	// Should not happen. If module disabled, cron job should not be visible.
+		{
+			$langs->load("agenda");
+			$this->output = $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv("Agenda"));
+			return 0;
+		}
+		if (empty($conf->global->AGENDA_REMINDER_EMAIL))
     	{
     		$langs->load("agenda");
     		$this->output = $langs->trans('EventRemindersByEmailNotEnabled', $langs->transnoentitiesnoconv("Agenda"));
     		return 0;
     	}
-
+		
     	$now = dol_now();
 
     	dol_syslog(__METHOD__, LOG_DEBUG);

+ 5 - 5
htdocs/comm/action/list.php

@@ -74,10 +74,14 @@ $filtert = GETPOST("filtert","int",3);
 $usergroup = GETPOST("usergroup","int",3);
 $showbirthday = empty($conf->use_javascript_ajax)?GETPOST("showbirthday","int"):1;
 
+// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
+$object = new ActionComm($db);
+$hookmanager->initHooks(array('agendalist'));
+
 $extrafields = new ExtraFields($db);
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('actioncomm');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 // If not choice done on calendar owner, we filter on user.
 if (empty($filtert) && empty($conf->global->AGENDA_ALL_CALENDARS))
 {
@@ -117,10 +121,6 @@ if (! $user->rights->agenda->allactions->read || $filter=='mine')	// If no permi
 	$filtert=$user->id;
 }
 
-// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
-$object = new ActionComm($db);
-$hookmanager->initHooks(array('agendalist'));
-
 $arrayfields=array(
 	'a.id'=>array('label'=>"Ref", 'checked'=>1),
 	'owner'=>array('label'=>"Owner", 'checked'=>1),

+ 1 - 1
htdocs/comm/mailing/card.php

@@ -747,7 +747,7 @@ if ($action == 'create')
 	dol_fiche_head();
 
 	print '<table class="border" width="100%">';
-	print '<tr><td class="fieldrequired titlefieldcreate">'.$langs->trans("MailTitle").'</td><td><input class="flat minwidth300" name="titre" value="'.dol_escape_htmltag(GETPOST('titre')).'"></td></tr>';
+	print '<tr><td class="fieldrequired titlefieldcreate">'.$langs->trans("MailTitle").'</td><td><input class="flat minwidth300" name="titre" value="'.dol_escape_htmltag(GETPOST('titre')).'" autofocus="autofocus"></td></tr>';
 	print '<tr><td class="fieldrequired">'.$langs->trans("MailFrom").'</td><td><input class="flat minwidth200" name="from" value="'.$conf->global->MAILING_EMAIL_FROM.'"></td></tr>';
 	print '<tr><td>'.$langs->trans("MailErrorsTo").'</td><td><input class="flat minwidth200" name="errorsto" value="'.(!empty($conf->global->MAILING_EMAIL_ERRORSTO)?$conf->global->MAILING_EMAIL_ERRORSTO:$conf->global->MAIN_MAIL_ERRORS_TO).'"></td></tr>';
 

+ 3 - 2
htdocs/comm/mailing/list.php

@@ -46,20 +46,21 @@ $search_all=trim((GETPOST('search_all', 'alphanohtml')!='')?GETPOST('search_all'
 $search_ref=GETPOST("search_ref", "alpha") ? GETPOST("search_ref", "alpha") : GETPOST("sref", "alpha");
 $filteremail=GETPOST('filteremail','alpha');
 
+$object = new Mailing($db);
+
 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
 $hookmanager->initHooks(array('mailinglist'));
 $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('mailing');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // List of fields to search into when doing a "search in all"
 $fieldstosearchall = array(
     'm.titre'=>'Ref',
 );
 
-$object = new Mailing($db);
 
 
 

+ 1 - 1
htdocs/comm/propal/list.php

@@ -129,7 +129,7 @@ $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('propal');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // List of fields to search into when doing a "search in all"
 $fieldstosearchall = array(

+ 44 - 36
htdocs/comm/remx.php

@@ -254,7 +254,7 @@ if ($socid > 0)
     print '<div class="fichecenter">';
 
     print '<div class="underbanner clearboth"></div>';
-    
+
     if(! $isCustomer && ! $isSupplier) {
     	print '<p class="opacitymedium">'.$langs->trans('ThirdpartyIsNeitherCustomerNorClientSoCannotHaveDiscounts').'</p>';
 
@@ -266,8 +266,8 @@ if ($socid > 0)
     	$db->close();
     	exit;
     }
-    
-    
+
+
 	print '<table class="border centpercent">';
 
 	if($isCustomer) {	// Calcul avoirs client en cours
@@ -293,7 +293,7 @@ if ($socid > 0)
 
 		print '<tr><td class="titlefield">'.$langs->trans("CustomerAbsoluteDiscountAllUsers").'</td>';
 		print '<td>'.$remise_all.'&nbsp;'.$langs->trans("Currency".$conf->currency).' '.$langs->trans("HT").'</td></tr>';
-	
+
 		if (! empty($user->fk_soc))    // No need to show this for external users
 		{
 			print '<tr><td>'.$langs->trans("CustomerAbsoluteDiscountMy").'</td>';
@@ -322,10 +322,10 @@ if ($socid > 0)
 		{
 			dol_print_error($db);
 		}
-		
+
 		print '<tr><td class="titlefield">'.$langs->trans("SupplierAbsoluteDiscountAllUsers").'</td>';
 		print '<td>'.$remise_all.'&nbsp;'.$langs->trans("Currency".$conf->currency).' '.$langs->trans("HT").'</td></tr>';
-		
+
 		if (! empty($user->fk_soc))    // No need to show this for external users
 		{
 			print '<tr><td>'.$langs->trans("SupplierAbsoluteDiscountMy").'</td>';
@@ -344,11 +344,11 @@ if ($socid > 0)
     	print load_fiche_titre($langs->trans("NewGlobalDiscount"),'','');
 
     	print '<div class="underbanner clearboth"></div>';
- 
+
     	if($isCustomer && ! $isSupplier) {
     		print '<input type="hidden" name="discount_type" value="0" />';
     	}
-    	
+
     	if(! $isCustomer && $isSupplier) {
     		print '<input type="hidden" name="discount_type" value="1" />';
     	}
@@ -356,8 +356,8 @@ if ($socid > 0)
     	print '<table class="border" width="100%">';
 		if($isCustomer && $isSupplier) {
 			print '<tr><td class="titlefield fieldrequired">'.$langs->trans('DiscountType').'</td>';
-			print '<td><input type="radio" name="discount_type" id="discount_type_0" selected value="0"/> <label for="discount_type_0">'.$langs->trans('Customer').'</label>';
-			print ' <input type="radio" name="discount_type" id="discount_type_1" selected value="1"/> <label for="discount_type_1">'.$langs->trans('Supplier').'</label>';
+			print '<td><input type="radio" name="discount_type" id="discount_type_0" checked="checked" value="0"/> <label for="discount_type_0">'.$langs->trans('Customer').'</label>';
+			print ' &nbsp; <input type="radio" name="discount_type" id="discount_type_1" value="1"/> <label for="discount_type_1">'.$langs->trans('Supplier').'</label>';
 			print '</td></tr>';
 		}
     	print '<tr><td class="titlefield fieldrequired">'.$langs->trans("AmountHT").'</td>';
@@ -401,7 +401,7 @@ if ($socid > 0)
 	/*
 	 * Liste remises fixes client restant en cours (= liees a aucune facture ni ligne de facture)
 	 */
-	
+
 	print load_fiche_titre($langs->trans("DiscountStillRemaining"));
 
 	if($isCustomer) {
@@ -424,10 +424,11 @@ if ($socid > 0)
 		$sql.= " AND rc.discount_type = 0"; // Eliminate supplier discounts
 		$sql.= " AND (rc.fk_facture_line IS NULL AND rc.fk_facture IS NULL)";
 		$sql.= " ORDER BY rc.datec DESC";
-	
+
 		$resql=$db->query($sql);
 		if ($resql)
 		{
+			print '<div class="div-table-responsive-no-min">';
 			print '<table width="100%" class="noborder">';
 			print '<tr class="liste_titre">';
 			print '<td class="widthdate">'.$langs->trans("Date").'</td>';	// Need 120+ for format with AM/PM
@@ -439,9 +440,9 @@ if ($socid > 0)
 			print '<td width="100" align="center">'.$langs->trans("DiscountOfferedBy").'</td>';
 			print '<td width="50">&nbsp;</td>';
 			print '</tr>';
-	
+
 			$showconfirminfo=array();
-	
+
 			$i = 0;
 			$num = $db->num_rows($resql);
 			if ($num > 0)
@@ -449,7 +450,7 @@ if ($socid > 0)
 	    		while ($i < $num)
 	    		{
 	    			$obj = $db->fetch_object($resql);
-	
+
 	    			print '<tr class="oddeven">';
 	    			print '<td>'.dol_print_date($db->jdate($obj->dc),'dayhour').'</td>';
 	    			if (preg_match('/\(CREDIT_NOTE\)/',$obj->description))
@@ -502,7 +503,7 @@ if ($socid > 0)
 	    			}
 	    			else print '<td>&nbsp;</td>';
 	    			print '</tr>';
-	
+
 	    			if ($_GET["action"]=='split' && GETPOST('remid') == $obj->rowid)
 	    			{
 	    				$showconfirminfo['rowid']=$obj->rowid;
@@ -517,7 +518,8 @@ if ($socid > 0)
 			}
 			$db->free($resql);
 			print "</table>";
-	
+			print '</div>';
+
 			if (count($showconfirminfo))
 			{
 				$amount1=price2num($showconfirminfo['amount_ttc']/2,'MT');
@@ -561,10 +563,11 @@ if ($socid > 0)
 		$sql.= " AND rc.discount_type = 1"; // Eliminate customer discounts
 		$sql.= " AND (rc.fk_invoice_supplier IS NULL AND rc.fk_invoice_supplier_line IS NULL)";
 		$sql.= " ORDER BY rc.datec DESC";
-		
+
 		$resql=$db->query($sql);
 		if ($resql)
 		{
+			print '<div class="div-table-responsive-no-min">';
 			print '<table width="100%" class="noborder">';
 			print '<tr class="liste_titre">';
 			print '<td class="widthdate">'.$langs->trans("Date").'</td>';	// Need 120+ for format with AM/PM
@@ -576,9 +579,9 @@ if ($socid > 0)
 			print '<td width="100" align="center">'.$langs->trans("DiscountOfferedBy").'</td>';
 			print '<td width="50">&nbsp;</td>';
 			print '</tr>';
-			
+
 			$showconfirminfo=array();
-			
+
 			$i = 0;
 			$num = $db->num_rows($resql);
 			if ($num > 0)
@@ -586,7 +589,7 @@ if ($socid > 0)
 				while ($i < $num)
 				{
 					$obj = $db->fetch_object($resql);
-					
+
 					print '<tr class="oddeven">';
 					print '<td>'.dol_print_date($db->jdate($obj->dc),'dayhour').'</td>';
 					if (preg_match('/\(CREDIT_NOTE\)/',$obj->description))
@@ -639,7 +642,7 @@ if ($socid > 0)
 					}
 					else print '<td>&nbsp;</td>';
 					print '</tr>';
-					
+
 					if ($_GET["action"]=='split' && GETPOST('remid') == $obj->rowid)
 					{
 						$showconfirminfo['rowid']=$obj->rowid;
@@ -654,7 +657,8 @@ if ($socid > 0)
 			}
 			$db->free($resql);
 			print "</table>";
-			
+			print '</div>';
+
 			if (count($showconfirminfo))
 			{
 				$amount1=price2num($showconfirminfo['amount_ttc']/2,'MT');
@@ -673,19 +677,19 @@ if ($socid > 0)
 			dol_print_error($db);
 		}
 
-		if($isCustomer) {
+		if ($isCustomer) {
 			print '</div>'; // class="ficheaddleft"
 			print '</div>'; // class="fichehalfright"
 			print '</div>'; // class="fichecenter"
 		}
 	}
 
-	print '<br>';
+	print '<div class="clearboth"></div><br>';
 
 	/*
 	 * List discount consumed (=liees a une ligne de facture ou facture)
 	 */
-	
+
 	print load_fiche_titre($langs->trans("DiscountAlreadyCounted"));
 
 	if($isCustomer) {
@@ -730,12 +734,13 @@ if ($socid > 0)
 		$sql2.= " AND rc.fk_user = u.rowid";
 		$sql2.= " AND rc.discount_type = 0"; // Eliminate supplier discounts
 		$sql2.= " ORDER BY dc DESC";
-	
+
 		$resql=$db->query($sql);
 		$resql2=null;
 		if ($resql) $resql2=$db->query($sql2);
 		if ($resql2)
 		{
+			print '<div class="div-table-responsive-no-min">';
 			print '<table class="noborder" width="100%">';
 			print '<tr class="liste_titre">';
 			print '<td class="widthdate">'.$langs->trans("Date").'</td>';	// Need 120+ for format with AM/PM
@@ -747,7 +752,7 @@ if ($socid > 0)
 			print '<td width="100" align="center">'.$langs->trans("Author").'</td>';
 			print '<td width="50">&nbsp;</td>';
 			print '</tr>';
-	
+
 			$tab_sqlobj=array();
 			$tab_sqlobjOrder=array();
 			$num = $db->num_rows($resql);
@@ -761,7 +766,7 @@ if ($socid > 0)
 	    		}
 			}
 			$db->free($resql);
-	
+
 			$num = $db->num_rows($resql2);
 			for ($i = 0;$i < $num;$i++)
 			{
@@ -771,7 +776,7 @@ if ($socid > 0)
 			}
 			$db->free($resql2);
 			array_multisort($tab_sqlobjOrder,SORT_DESC,$tab_sqlobj);
-	
+
 			$num = count($tab_sqlobj);
 			if ($num > 0)
 			{
@@ -830,8 +835,9 @@ if ($socid > 0)
 			{
 			    print '<tr><td colspan="8" class="opacitymedium">'.$langs->trans("None").'</td></tr>';
 			}
-	
+
 			print "</table>";
+			print '</div>';
 		}
 		else
 		{
@@ -882,12 +888,13 @@ if ($socid > 0)
 		$sql2.= " AND rc.fk_user = u.rowid";
 		$sql2.= " AND rc.discount_type = 1"; // Eliminate customer discounts
 		$sql2.= " ORDER BY dc DESC";
-		
+
 		$resql=$db->query($sql);
 		$resql2=null;
 		if ($resql) $resql2=$db->query($sql2);
 		if ($resql2)
 		{
+			print '<div class="div-table-responsive-no-min">';
 			print '<table class="noborder" width="100%">';
 			print '<tr class="liste_titre">';
 			print '<td class="widthdate">'.$langs->trans("Date").'</td>';	// Need 120+ for format with AM/PM
@@ -899,7 +906,7 @@ if ($socid > 0)
 			print '<td width="100" align="center">'.$langs->trans("Author").'</td>';
 			print '<td width="50">&nbsp;</td>';
 			print '</tr>';
-			
+
 			$tab_sqlobj=array();
 			$tab_sqlobjOrder=array();
 			$num = $db->num_rows($resql);
@@ -913,7 +920,7 @@ if ($socid > 0)
 				}
 			}
 			$db->free($resql);
-			
+
 			$num = $db->num_rows($resql2);
 			for ($i = 0;$i < $num;$i++)
 			{
@@ -923,7 +930,7 @@ if ($socid > 0)
 			}
 			$db->free($resql2);
 			array_multisort($tab_sqlobjOrder,SORT_DESC,$tab_sqlobj);
-			
+
 			$num = count($tab_sqlobj);
 			if ($num > 0)
 			{
@@ -982,8 +989,9 @@ if ($socid > 0)
 			{
 				print '<tr><td colspan="8" class="opacitymedium">'.$langs->trans("None").'</td></tr>';
 			}
-			
+
 			print "</table>";
+			print '</div>';
 		}
 		else
 		{

+ 4 - 2
htdocs/commande/list.php

@@ -108,7 +108,7 @@ $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('commande');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // List of fields to search into when doing a "search in all"
 $fieldstosearchall = array(
@@ -466,7 +466,9 @@ if ($resql)
 	print '<input type="hidden" name="page" value="'.$page.'">';
 	print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
 	print '<input type="hidden" name="viewstatut" value="'.$viewstatut.'">';
-
+	print '<input type="hidden" name="socid" value="'.$socid.'">';
+	
+	
 	print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_commercial.png', 0, $newcardbutton, '', $limit);
 
 	$topicmail="SendOrderRef";

+ 1 - 1
htdocs/compta/bank/bankentries_list.php

@@ -129,7 +129,7 @@ $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('banktransaction');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost('banktransaction','','search_');
 
 $arrayfields=array(
     'b.rowid'=>array('label'=>$langs->trans("Ref"), 'checked'=>1),

+ 1 - 1
htdocs/compta/bank/list.php

@@ -74,7 +74,7 @@ $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('bank_account');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // List of fields to search into when doing a "search in all"
 $fieldstosearchall = array(

+ 2 - 0
htdocs/compta/facture/card.php

@@ -1357,6 +1357,8 @@ if (empty($reshook))
 								{
 									// Don't add lines with qty 0 when coming from a shipment including all order lines
 									if($srcobject->element == 'shipping' && $conf->global->SHIPMENT_GETS_ALL_ORDER_PRODUCTS && $lines[$i]->qty == 0) continue;
+									// Don't add closed lines when coming from a contract
+									if($srcobject->element == 'contrat' && $lines[$i]->statut == 5) continue;
 
 									$label=(! empty($lines[$i]->label)?$lines[$i]->label:'');
 									$desc=(! empty($lines[$i]->desc)?$lines[$i]->desc:$lines[$i]->libelle);

+ 1 - 1
htdocs/compta/facture/fiche-rec.php

@@ -94,7 +94,7 @@ $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('facture_rec');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 $permissionnote = $user->rights->facture->creer; // Used by the include of actions_setnotes.inc.php
 $permissiondellink=$user->rights->facture->creer;	// Used by the include of actions_dellink.inc.php

+ 1 - 1
htdocs/compta/facture/invoicetemplate_list.php

@@ -108,7 +108,7 @@ $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('facture_rec');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 $permissionnote = $user->rights->facture->creer; // Used by the include of actions_setnotes.inc.php
 $permissiondellink=$user->rights->facture->creer;	// Used by the include of actions_dellink.inc.php

+ 1 - 1
htdocs/compta/facture/list.php

@@ -134,7 +134,7 @@ $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('facture');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // List of fields to search into when doing a "search in all"
 $fieldstosearchall = array(

+ 1 - 1
htdocs/compta/index.php

@@ -636,7 +636,7 @@ if (! empty($conf->tax->enabled) && $user->rights->tax->charges->lire)
 
 			print '<table class="noborder" width="100%">';
 			print '<tr class="liste_titre">';
-			print '<th>'.$langs->trans("ContributionsToPay").($num?' <a href="'.DOL_URL_ROOT.'/compta/sociales/index.php?status=0"><span class="badge">'.$num.'</span></a>':'').'</th>';
+			print '<th>'.$langs->trans("ContributionsToPay").($num?' <a href="'.DOL_URL_ROOT.'/compta/sociales/list.php?status=0"><span class="badge">'.$num.'</span></a>':'').'</th>';
 			print '<th align="center">'.$langs->trans("DateDue").'</th>';
 			print '<th align="right">'.$langs->trans("AmountTTC").'</th>';
 			print '<th align="right">'.$langs->trans("Paid").'</th>';

+ 1 - 1
htdocs/compta/resultat/clientfourn.php

@@ -773,7 +773,7 @@ else
 
 		            print '<tr class="oddeven"><td>&nbsp;</td>';
 
-		            print "<td>".$langs->trans("Salary")." <a href=\"".DOL_URL_ROOT."/compta/salaries/index.php?filtre=s.fk_user=".$obj->fk_user."\">".$obj->firstname." ".$obj->lastname."</a></td>\n";
+		            print "<td>".$langs->trans("Salary")." <a href=\"".DOL_URL_ROOT."/compta/salaries/list.php?filtre=s.fk_user=".$obj->fk_user."\">".$obj->firstname." ".$obj->lastname."</a></td>\n";
 
 		            if ($modecompta == 'CREANCES-DETTES') print '<td align="right">'.price(-$obj->amount).'</td>';
 		            print '<td align="right">'.price(-$obj->amount).'</td>';

+ 2 - 2
htdocs/compta/salaries/card.php

@@ -174,7 +174,7 @@ if ($action == 'delete')
 			if ($result >= 0)
 			{
 				$db->commit();
-				header("Location: ".DOL_URL_ROOT.'/compta/salaries/index.php');
+				header("Location: ".DOL_URL_ROOT.'/compta/salaries/list.php');
 				exit;
 			}
 			else
@@ -365,7 +365,7 @@ if ($id)
 
 	dol_fiche_head($head, 'card', $langs->trans("SalaryPayment"), -1, 'payment');
 
-	$linkback = '<a href="'.DOL_URL_ROOT.'/compta/salaries/index.php'.(! empty($socid)?'?socid='.$socid:'').'">'.$langs->trans("BackToList").'</a>';
+	$linkback = '<a href="'.DOL_URL_ROOT.'/compta/salaries/list.php?restore_lastsearch_values=1'.(! empty($socid)?'&socid='.$socid:'').'">'.$langs->trans("BackToList").'</a>';
 
 	$morehtmlref='<div class="refidno">';
 

+ 57 - 8
htdocs/compta/salaries/class/paymentsalary.class.php

@@ -537,24 +537,73 @@ class PaymentSalary extends CommonObject
 	/**
 	 *	Send name clicable (with possibly the picto)
 	 *
-	 *	@param	int		$withpicto		0=No picto, 1=Include picto into link, 2=Only picto
-	 *	@param	string	$option			link option
-	 *	@return	string					Chaine with URL
+	 *	@param	int		$withpicto					0=No picto, 1=Include picto into link, 2=Only picto
+	 *	@param	string	$option						link option
+     *  @param	int  	$notooltip					1=Disable tooltip
+     *  @param  string  $morecss            		Add more css on link
+     *  @param  int     $save_lastsearch_value    	-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
+	 *	@return	string								Chaine with URL
 	 */
-	function getNomUrl($withpicto=0,$option='')
+	function getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
 	{
-		global $langs;
+		global $db, $conf, $langs, $hookmanager;
+		global $dolibarr_main_authentication, $dolibarr_main_demo;
+		global $menumanager;
+
+		if (! empty($conf->dol_no_mouse_hover)) $notooltip=1;   // Force disable tooltips
+
+		$result = '';
+
+		$label = '<u>' . $langs->trans("ShowSalaryPayment") . '</u>';
+		$label.= '<br>';
+		$label.= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
+
+		$url = DOL_URL_ROOT.'/compta/salaries/card.php?id='.$this->id;
+
+		if ($option != 'nolink')
+		{
+			// Add param to save lastsearch_values or not
+			$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
+			if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
+			if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
+		}
 
-		$result='';
-		$label=$langs->trans("ShowSalaryPayment").': '.$this->ref;
+		$linkclose='';
+		if (empty($notooltip))
+		{
+			if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
+			{
+				$label=$langs->trans("ShowMyObject");
+				$linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
+			}
+			$linkclose.=' title="'.dol_escape_htmltag($label, 1).'"';
+			$linkclose.=' class="classfortooltip'.($morecss?' '.$morecss:'').'"';
+
+			/*
+			 $hookmanager->initHooks(array('myobjectdao'));
+			 $parameters=array('id'=>$this->id);
+			 $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
+			 if ($reshook > 0) $linkclose = $hookmanager->resPrint;
+			 */
+		}
+		else $linkclose = ($morecss?' class="'.$morecss.'"':'');
 
-		$linkstart = '<a href="'.DOL_URL_ROOT.'/compta/salaries/card.php?id='.$this->id.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
+		$linkstart = '<a href="'.$url.'"';
+		$linkstart.=$linkclose.'>';
 		$linkend='</a>';
 
 		$result .= $linkstart;
 		if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
 		if ($withpicto != 2) $result.= $this->ref;
 		$result .= $linkend;
+		//if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
+
+		global $action,$hookmanager;
+		$hookmanager->initHooks(array('salarypayment'));
+		$parameters=array('id'=>$this->id, 'getnomurl'=>$result);
+		$reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
+		if ($reshook > 0) $result = $hookmanager->resPrint;
+		else $result .= $hookmanager->resPrint;
 
 		return $result;
 	}

+ 1 - 1
htdocs/compta/salaries/document.php

@@ -99,7 +99,7 @@ if ($object->id)
 		$totalsize+=$file['size'];
 	}
 
-	$linkback = '<a href="'.DOL_URL_ROOT.'/compta/salaries/index.php'.(! empty($socid)?'?socid='.$socid:'').'">'.$langs->trans("BackToList").'</a>';
+	$linkback = '<a href="'.DOL_URL_ROOT.'/compta/salaries/list.php?restore_lastsearch_values=1'.(! empty($socid)?'&socid='.$socid:'').'">'.$langs->trans("BackToList").'</a>';
 
 	$morehtmlref='<div class="refidno">';
 

+ 1 - 1
htdocs/compta/salaries/info.php

@@ -54,7 +54,7 @@ $head = salaries_prepare_head($object);
 
 dol_fiche_head($head, 'info', $langs->trans("SalaryPayment"), -1, 'payment');
 
-$linkback = '<a href="'.DOL_URL_ROOT.'/compta/salaries/index.php'.(! empty($socid)?'?socid='.$socid:'').'">'.$langs->trans("BackToList").'</a>';
+$linkback = '<a href="'.DOL_URL_ROOT.'/compta/salaries/list.php?restore_lastsearch_values=1'.(! empty($socid)?'&socid='.$socid:'').'">'.$langs->trans("BackToList").'</a>';
 
 $morehtmlref='<div class="refidno">';
 

+ 2 - 1
htdocs/compta/salaries/index.php → htdocs/compta/salaries/list.php

@@ -18,7 +18,7 @@
  */
 
 /**
- *	    \file       htdocs/compta/salaries/index.php
+ *	    \file       htdocs/compta/salaries/list.php
  *      \ingroup    salaries
  *		\brief     	List of salaries payments
  */
@@ -231,6 +231,7 @@ if ($result)
 
         $salstatic->id=$obj->rowid;
 		$salstatic->ref=$obj->rowid;
+
         // Ref
 		print "<td>".$salstatic->getNomUrl(1)."</td>\n";
 		// Employee

+ 1 - 1
htdocs/compta/sociales/card.php

@@ -481,7 +481,7 @@ if ($id > 0)
 		}
 		$morehtmlref.='</div>';
 
-		$linkback = '<a href="' . DOL_URL_ROOT . '/compta/sociales/index.php?restore_lastsearch_values=1">' . $langs->trans("BackToList") . '</a>';
+		$linkback = '<a href="' . DOL_URL_ROOT . '/compta/sociales/list.php?restore_lastsearch_values=1">' . $langs->trans("BackToList") . '</a>';
 
 		$object->totalpaye = $totalpaye;   // To give a chance to dol_banner_tab to use already paid amount to show correct status
 

+ 1 - 1
htdocs/compta/sociales/document.php

@@ -127,7 +127,7 @@ if ($object->id)
 	}
 	$morehtmlref.='</div>';
 
-	$linkback = '<a href="' . DOL_URL_ROOT . '/compta/sociales/index.php?restore_lastsearch_values=1">' . $langs->trans("BackToList") . '</a>';
+	$linkback = '<a href="' . DOL_URL_ROOT . '/compta/sociales/list.php?restore_lastsearch_values=1">' . $langs->trans("BackToList") . '</a>';
 
 	$object->totalpaye = $totalpaye;   // To give a chance to dol_banner_tab to use already paid amount to show correct status
 

+ 1 - 1
htdocs/compta/sociales/info.php

@@ -98,7 +98,7 @@ if (! empty($conf->projet->enabled))
 }
 $morehtmlref.='</div>';
 
-$linkback = '<a href="' . DOL_URL_ROOT . '/compta/sociales/index.php?restore_lastsearch_values=1">' . $langs->trans("BackToList") . '</a>';
+$linkback = '<a href="' . DOL_URL_ROOT . '/compta/sociales/list.php?restore_lastsearch_values=1">' . $langs->trans("BackToList") . '</a>';
 
 $object->totalpaye = $totalpaye;   // To give a chance to dol_banner_tab to use already paid amount to show correct status
 

+ 17 - 12
htdocs/compta/sociales/index.php → htdocs/compta/sociales/list.php

@@ -19,7 +19,7 @@
  */
 
 /**
- *   	\file       htdocs/compta/sociales/index.php
+ *   	\file       htdocs/compta/list/index.php
  *		\ingroup    tax
  *		\brief      Page to list all social contributions
  */
@@ -55,19 +55,19 @@ if (! $sortorder) $sortorder="DESC";
 $year=GETPOST("year",'int');
 $filtre=GETPOST("filtre",'int');
 
-if (empty($_REQUEST['typeid']))
+if (! GETPOSTISSET('search_typeid'))
 {
 	$newfiltre=str_replace('filtre=','',$filtre);
 	$filterarray=explode('-',$newfiltre);
 	foreach($filterarray as $val)
 	{
 		$part=explode(':',$val);
-		if ($part[0] == 'cs.fk_type') $typeid=$part[1];
+		if ($part[0] == 'cs.fk_type') $search_typeid=$part[1];
 	}
 }
 else
 {
-	$typeid=$_REQUEST['typeid'];
+	$search_typeid=GETPOST('search_typeid','int');
 }
 
 if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') || GETPOST('button_removefilter','alpha')) // All test are required to be compatible with all browsers
@@ -76,11 +76,12 @@ if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x',
 	$search_label="";
 	$search_amount="";
 	$search_status='';
-    $typeid="";
+    $search_typeid="";
 	$year="";
 	$month="";
 }
 
+
 /*
  *	View
  */
@@ -118,8 +119,8 @@ if ($filtre) {
     $filtre=str_replace(":","=",$filtre);
     $sql .= " AND ".$filtre;
 }
-if ($typeid) {
-    $sql .= " AND cs.fk_type=".$db->escape($typeid);
+if ($search_typeid) {
+    $sql .= " AND cs.fk_type=".$db->escape($search_typeid);
 }
 $sql.= " GROUP BY cs.rowid, cs.fk_type, cs.amount, cs.date_ech, cs.libelle, cs.paye, cs.periode, c.libelle";
 $sql.= $db->order($sortfield,$sortorder);
@@ -139,10 +140,14 @@ if ($resql)
 	$i = 0;
 
 	$param='';
-    if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage;
-	if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit;
-	if ($year)   $param.='&amp;year='.$year;
-	if ($typeid) $param.='&amp;typeid='.$typeid;
+    if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage);
+	if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit);
+	if ($search_ref)    $param.='&search_ref='.urlencode($search_ref);
+	if ($search_label)  $param.='&search_label='.urlencode($search_label);
+	if ($search_amount) $param.='&search_amount='.urlencode($search_amount);
+	if ($search_typeid) $param.='&search_typeid='.urlencode($search_typeid);
+	if ($search_status != '' && $search_status != '-1') $param.='&search_status='.urlencode($search_status);
+	if ($year)          $param.='&year='.urlencode($year);
 
 	$newcardbutton='';
 	if($user->rights->tax->charges->creer)
@@ -193,7 +198,7 @@ if ($resql)
 		print '<td class="liste_titre"><input type="text" class="flat" size="8" name="search_label" value="'.dol_escape_htmltag($search_label).'"></td>';
 		// Type
 		print '<td class="liste_titre" align="left">';
-	    $formsocialcontrib->select_type_socialcontrib($typeid,'typeid',1,0,0,'maxwidth100onsmartphone');
+	    $formsocialcontrib->select_type_socialcontrib($search_typeid,'search_typeid',1,0,0,'maxwidth100onsmartphone');
 	    print '</td>';
 		// Period end date
 		print '<td class="liste_titre">&nbsp;</td>';

+ 2 - 1
htdocs/contact/list.php

@@ -133,7 +133,7 @@ $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('contact');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // List of fields to search into when doing a "search in all"
 $fieldstosearchall = array(
@@ -321,6 +321,7 @@ if (strlen($search_twitter))        $sql.= natural_search('p.twitter', $search_t
 if (strlen($search_facebook))       $sql.= natural_search('p.facebook', $search_facebook);
 if (strlen($search_email))          $sql.= natural_search('p.email', $search_email);
 if (strlen($search_zip))   			$sql.= natural_search("p.zip",$search_zip);
+if (strlen($search_town))   		$sql.= natural_search("p.town",$search_town);
 if ($search_status != '' && $search_status >= 0) $sql.= " AND p.statut = ".$db->escape($search_status);
 if ($search_import_key)             $sql.= natural_search("p.import_key",$search_import_key);
 if ($type == "o")        // filtre sur type

+ 1 - 1
htdocs/contrat/class/contrat.class.php

@@ -2581,7 +2581,7 @@ class ContratLigne extends CommonObjectLine
 	/**
 	 * @var float
 	 * @deprecated Use $price_ht instead
-	 * @see price_ht
+	 * @see $price_ht
 	 */
 	public $price;
 

+ 1 - 1
htdocs/contrat/list.php

@@ -100,7 +100,7 @@ $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('contrat');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 // List of fields to search into when doing a "search in all"
 $fieldstosearchall = array(
 	'c.ref'=>'Ref',

+ 1 - 1
htdocs/contrat/services_list.php

@@ -84,7 +84,7 @@ $extrafields = new ExtraFields($db);
 
 // fetch optionals attributes and labels
 $extralabels = $extrafields->fetch_name_optionals_label('contratdet');
-$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
 
 // Security check
 $contratid = GETPOST('id','int');

+ 4 - 0
htdocs/core/actions_sendmails.inc.php

@@ -439,6 +439,10 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO
 							$object->sendtouserid	= $sendtouserid;
 						}
 
+						// TODO Set object->email_xxx properties
+						$object->email_msgid = $mailfile->msgid;
+						//...
+
 						// Call of triggers
 						if (! empty($trigger_name))
 						{

+ 22 - 11
htdocs/core/class/extrafields.class.php

@@ -1941,24 +1941,36 @@ class ExtraFields
 	/**
 	 * return array_options array of data of extrafields value of object sent by a search form
 	 *
-	 * @param  array   $extralabels    $array of extrafields (@deprecated)
-	 * @param  string  $keyprefix      Prefix string to add into name and id of field (can be used to avoid duplicate names)
-	 * @param  string  $keysuffix      Suffix string to add into name and id of field (can be used to avoid duplicate names)
-	 * @return array|int               array_options set or 0 if no value
+	 * @param  array|string		$extrafieldsobjectkey  	array of extrafields (old usage) or value of object->table_element (new usage)
+	 * @param  string			$keyprefix      		Prefix string to add into name and id of field (can be used to avoid duplicate names)
+	 * @param  string			$keysuffix      		Suffix string to add into name and id of field (can be used to avoid duplicate names)
+	 * @return array|int								array_options set or 0 if no value
 	 */
-	function getOptionalsFromPost($extralabels,$keyprefix='',$keysuffix='')
+	function getOptionalsFromPost($extrafieldsobjectkey, $keyprefix='', $keysuffix='')
 	{
 		global $_POST;
 
-		if (is_array($this->attributes[$object->table_element]['label'])) $extralabels=$this->attributes[$object->table_element]['label'];
+		if (is_string($extrafieldsobjectkey) && is_array($this->attributes[$extrafieldsobjectkey]['label']))
+		{
+			$extralabels = $this->attributes[$extrafieldsobjectkey]['label'];
+		}
+		else
+		{
+			$extralabels = $extrafieldsobjectkey;
+		}
 
-		$array_options = array();
 		if (is_array($extralabels))
 		{
+			$array_options = array();
+
 			// Get extra fields
 			foreach ($extralabels as $key => $value)
 			{
-				$key_type = $this->attributes[$object->table_element]['type'][$key];
+				$key_type = '';
+				if (is_string($extrafieldsobjectkey))
+				{
+					$key_type = $this->attributes[$extrafieldsobjectkey]['type'][$key];
+				}
 
 				if (in_array($key_type,array('date','datetime')))
 				{
@@ -1987,8 +1999,7 @@ class ExtraFields
 
 			return $array_options;
 		}
-		else {
-			return 0;
-		}
+
+		return 0;
 	}
 }

+ 9 - 4
htdocs/core/class/html.formfile.class.php

@@ -132,6 +132,7 @@ class FormFile
 
 			if ($maxmin > 0)
 			{
+				// MAX_FILE_SIZE doit précéder le champ input de type file
 				$out .= '<input type="hidden" name="max_file_size" value="'.($maxmin*1024).'">';
 			}
 
@@ -1017,7 +1018,7 @@ class FormFile
 		global $form;
 
 		$disablecrop=1;
-		if (in_array($modulepart, array('societe','product','produit','service','expensereport','holiday','member','project','ticket','user'))) $disablecrop=0;
+		if (in_array($modulepart, array('expensereport','holiday','member','project','product','produit','service','societe','tax','ticket','user'))) $disablecrop=0;
 
 		// Define relative path used to store the file
 		if (empty($relativepath))
@@ -1218,9 +1219,13 @@ class FormFile
 							if (! dol_is_file($file['path'].'/'.$minifile)) $minifile=getImageFileNameForSize($file['name'], '_mini', '.png'); // For backward compatibility of old thumbs that were created with filename in lower case and with .png extension
 							//print $file['path'].'/'.$minifile.'<br>';
 
-							$urlforhref=getAdvancedPreviewUrl($modulepart, $relativepath.$fileinfo['filename'].'.'.strtolower($fileinfo['extension']), 0, '&entity='.(!empty($object->entity)?$object->entity:$conf->entity));
-							if (empty($urlforhref)) $urlforhref=DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.(!empty($object->entity)?$object->entity:$conf->entity).'&file='.urlencode($relativepath.$fileinfo['filename'].'.'.strtolower($fileinfo['extension']));
-							print '<a href="'.$urlforhref.'" class="aphoto" target="_blank">';
+							$urlforhref=getAdvancedPreviewUrl($modulepart, $relativepath.$fileinfo['filename'].'.'.strtolower($fileinfo['extension']), 1, '&entity='.(!empty($object->entity)?$object->entity:$conf->entity));
+							if (empty($urlforhref)) {
+								$urlforhref=DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.(!empty($object->entity)?$object->entity:$conf->entity).'&file='.urlencode($relativepath.$fileinfo['filename'].'.'.strtolower($fileinfo['extension']));
+								print '<a href="'.$urlforhref.'" class="aphoto" target="_blank">';
+							} else {
+								print '<a href="'.$urlforhref['url'].'" class="'.$urlforhref['css'].'" target="'.$urlforhref['target'].'" mime="'.$urlforhref['mime'].'">';
+							}
 							print '<img border="0" height="'.$maxheightmini.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.(!empty($object->entity)?$object->entity:$conf->entity).'&file='.urlencode($relativepath.$minifile).'" title="">';
 							print '</a>';
 						}

+ 11 - 5
htdocs/core/class/html.formprojet.class.php

@@ -317,14 +317,20 @@ class FormProjets
 	 *  @param	string	$morecss        More css added to the select component
 	 *  @param	string	$projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids.
 	 *  @param	string	$showproject	'all' = Show project info, ''=Hide project info
+	 *  @param	User	$usertofilter	User object to use for filtering
 	 *	@return int         			Nbr of project if OK, <0 if KO
 	 */
-	function selectTasks($socid=-1, $selected='', $htmlname='taskid', $maxlength=24, $option_only=0, $show_empty='1', $discard_closed=0, $forcefocus=0, $disabled=0, $morecss='maxwidth500', $projectsListId='', $showproject='all')
+	function selectTasks($socid=-1, $selected='', $htmlname='taskid', $maxlength=24, $option_only=0, $show_empty='1', $discard_closed=0, $forcefocus=0, $disabled=0, $morecss='maxwidth500', $projectsListId='', $showproject='all', $usertofilter=null)
 	{
 		global $user,$conf,$langs;
 
 		require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
 
+		if (is_null($usertofilter))
+		{
+			$usertofilter = $user;
+		}
+
 		$out='';
 
 		$hideunselectables = false;
@@ -332,10 +338,10 @@ class FormProjets
 
 		if (empty($projectsListId))
 		{
-			if (empty($user->rights->projet->all->lire))
+			if (empty($usertofilter->rights->projet->all->lire))
 			{
 				$projectstatic=new Project($this->db);
-				$projectsListId = $projectstatic->getProjectsAuthorizedForUser($user,0,1);
+				$projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter,0,1);
 			}
 		}
 
@@ -381,7 +387,7 @@ class FormProjets
 				{
 					$obj = $this->db->fetch_object($resql);
 					// If we ask to filter on a company and user has no permission to see all companies and project is linked to another company, we hide project.
-					if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && empty($user->rights->societe->lire))
+					if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && empty($usertofilter->rights->societe->lire))
 					{
 						// Do nothing
 					}
@@ -648,7 +654,7 @@ class FormProjets
 					$sellist .= '>';
 					if ($useshortlabel)
 					{
-						$finallabel = ($langs->transnoentitiesnoconv("OppStatusShort".$obj->code) != "OppStatusShort".$obj->code ? $langs->transnoentitiesnoconv("OppStatusShort".$obj->code) : $obj->label);
+						$finallabel = ($langs->transnoentitiesnoconv("OppStatus".$obj->code) != "OppStatus".$obj->code ? $langs->transnoentitiesnoconv("OppStatus".$obj->code) : $obj->label);
 					}
 					else
 					{

+ 2 - 2
htdocs/core/class/notify.class.php

@@ -631,13 +631,13 @@ class Notify
 						$mesg = $langs->transnoentitiesnoconv("EMailTextOrderValidated",$link);
 						break;
 					case 'PROPAL_VALIDATE':
-						$link='<a href="' . $urlwithroot . '/comm/propal/card.php?id='.$object->id . '">' . $newref . '</a>';
+						$link = '<a href="' . $urlwithroot . '/comm/propal/card.php?id='.$object->id . '">' . $newref . '</a>';
 						$dir_output = $conf->propal->multidir_output[$object->entity];
 						$object_type = 'propal';
 						$mesg = $langs->transnoentitiesnoconv("EMailTextProposalValidated",$link);
 						break;
 					case 'PROPAL_CLOSE_SIGNED':
-						$link='<a href="' . $urlwithroot . '/comm/propal/card.php?id='.$object->id . '">' . $newref . '</a>';
+						$link = '<a href="' . $urlwithroot . '/comm/propal/card.php?id='.$object->id . '">' . $newref . '</a>';
 						$dir_output = $conf->propal->multidir_output[$object->entity];
 						$object_type = 'propal';
 						$mesg = $langs->transnoentitiesnoconv("EMailTextProposalClosedSigned",$link);

+ 5 - 5
htdocs/core/class/translate.class.php

@@ -562,10 +562,6 @@ class Translate
         elseif (preg_match('/^PaymentTypeShort([0-9A-Z]+)$/i',$key,$reg))
         {
             $newstr=$this->getLabelFromKey($db,$reg[1],'c_paiement','code','libelle','',1);
-        }
-		elseif (preg_match('/^OppStatusShort([0-9A-Z]+)$/i',$key,$reg))
-        {
-            $newstr=$this->getLabelFromKey($db,$reg[1],'c_lead_status','code','label');
         }
         elseif (preg_match('/^OppStatus([0-9A-Z]+)$/i',$key,$reg))
         {
@@ -577,7 +573,11 @@ class Translate
             //$newstr=$this->getLabelFromKey($db,$reg[1],'c_ordersource','code','label');
         }
 
-        if (! empty($conf->global->MAIN_FEATURES_LEVEL) && $conf->global->MAIN_FEATURES_LEVEL >= 2) dol_syslog(__METHOD__." missing translation for key '".$newstr."' in ".$_SERVER["PHP_SELF"], LOG_DEBUG);
+        /* Disabled. There is too many cases where translation of $newstr is not defined is normal (like when output with setEventMessage an already translated string)
+        if (! empty($conf->global->MAIN_FEATURES_LEVEL) && $conf->global->MAIN_FEATURES_LEVEL >= 2)
+        {
+        	dol_syslog(__METHOD__." MAIN_FEATURES_LEVEL=DEVELOP: missing translation for key '".$newstr."' in ".$_SERVER["PHP_SELF"], LOG_DEBUG);
+        }*/
 
         return $newstr;
 	}

+ 1 - 1
htdocs/core/js/lib_head.js.php

@@ -918,7 +918,7 @@ function document_preview(file, type, title)
 		{
 			optionsbuttons = {
 			    "<?php echo dol_escape_js($langs->transnoentitiesnoconv("OriginalSize")); ?>": function() { console.log("Click on original size"); jQuery(".ui-dialog-content.ui-widget-content > object").css({ "max-height": "none" }); },
-				"<?php echo dol_escape_js($langs->transnoentitiesnoconv("Close")); ?>": function() { $( this ).dialog( "close" ); }
+				"<?php echo dol_escape_js($langs->transnoentitiesnoconv("CloseWindow")); ?>": function() { $( this ).dialog( "close" ); }
 				};
 		}
 

+ 17 - 6
htdocs/core/lib/admin.lib.php

@@ -944,7 +944,6 @@ function activateModule($value,$withdeps=1)
                 	$activate = false;
                 	foreach ($modulesdir as $dir)
                 	{
-                		var_dump($modulestring);
                 		if (file_exists($dir.$modulestring.".class.php"))
                 		{
                 			$resarray = activateModule($modulestring);
@@ -1354,7 +1353,8 @@ function complete_elementList_with_modules(&$elementList)
 /**
  *	Show array with constants to edit
  *
- *	@param	array	$tableau		Array of constants array('key'=>type, ) where type can be 'string', 'text', 'textarea', 'html', 'yesno', 'emailtemplate:xxx', ...
+ *	@param	array	$tableau		Array of constants array('key'=>array('type'=>type, 'label'=>label)
+ *									where type can be 'string', 'text', 'textarea', 'html', 'yesno', 'emailtemplate:xxx', ...
  *	@param	int		$strictw3c		0=Include form into table (deprecated), 1=Form is outside table to respect W3C (no form into table), 2=No form nor button at all
  *  @param  string  $helptext       Help
  *	@return	void
@@ -1378,17 +1378,28 @@ function form_constantes($tableau, $strictw3c=0, $helptext='')
     if (empty($strictw3c)) print '<td align="center" width="80">'.$langs->trans("Action").'</td>';
     print "</tr>\n";
 
+    $label='';
     $listofparam=array();
     foreach($tableau as $key => $const)	// Loop on each param
     {
+    	$label='';
     	// $const is a const key like 'MYMODULE_ABC'
-    	if (is_numeric($key)) {
+    	if (is_numeric($key)) {		// Very old behaviour
     		$type = 'string';
     	}
     	else
     	{
-    		$type = $const;
-    		$const = $key;
+    		if (is_array($const))
+    		{
+    			$type = $const['type'];
+				$label = $const['label'];
+    			$const = $key;
+    		}
+    		else
+    		{
+    			$type = $const;
+    			$const = $key;
+    		}
     	}
 
         $sql = "SELECT ";
@@ -1429,7 +1440,7 @@ function form_constantes($tableau, $strictw3c=0, $helptext='')
             print '<input type="hidden" name="constnote_'.$obj->name.'" value="'.nl2br(dol_escape_htmltag($obj->note)).'">';
             print '<input type="hidden" name="consttype_'.$obj->name.'" value="'.($obj->type?$obj->type:'string').'">';
 
-            print $langs->trans('Desc'.$const);
+            print ($label ? $label : $langs->trans('Desc'.$const));
 
             if ($const == 'ADHERENT_MAILMAN_URL')
             {

+ 5 - 3
htdocs/core/lib/company.lib.php

@@ -120,6 +120,7 @@ function societe_prepare_head(Societe $object)
     	$sql = "SELECT COUNT(n.rowid) as nb";
     	$sql.= " FROM ".MAIN_DB_PREFIX."projet as n";
     	$sql.= " WHERE fk_soc = ".$object->id;
+    	$sql.= " AND entity IN (".getEntity('project').")";
     	$resql=$db->query($sql);
     	if ($resql)
     	{
@@ -739,11 +740,12 @@ function show_projects($conf, $langs, $db, $object, $backtopage='', $nocreatelin
         print '<div class="div-table-responsive">';
         print "\n".'<table class="noborder" width=100%>';
 
-        $sql  = "SELECT p.rowid as id, p.title, p.ref, p.public, p.dateo as do, p.datee as de, p.fk_statut as status, p.fk_opp_status, p.opp_amount, p.opp_percent, p.tms as date_update, p.budget_amount";
+        $sql  = "SELECT p.rowid as id, p.entity, p.title, p.ref, p.public, p.dateo as do, p.datee as de, p.fk_statut as status, p.fk_opp_status, p.opp_amount, p.opp_percent, p.tms as date_update, p.budget_amount";
         $sql .= ", cls.code as opp_status_code";
         $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
         $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_lead_status as cls on p.fk_opp_status = cls.rowid";
         $sql .= " WHERE p.fk_soc = ".$object->id;
+        $sql .= " AND p.entity IN (".getEntity('project').")";
         $sql .= " ORDER BY p.dateo DESC";
 
         $result=$db->query($sql);
@@ -799,7 +801,7 @@ function show_projects($conf, $langs, $db, $object, $backtopage='', $nocreatelin
                         print '</td>';
                         // Opp status
                         print '<td align="center">';
-            			if ($obj->opp_status_code) print $langs->trans("OppStatusShort".$obj->opp_status_code);
+            			if ($obj->opp_status_code) print $langs->trans("OppStatus".$obj->opp_status_code);
             			print '</td>';
 			            // Opp percent
             			print '<td align="right">';
@@ -908,7 +910,7 @@ function show_contacts($conf,$langs,$db,$object,$backtopage='')
     {
     	if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha');
     }
-    $search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+    $search_array_options=$extrafields->getOptionalsFromPost($contactstatic->table_element,'','search_');
 
     // Purge search criteria
     if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') ||GETPOST('button_removefilter','alpha')) // All tests are required to be compatible with all browsers

+ 15 - 16
htdocs/core/lib/functions.lib.php

@@ -668,7 +668,7 @@ function dol_include_once($relpath, $classname='')
 
 
 /**
- *	Return path of url or filesystem. Return alternate root if exists.
+ *	Return path of url or filesystem. Can check into alternate dir or alternate dir + main dir depending on value of $returnemptyifnotfound.
  *
  * 	@param	string	$path						Relative path to file (if mode=0) or relative url (if mode=1). Ie: mydir/myfile, ../myfile
  *  @param	int		$type						0=Used for a Filesystem path, 1=Used for an URL path (output relative), 2=Used for an URL path (output full path using same host that current url), 3=Used for an URL path (output full path using host defined into $dolibarr_main_url_root of conf file)
@@ -688,7 +688,10 @@ function dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
 		$res = DOL_DOCUMENT_ROOT.'/'.$path;		// Standard default path
 		foreach ($conf->file->dol_document_root as $key => $dirroot)	// ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
 		{
-			if ($key == 'main') continue;
+			if ($key == 'main')
+			{
+				continue;
+			}
 			if (file_exists($dirroot.'/'.$path))
 			{
 				$res=$dirroot.'/'.$path;
@@ -1035,6 +1038,8 @@ function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename='
 		}
 		if ($level > $conf->global->SYSLOG_LEVEL) return;
 
+		$message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message);	// protection to avoid to have value of password in log
+
 		// If adding log inside HTML page is required
 		if (! empty($_REQUEST['logtohtml']) && (! empty($conf->global->MAIN_ENABLE_LOG_TO_HTML) || ! empty($conf->global->MAIN_LOGTOHTML)))   // MAIN_LOGTOHTML kept for backward compatibility
 		{
@@ -1042,7 +1047,7 @@ function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename='
 		}
 
 		//TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
-		// If enable html log tag enabled and url parameter log defined, we show output log on HTML comments
+		// If html log tag enabled and url parameter log defined, we show output log on HTML comments
 		if (! empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && ! empty($_GET["log"]))
 		{
 			print "\n\n<!-- Log start\n";
@@ -6808,7 +6813,7 @@ function dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id
  * Verify if condition in string is ok or not
  *
  * @param 	string		$strRights		String with condition to check
- * @return 	boolean						True or False. Return true if strRights is ''
+ * @return 	boolean						True or False. Return True if strRights is ''
  */
 function verifCond($strRights)
 {
@@ -6820,13 +6825,8 @@ function verifCond($strRights)
 	$rights = true;
 	if ($strRights != '')
 	{
-		//$tab_rights = explode('&&', $strRights);
-		//$i = 0;
-		//while (($i < count($tab_rights)) && ($rights == true)) {
 		$str = 'if(!(' . $strRights . ')) { $rights = false; }';
-		dol_eval($str);
-		//	$i++;
-		//}
+		dol_eval($str);		// The dol_eval must contains all the global $xxx used into a condition
 	}
 	return $rights;
 }
@@ -6843,8 +6843,8 @@ function verifCond($strRights)
 function dol_eval($s, $returnvalue=0, $hideerrors=1)
 {
 	// Only global variables can be changed by eval function and returned to caller
-	global $db, $langs, $user, $conf;
-	global $mainmenu, $leftmenu;
+	global $db, $langs, $user, $conf, $website, $websitepage;
+	global $action, $mainmenu, $leftmenu;
 	global $rights;
 	global $object;
 	global $mysoc;
@@ -7288,7 +7288,8 @@ function dol_getmypid()
  *                             			If param $mode is 1, can contains an operator <, > or = like "<10" or ">=100.5 < 1000"
  *                             			If param $mode is 2, can contains a list of int id separated by comma like "1,3,4"
  *                             			If param $mode is 3, can contains a list of string separated by comma like "a,b,c"
- * @param	integer			$mode		0=value is list of keyword strings, 1=value is a numeric test (Example ">5.5 <10"), 2=value is a list of id separated with comma (Example '1,3,4')
+ * @param	integer			$mode		0=value is list of keyword strings, 1=value is a numeric test (Example ">5.5 <10"), 2=value is a list of ID separated with comma (Example '1,3,4')
+ * 										3=value is list of string separated with comma (Example 'text 1,text 2'), 4=value is a list of ID separated with comma (Example '1,3,4') for search into a multiselect string ('1,2')
  * @param	integer			$nofirstand	1=Do not output the first 'AND'
  * @return 	string 			$res 		The statement to append to the SQL query
  */
@@ -7372,11 +7373,9 @@ function natural_search($fields, $value, $mode=0, $nofirstand=0)
 			else if ($mode == 4)
 			{
 			    $tmparray=explode(',',trim($crit));
-
 			    if (count($tmparray))
 			    {
 			        $listofcodes='';
-
 			        foreach($tmparray as $val)
 			        {
 			            if ($val)
@@ -7385,7 +7384,7 @@ function natural_search($fields, $value, $mode=0, $nofirstand=0)
 			                $newres .= ' OR '. $field . ' = \'' . $db->escape(trim($val)) . '\'';
 			                $newres .= ' OR '. $field . ' LIKE \'%,' . $db->escape(trim($val)) . '\'';
 			                $newres .= ' OR '. $field . ' LIKE \'%,' . $db->escape(trim($val)) . ',%\'';
-					$newres .= ')';
+			                $newres .= ')';
 			                $i2++;
 			            }
 			        }

+ 4 - 4
htdocs/core/menus/init_menu_auguria.sql

@@ -205,15 +205,15 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left
 -- insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->don->enabled && $leftmenu=="donations"', __HANDLER__, 'left', 2003__+MAX_llx_menu__, 'billing', '', 2000__+MAX_llx_menu__, '/don/stats/index.php?leftmenu=donations&amp;mainmenu=billing', 'Statistics', 1, 'donations', '$user->rights->don->lire', '', 2, 2, __ENTITY__);
 -- Special expenses
 insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled || $conf->salaries->enabled || $conf->loan->enabled || $conf->banque->enabled', __HANDLER__, 'left', 2200__+MAX_llx_menu__, 'billing', 'tax', 6__+MAX_llx_menu__, '/compta/charges/index.php?leftmenu=tax&amp;mainmenu=billing', 'MenuSpecialExpenses', 0, 'compta', '(! empty($conf->tax->enabled) && $user->rights->tax->charges->lire) || (! empty($conf->salaries->enabled) && ! empty($user->rights->salaries->read)) || (! empty($conf->loan->enabled) && $user->rights->loan->read) || (! empty($conf->banque->enabled) && $user->rights->banque->lire)', '', 0, 6, __ENTITY__);
-insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->salaries->enabled', __HANDLER__, 'left', 2210__+MAX_llx_menu__, 'billing', 'tax_sal', 2200__+MAX_llx_menu__, '/compta/salaries/index.php?leftmenu=tax_salary&amp;mainmenu=billing', 'Salaries', 1, 'salaries', '$user->rights->salaries->read', '', 0, 1, __ENTITY__);
+insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->salaries->enabled', __HANDLER__, 'left', 2210__+MAX_llx_menu__, 'billing', 'tax_sal', 2200__+MAX_llx_menu__, '/compta/salaries/list.php?leftmenu=tax_salary&amp;mainmenu=billing', 'Salaries', 1, 'salaries', '$user->rights->salaries->read', '', 0, 1, __ENTITY__);
 insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->salaries->enabled && $leftmenu=="tax_salary"', __HANDLER__, 'left', 2211__+MAX_llx_menu__, 'billing', '', 2210__+MAX_llx_menu__, '/compta/salaries/card.php?leftmenu=tax_salary&amp;action=create', 'NewPayment', 2, 'companies', '$user->rights->salaries->write', '', 0, 2, __ENTITY__);
-insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->salaries->enabled && $leftmenu=="tax_salary"', __HANDLER__, 'left', 2212__+MAX_llx_menu__, 'billing', '', 2210__+MAX_llx_menu__, '/compta/salaries/index.php?leftmenu=tax_salary', 'Payments', 2, 'companies', '$user->rights->salaries->read', '', 0, 3, __ENTITY__);
+insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->salaries->enabled && $leftmenu=="tax_salary"', __HANDLER__, 'left', 2212__+MAX_llx_menu__, 'billing', '', 2210__+MAX_llx_menu__, '/compta/salaries/list.php?leftmenu=tax_salary', 'Payments', 2, 'companies', '$user->rights->salaries->read', '', 0, 3, __ENTITY__);
 insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->salaries->enabled && $leftmenu=="tax_salary"', __HANDLER__, 'left', 2213__+MAX_llx_menu__, 'billing', '', 2210__+MAX_llx_menu__, '/compta/salaries/stats/index.php?leftmenu=tax_salary', 'Statistics', 2, 'companies', '$user->rights->salaries->read', '', 0, 4, __ENTITY__);
-insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->loan->enabled', __HANDLER__, 'left', 2220__+MAX_llx_menu__, 'billing', 'tax_loan', 2200__+MAX_llx_menu__, '/loan/index.php?leftmenu=tax_loan&amp;mainmenu=billing', 'Loans', 1, 'loan', '$user->rights->loan->read', '', 0, 1, __ENTITY__);
+insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->loan->enabled', __HANDLER__, 'left', 2220__+MAX_llx_menu__, 'billing', 'tax_loan', 2200__+MAX_llx_menu__, '/loan/list.php?leftmenu=tax_loan&amp;mainmenu=billing', 'Loans', 1, 'loan', '$user->rights->loan->read', '', 0, 1, __ENTITY__);
 insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->loan->enabled && $leftmenu=="tax_loan"', __HANDLER__, 'left', 2221__+MAX_llx_menu__, 'billing', '', 2220__+MAX_llx_menu__, '/loan/card.php?leftmenu=tax_loan&amp;action=create', 'NewLoan', 2, 'loan', '$user->rights->loan->write', '', 0, 2, __ENTITY__);
 --insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->loan->enabled && $leftmenu=="tax_loan"', __HANDLER__, 'left', 2222__+MAX_llx_menu__, 'billing', '', 2220__+MAX_llx_menu__, '/loan/payment/list.php?leftmenu=tax_loan', 'Payments', 2, 'companies', '$user->rights->loan->read', '', 0, 3, __ENTITY__);
 insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->loan->enabled && $leftmenu=="tax_loan" && ! empty($conf->global->LOAN_SHOW_CALCULATOR)', __HANDLER__, 'left', 2223__+MAX_llx_menu__, 'billing', '', 2220__+MAX_llx_menu__, '/loan/calc.php?leftmenu=tax_loan', 'Calculator', 2, 'companies', '$user->rights->loan->calc', '', 0, 4, __ENTITY__);
-insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled', __HANDLER__, 'left', 2250__+MAX_llx_menu__, 'billing', 'tax_social', 2200__+MAX_llx_menu__, '/compta/sociales/index.php?leftmenu=tax_social', 'SocialContributions', 1, '', '$user->rights->tax->charges->lire', '', 0, 1, __ENTITY__);
+insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled', __HANDLER__, 'left', 2250__+MAX_llx_menu__, 'billing', 'tax_social', 2200__+MAX_llx_menu__, '/compta/sociales/list.php?leftmenu=tax_social', 'SocialContributions', 1, '', '$user->rights->tax->charges->lire', '', 0, 1, __ENTITY__);
 insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && $leftmenu=="tax_social"', __HANDLER__, 'left', 2251__+MAX_llx_menu__, 'billing', '', 2250__+MAX_llx_menu__, '/compta/sociales/card.php?leftmenu=tax_social&amp;action=create', 'MenuNewSocialContribution', 2, '', '$user->rights->tax->charges->creer', '', 0, 2, __ENTITY__);
 insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && $leftmenu=="tax_social"', __HANDLER__, 'left', 2252__+MAX_llx_menu__, 'billing', '', 2250__+MAX_llx_menu__, '/compta/sociales/payments.php?leftmenu=tax_social&amp;mainmenu=billing&amp;mode=sconly', 'Payments', 2, '', '$user->rights->tax->charges->lire', '', 0, 3, __ENTITY__);
 insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS)', __HANDLER__, 'left', 2300__+MAX_llx_menu__, 'billing', 'tax_vat', 2200__+MAX_llx_menu__, '/compta/tva/list.php?leftmenu=tax_vat&amp;mainmenu=billing', 'VAT', 1, 'companies', '$user->rights->tax->charges->lire', '', 0, 7, __ENTITY__);

+ 5 - 5
htdocs/core/menus/standard/eldy.lib.php

@@ -906,9 +906,9 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
 				// Social contributions
 				if (! empty($conf->tax->enabled))
 				{
-					$newmenu->add("/compta/sociales/index.php?leftmenu=tax_social",$langs->trans("MenuSocialContributions"),1,$user->rights->tax->charges->lire);
+					$newmenu->add("/compta/sociales/list.php?leftmenu=tax_social",$langs->trans("MenuSocialContributions"),1,$user->rights->tax->charges->lire);
 					if ($usemenuhider || empty($leftmenu) || preg_match('/^tax_social/i',$leftmenu)) $newmenu->add("/compta/sociales/card.php?leftmenu=tax_social&action=create",$langs->trans("MenuNewSocialContribution"), 2, $user->rights->tax->charges->creer);
-					if ($usemenuhider || empty($leftmenu) || preg_match('/^tax_social/i',$leftmenu)) $newmenu->add("/compta/sociales/index.php?leftmenu=tax_social",$langs->trans("List"),2,$user->rights->tax->charges->lire);
+					if ($usemenuhider || empty($leftmenu) || preg_match('/^tax_social/i',$leftmenu)) $newmenu->add("/compta/sociales/list.php?leftmenu=tax_social",$langs->trans("List"),2,$user->rights->tax->charges->lire);
 					if ($usemenuhider || empty($leftmenu) || preg_match('/^tax_social/i',$leftmenu)) $newmenu->add("/compta/sociales/payments.php?leftmenu=tax_social&amp;mainmenu=billing&amp;mode=sconly",$langs->trans("Payments"), 2, $user->rights->tax->charges->lire);
 					// VAT
 					if (empty($conf->global->TAX_DISABLE_VAT_MENUS))
@@ -948,9 +948,9 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
 				if (! empty($conf->salaries->enabled))
 				{
 					$langs->load("salaries");
-					$newmenu->add("/compta/salaries/index.php?leftmenu=tax_salary&amp;mainmenu=billing",$langs->trans("Salaries"),1,$user->rights->salaries->read, '', $mainmenu, 'tax_salary');
+					$newmenu->add("/compta/salaries/list.php?leftmenu=tax_salary&amp;mainmenu=billing",$langs->trans("Salaries"),1,$user->rights->salaries->read, '', $mainmenu, 'tax_salary');
 					if ($usemenuhider || empty($leftmenu) || preg_match('/^tax_salary/i',$leftmenu)) $newmenu->add("/compta/salaries/card.php?leftmenu=tax_salary&action=create",$langs->trans("NewPayment"),2,$user->rights->salaries->write);
-					if ($usemenuhider || empty($leftmenu) || preg_match('/^tax_salary/i',$leftmenu)) $newmenu->add("/compta/salaries/index.php?leftmenu=tax_salary",$langs->trans("Payments"),2,$user->rights->salaries->read);
+					if ($usemenuhider || empty($leftmenu) || preg_match('/^tax_salary/i',$leftmenu)) $newmenu->add("/compta/salaries/list.php?leftmenu=tax_salary",$langs->trans("Payments"),2,$user->rights->salaries->read);
 					if ($usemenuhider || empty($leftmenu) || preg_match('/^tax_salary/i',$leftmenu)) $newmenu->add("/compta/salaries/stats/index.php?leftmenu=tax_salary", $langs->trans("Statistics"),2,$user->rights->salaries->read);
 				}
 
@@ -958,7 +958,7 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
 				if (! empty($conf->loan->enabled))
 				{
 					$langs->load("loan");
-					$newmenu->add("/loan/index.php?leftmenu=tax_loan&amp;mainmenu=billing",$langs->trans("Loans"),1,$user->rights->loan->read, '', $mainmenu, 'tax_loan');
+					$newmenu->add("/loan/list.php?leftmenu=tax_loan&amp;mainmenu=billing",$langs->trans("Loans"),1,$user->rights->loan->read, '', $mainmenu, 'tax_loan');
 					if ($usemenuhider || empty($leftmenu) || preg_match('/^tax_loan/i',$leftmenu)) $newmenu->add("/loan/card.php?leftmenu=tax_loan&action=create",$langs->trans("NewLoan"),2,$user->rights->loan->write);
 					//if (empty($leftmenu) || preg_match('/^tax_loan/i',$leftmenu)) $newmenu->add("/loan/payment/list.php?leftmenu=tax_loan",$langs->trans("Payments"),2,$user->rights->loan->read);
 				}

+ 2101 - 2126
htdocs/core/modules/DolibarrModules.class.php

@@ -35,2215 +35,2190 @@
  */
 class DolibarrModules // Can not be abstract, because we need to instantiate it into unActivateModule to be able to disable a module whose files were removed.
 {
-	/**
-	 * @var DoliDb Database handler
-	 */
-	public $db;
-
-	/**
-	 * @var int Module unique ID
-	 * @see https://wiki.dolibarr.org/index.php/List_of_modules_id
-	 */
-	public $numero;
-
-	/**
-	 * @var string Publisher name
-	 * @since 4.0.0
-	 */
-	public $editor_name;
-
-	/**
-	 * @var string URL of module at publisher site
-	 * @since 4.0.0
-	 */
-	public $editor_url;
-
-	/**
-	 * @var string Family
-	 * @see familyinfo
-	 *
-	 * Native values: 'crm', 'financial', 'hr', 'projects', 'products', 'ecm', 'technic', 'other'.
-	 * Use familyinfo to declare a custom value.
-	 */
-	public $family;
-
-	/**
-	 * @var array Custom family informations
-	 * @see family
-	 *
-	 * e.g.:
-	 * array(
-	 *     'myownfamily' => array(
-	 *         'position' => '001',
-	 *         'label' => $langs->trans("MyOwnFamily")
-	 *     )
-	 * );
-	 *
-	 */
-	public $familyinfo;
-
-	/**
-	 * @var string	Module position on 2 digits
-	 */
-	public $module_position='50';
-
-	/**
-	 * @var string Module name
-	 *
-	 * Only used if Module[ID]Name translation string is not found.
-	 *
-	 * You can use the following code to automatically derive it from your module's class name:
-	 * preg_replace('/^mod/i', '', get_class($this))
-	 */
-	public $name;
-
-	/**
-	 * @var string[] Paths to create when module is activated
-	 *
-	 * e.g.: array('/mymodule/temp')
-	 */
-	public $dirs = array();
-
-	/**
-	 * @var array Module boxes
-	 */
-	public $boxes = array();
-
-	/**
-	 * @var array Module constants
-	 */
-	public $const = array();
-
-	/**
-	 * @var array Module cron jobs entries
-	 */
-	public $cronjobs = array();
-
-	/**
-	 * @var array Module access rights
-	 */
-	public $rights;
-
-	/**
-	 * @var string Module access rights family
-	 */
-	public $rights_class;
-
-	/**
-	 * @var array Module menu entries
-	 */
-	public $menu = array();
-
-	/**
-	 * @var array Module parts
-	 *  array(
-	 *      // Set this to 1 if module has its own trigger directory (/mymodule/core/triggers)
-	 *      'triggers' => 0,
-	 *      // Set this to 1 if module has its own login method directory (/mymodule/core/login)
-	 *      'login' => 0,
-	 *      // Set this to 1 if module has its own substitution function file (/mymodule/core/substitutions)
-	 *      'substitutions' => 0,
-	 *      // Set this to 1 if module has its own menus handler directory (/mymodule/core/menus)
-	 *      'menus' => 0,
-	 *      // Set this to 1 if module has its own theme directory (/mymodule/theme)
-	 *      'theme' => 0,
-	 *      // Set this to 1 if module overwrite template dir (/mymodule/core/tpl)
-	 *      'tpl' => 0,
-	 *      // Set this to 1 if module has its own barcode directory (/mymodule/core/modules/barcode)
-	 *      'barcode' => 0,
-	 *      // Set this to 1 if module has its own models directory (/mymodule/core/modules/xxx)
-	 *      'models' => 0,
-	 *      // Set this to relative path of css file if module has its own css file
-	 *      'css' => '/mymodule/css/mymodule.css.php',
-	 *      // Set this to relative path of js file if module must load a js on all pages
-	 *      'js' => '/mymodule/js/mymodule.js',
-	 *      // Set here all hooks context managed by module
-	 *      'hooks' => array('hookcontext1','hookcontext2')
-	 *  )
-	 */
-	public $module_parts = array();
-
-	/**
-	 * @var string Module documents ?
-	 * @deprecated Seems unused anywhere
-	 */
-	public $docs;
-
-	/**
-	 * @var string ?
-	 * @deprecated Seems unused anywhere
-	 */
-	public $dbversion = "-";
-
-	/**
-	 * @var string Error message
-	 */
-	public $error;
-
-	/**
-	 * @var string Module version
-	 * @see http://semver.org
-	 *
-	 * The following keywords can also be used:
-	 * 'development'
-	 * 'experimental'
-	 * 'dolibarr': only for core modules that share its version
-	 * 'dolibarr_deprecated': only for deprecated core modules
-	 *
-	 */
-	public $version;
-
-	/**
-	 * @var string Module description (short text)
-	 *
-	 * Only used if Module[ID]Desc translation string is not found.
-	 */
-	public $description;
-
-	/**
-	 * @var string Module description (long text)
-	 * @since 4.0.0
-	 *
-	 * HTML content supported.
-	 */
-	public $descriptionlong;
-
-
-	// For exports
-
-	/**
-	 * @var string Module export code
-	 */
-	public $export_code;
-
-	/**
-	 * @var string Module export label
-	 */
-	public $export_label;
-
-	public $export_permission;
-	public $export_fields_array;
-	public $export_TypeFields_array;
-	public $export_entities_array;
-	public $export_special_array;           // special or computed field
-	public $export_dependencies_array;
-	public $export_sql_start;
-	public $export_sql_end;
-	public $export_sql_order;
-
-
-	// For import
-
-	/**
-	 * @var string Module import code
-	 */
-	public $import_code;
-
-	/**
-	 * @var string Module import label
-	 */
-	public $import_label;
-
-
-	/**
-	 * @var string Module constant name
-	 */
-	public $const_name;
-
-	/**
-	 * @var bool Module can't be disabled
-	 */
-	public $always_enabled;
-
-	/**
-	 * @var int Module is enabled globally (Multicompany support)
-	 */
-	public $core_enabled;
-
-	/**
-	 * @var string Relative path to module style sheet
-	 * @deprecated
-	 * @see module_parts
-	 */
-	public $style_sheet = '';
-
-	/**
-	 * @var 0|1|2|3 Where to display the module in setup page
-	 * @deprecated @since 4.0.0
-	 * @see family
-	 * @see familyinfo
-	 *
-	 * 0: common
-	 * 1: interface
-	 * 2: others
-	 * 3: very specific
-	 */
-	public $special;
-
-	/**
-	 * @var string Name of image file used for this module
-	 *
-	 * If file is in theme/yourtheme/img directory under name object_pictoname.png use 'pictoname'
-	 * If file is in module/img directory under name object_pictoname.png use 'pictoname@module'
-	 */
-	public $picto;
-
-	/**
-	 * @var string[] List of config pages
-	 *
-	 * Name of php pages stored into module/admin directory, used to setup module.
-	 * e.g.: "admin.php@module"
-	 */
-	public $config_page_url;
-
-
-	/**
-	 * @var string[] List of module class names that must be enabled if this module is enabled.
-	 *
-	 * e.g.: array('modAnotherModule', 'FR'=>'modYetAnotherModule')
-	 */
-	public $depends;
-
-	/**
-	 * @var int[] List of module ids to disable if this one is disabled.
-	 */
-	public $requiredby;
-
-	/**
-	 * @var string[] List of module class names as string this module is in conflict with.
-	 * @see depends
-	 */
-	public $conflictwith;
-
-	/**
-	 * @var string[] Module language files
-	 */
-	public $langfiles;
-
-	/**
-	 * @var array<string,string> Array of warnings to show when we activate the module
-	 *
-	 * array('always'='text') or array('FR'='text')
-	 */
-	public $warnings_activation;
-
-	/**
-	 * @var array<string,string> Array of warnings to show when we activate an external module
-	 *
-	 * array('always'='text') or array('FR'='text')
-	 */
-	public $warnings_activation_ext;
-
-
-	/**
-	 * @var array() Minimum version of PHP required by module.
-	 * e.g.: PHP ≥ 5.4 = array(5, 4)
-	 */
-	public $phpmin;
-
-	/**
-	 * @var array Minimum version of Dolibarr required by module.
-	 * e.g.: Dolibarr ≥ 3.6 = array(3, 6)
-	 */
-	public $need_dolibarr_version;
-
-	/**
-	 * @var bool Whether to hide the module.
-	 */
-	public $hidden = false;
-
-
-
-
-
-	/**
-	 * Constructor. Define names, constants, directories, boxes, permissions
-	 *
-	 * @param DoliDB		$db      Database handler
-	 */
-	public function __construct($db)
-	{
-		$this->db = $db;
-	}
-	// We should but can't set this as abstract because this will make dolibarr hang
-	// after migration due to old module not implementing. We must wait PHP is able to make
-	// a try catch on Fatal error to manage this correctly.
-	// We need constructor into function unActivateModule into admin.lib.php
-
-
-	/**
-	 * Enables a module.
-	 * Inserts all informations into database
-	 *
-	 * @param   array  		$array_sql  SQL requests to be executed when enabling module
-	 * @param   string      $options    String with options when disabling module:
-	 *                                  - 'noboxes' = Do not insert boxes
-	 *                                  - 'newboxdefonly' = For boxes, insert def of boxes only and not boxes activation
-	 *
-	 * @return  int                         1 if OK, 0 if KO
-	 */
-	function _init($array_sql, $options='')
-	{
-		global $conf;
-		$err=0;
-
-		$this->db->begin();
-
-		// Insert activation module constant
-		if (! $err) $err+=$this->_active();
-
-		// Insert new pages for tabs (into llx_const)
-		if (! $err) $err+=$this->insert_tabs();
-
-		// Insert activation of module's parts
-		if (! $err) $err+=$this->insert_module_parts();
-
-		// Insert constant defined by modules (into llx_const)
-		if (! $err && ! preg_match('/newboxdefonly/',$options)) $err+=$this->insert_const();	// Test on newboxdefonly to avoid to erase value during upgrade
-
-		// Insert boxes def into llx_boxes_def and boxes setup (into llx_boxes)
-		if (! $err && ! preg_match('/noboxes/',$options)) $err+=$this->insert_boxes($options);
-
-		// Insert cron job entries (entry in llx_cronjobs)
-		if (! $err) $err+=$this->insert_cronjobs();
-
-		// Insert permission definitions of module into llx_rights_def. If user is admin, grant this permission to user.
-		if (! $err) $err+=$this->insert_permissions(1, null, 1);
-
-		// Insert specific menus entries into database
-		if (! $err) $err+=$this->insert_menus();
-
-		// Create module's directories
-		if (! $err) $err+=$this->create_dirs();
-
-		// Execute addons requests
-		$num=count($array_sql);
-		for ($i = 0; $i < $num; $i++)
-		{
-			if (! $err)
-			{
-				$val=$array_sql[$i];
-				$sql=$val;
-				$ignoreerror=0;
-				if (is_array($val))
-				{
-					$sql=$val['sql'];
-					$ignoreerror=$val['ignoreerror'];
-				}
-				// Add current entity id
-				$sql=str_replace('__ENTITY__', $conf->entity, $sql);
-
-				dol_syslog(get_class($this)."::_init ignoreerror=".$ignoreerror."", LOG_DEBUG);
-				$result=$this->db->query($sql, $ignoreerror);
-				if (! $result)
-				{
-					if (! $ignoreerror)
-					{
-						$this->error=$this->db->lasterror();
-						$err++;
-					}
-					else
-					{
-						dol_syslog(get_class($this)."::_init Warning ".$this->db->lasterror(), LOG_WARNING);
-					}
-				}
-			}
-		}
-
-		// Return code
-		if (! $err)
-		{
-			$this->db->commit();
-			return 1;
-		}
-		else
-		{
-			$this->db->rollback();
-			return 0;
-		}
-	}
-
-	/**
-	 * Disable function. Deletes the module constants and boxes from the database.
-	 *
-	 * @param   string[]    $array_sql  SQL requests to be executed when module is disabled
-	 * @param   string      $options	Options when disabling module:
-	 *                                  - 'newboxdefonly|noboxes' = We don't remove boxes.
-	 *
-	 * @return  int                     1 if OK, 0 if KO
-	 */
-	function _remove($array_sql, $options='')
-	{
-		$err=0;
-
-		$this->db->begin();
-
-		// Remove activation module line (constant MAIN_MODULE_MYMODULE in llx_const)
-		if (! $err) $err+=$this->_unactive();
-
-		// Remove activation of module's new tabs (MAIN_MODULE_MYMODULE_TABS_XXX in llx_const)
-		if (! $err) $err+=$this->delete_tabs();
-
-		// Remove activation of module's parts (MAIN_MODULE_MYMODULE_XXX in llx_const)
-		if (! $err) $err+=$this->delete_module_parts();
-
-		// Remove constants defined by modules
-		if (! $err) $err+=$this->delete_const();
-
-		// Remove list of module's available boxes (entry in llx_boxes)
-		if (! $err && ! preg_match('/(newboxdefonly|noboxes)/',$options)) $err+=$this->delete_boxes();	// We don't have to delete if option ask to keep boxes safe or ask to add new box def only
-
-		// Remove list of module's cron job entries (entry in llx_cronjobs)
-		if (! $err) $err+=$this->delete_cronjobs();
-
-		// Remove module's permissions from list of available permissions (entries in llx_rights_def)
-		if (! $err) $err+=$this->delete_permissions();
-
-		// Remove module's menus (entries in llx_menu)
-		if (! $err) $err+=$this->delete_menus();
-
-		// Remove module's directories
-		if (! $err) $err+=$this->delete_dirs();
-
-		// Run complementary sql requests
-		$num=count($array_sql);
-		for ($i = 0; $i < $num; $i++)
-		{
-			if (! $err)
-			{
-				dol_syslog(get_class($this)."::_remove", LOG_DEBUG);
-				$result=$this->db->query($array_sql[$i]);
-				if (! $result)
-				{
-					$this->error=$this->db->error();
-					$err++;
-				}
-			}
-		}
-
-		// Return code
-		if (! $err)
-		{
-			$this->db->commit();
-			return 1;
-		}
-		else
-		{
-			$this->db->rollback();
-			return 0;
-		}
-	}
-
-
-	/**
-	 * Gives the translated module name if translation exists in admin.lang or into language files of module.
-	 * Otherwise return the module key name.
-	 *
-	 * @return  string  Translated module name
-	 */
-	function getName()
-	{
-		global $langs;
-		$langs->load("admin");
-
-		if ($langs->transnoentitiesnoconv("Module".$this->numero."Name") != ("Module".$this->numero."Name"))
-		{
-			// If module name translation exists
-			return $langs->transnoentitiesnoconv("Module".$this->numero."Name");
-		}
-		else
-		{
-			// If module name translation using it's unique id does not exist, we try to use its name to find translation
-			if (is_array($this->langfiles))
-			{
-				foreach($this->langfiles as $val)
-				{
-					if ($val) $langs->load($val);
-				}
-			}
-
-			if ($langs->trans("Module".$this->name."Name") != ("Module".$this->name."Name"))
-			{
-				// If module name translation exists
-				return $langs->transnoentitiesnoconv("Module".$this->name."Name");
-			}
-
-			// Last chance with simple label
-			return $langs->transnoentitiesnoconv($this->name);
-		}
-	}
-
-
-	/**
-	 * Gives the translated module description if translation exists in admin.lang or the default module description
-	 *
-	 * @return  string  Translated module description
-	 */
-	function getDesc()
-	{
-		global $langs;
-		$langs->load("admin");
-
-		if ($langs->transnoentitiesnoconv("Module".$this->numero."Desc") != ("Module".$this->numero."Desc"))
-		{
-			// If module description translation exists
-			return $langs->transnoentitiesnoconv("Module".$this->numero."Desc");
-		}
-		else
-		{
-			// If module description translation does not exist using its unique id, we can use its name to find translation
-			if (is_array($this->langfiles))
-			{
-				foreach($this->langfiles as $val)
-				{
-					if ($val) $langs->load($val);
-				}
-			}
-
-			if ($langs->transnoentitiesnoconv("Module".$this->name."Desc") != ("Module".$this->name."Desc"))
-			{
-				// If module name translation exists
-				return $langs->trans("Module".$this->name."Desc");
-			}
-
-			// Last chance with simple label
-			return $langs->trans($this->description);
-		}
-	}
-
-	/**
-	 * Gives the long description of a module. First check README-la_LA.md then README.md
-	 * If no markdown files found, it returns translated value of the key ->descriptionlong.
-	 *
-	 * @return  string     Long description of a module from README.md of from property.
-	 */
-	function getDescLong()
-	{
-		global $langs;
-		$langs->load("admin");
-
-		include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
-		include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
-
-		$pathoffile = $this->getDescLongReadmeFound();
-
-		if ($pathoffile)     // Mostly for external modules
-		{
-			$content = file_get_contents($pathoffile);
-
-			if ((float) DOL_VERSION >= 6.0)
-			{
-				@include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
-
-				$content = dolMd2Html($content, 'parsedown',
-					array(
-						'doc/'=>dol_buildpath(strtolower($this->name).'/doc/', 1),
-						'img/'=>dol_buildpath(strtolower($this->name).'/img/', 1),
-						'images/'=>dol_buildpath(strtolower($this->name).'/imgages/', 1),
-					));
-			}
-			else
-			{
-				$content = nl2br($content);
-			}
-		}
-		else                // Mostly for internal modules
-		{
-			if (! empty($this->descriptionlong))
-			{
-				if (is_array($this->langfiles))
-				{
-					foreach($this->langfiles as $val)
-					{
-						if ($val) $langs->load($val);
-					}
-				}
-
-				$content = $langs->transnoentitiesnoconv($this->descriptionlong);
-			}
-		}
-
-		return $content;
-	}
-
-	/**
-	 * Return path of file if a README file was found.
-	 *
-	 * @return  string      Path of file if a README file was found.
-	 */
-	function getDescLongReadmeFound()
-	{
-		global $langs;
-
-		$filefound= false;
-
-		// Define path to file README.md.
-		// First check README-la_LA.md then README-la.md then README.md
-		$pathoffile = dol_buildpath(strtolower($this->name).'/README-'.$langs->defaultlang.'.md', 0);
-		if (dol_is_file($pathoffile))
-		{
-			$filefound = true;
-		}
-		if (! $filefound)
-		{
-			$tmp=explode('_', $langs->defaultlang);
-			$pathoffile = dol_buildpath(strtolower($this->name).'/README-'.$tmp[0].'.md', 0);
-			if (dol_is_file($pathoffile))
-			{
-				$filefound = true;
-			}
-		}
-		if (! $filefound)
-		{
-			$pathoffile = dol_buildpath(strtolower($this->name).'/README.md', 0);
-			if (dol_is_file($pathoffile))
-			{
-				$filefound = true;
-			}
-		}
-
-		return ($filefound?$pathoffile:'');
-	}
-
-
-	/**
-	 * Gives the changelog. First check ChangeLog-la_LA.md then ChangeLog.md
-	 *
-	 * @return  string  Content of ChangeLog
-	 */
-	function getChangeLog()
-	{
-		global $langs;
-		$langs->load("admin");
-
-		include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
-		include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
-
-		$filefound= false;
-
-		// Define path to file README.md.
-		// First check README-la_LA.md then README.md
-		$pathoffile = dol_buildpath(strtolower($this->name).'/ChangeLog-'.$langs->defaultlang.'.md', 0);
-		if (dol_is_file($pathoffile))
-		{
-			$filefound = true;
-		}
-		if (! $filefound)
-		{
-			$pathoffile = dol_buildpath(strtolower($this->name).'/ChangeLog.md', 0);
-			if (dol_is_file($pathoffile))
-			{
-				$filefound = true;
-			}
-		}
-
-		if ($filefound)     // Mostly for external modules
-		{
-			$content = file_get_contents($pathoffile);
-
-			if ((float) DOL_VERSION >= 6.0)
-			{
-				@include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
-				$content = dolMd2Html($content, 'parsedown', array('doc/'=>dol_buildpath(strtolower($this->name).'/doc/', 1)));
-			}
-			else
-			{
-				$content = nl2br($content);
-			}
-		}
-
-		return $content;
-	}
-
-	/**
-	 * Gives the publisher name
-	 *
-	 * @return  string  Publisher name
-	 */
-	function getPublisher()
-	{
-		return $this->editor_name;
-	}
-
-	/**
-	 * Gives the publisher url
-	 *
-	 * @return  string  Publisher url
-	 */
-	function getPublisherUrl()
-	{
-		return $this->editor_url;
-	}
-
-	/**
-	 * Gives module version (translated if param $translated is on)
-	 * For 'experimental' modules, gives 'experimental' translation
-	 * For 'dolibarr' modules, gives Dolibarr version
-	 *
-	 * @param   int     $translated     1=Special version keys are translated, 0=Special version keys are not translated
-	 * @return  string                  Module version
-	 */
-	function getVersion($translated=1)
-	{
-		global $langs;
-		$langs->load("admin");
-
-		$ret='';
-
-		$newversion=preg_replace('/_deprecated/','',$this->version);
-		if ($newversion == 'experimental') $ret=($translated?$langs->transnoentitiesnoconv("VersionExperimental"):$newversion);
-		elseif ($newversion == 'development') $ret=($translated?$langs->transnoentitiesnoconv("VersionDevelopment"):$newversion);
-		elseif ($newversion == 'dolibarr') $ret=DOL_VERSION;
-		elseif ($newversion) $ret=$newversion;
-		else $ret=($translated?$langs->transnoentitiesnoconv("VersionUnknown"):'unknown');
-
-		if (preg_match('/_deprecated/',$this->version)) $ret.=($translated?' ('.$langs->transnoentitiesnoconv("Deprecated").')':$this->version);
-		return $ret;
-	}
-
-
-	/**
-	 * Tells if module is core or external
-	 *
-	 * @return  string  'core', 'external' or 'unknown'
-	 */
-	function isCoreOrExternalModule()
-	{
-		if ($this->version == 'dolibarr' || $this->version == 'dolibarr_deprecated') return 'core';
-		if (! empty($this->version) && ! in_array($this->version,array('experimental','development'))) return 'external';
-		if (! empty($this->editor_name) || ! empty($this->editor_url)) return 'external';
-		if ($this->numero >= 100000) return 'external';
-		return 'unknown';
-	}
-
-
-	/**
-	 * Gives module related language files list
-	 *
-	 * @return  string[]    Language files list
-	 */
-	function getLangFilesArray()
-	{
-		return $this->langfiles;
-	}
-
-	/**
-	 * Gives translated label of an export dataset
-	 *
-	 * @param   int     $r  Dataset index
-	 *
-	 * @return string       Translated databaset label
-	 */
-	function getExportDatasetLabel($r)
-	{
-		global $langs;
-
-		$langstring="ExportDataset_".$this->export_code[$r];
-		if ($langs->trans($langstring) == $langstring)
-		{
-			// Translation not found
-			return $langs->trans($this->export_label[$r]);
-		}
-		else
-		{
-			// Translation found
-			return $langs->trans($langstring);
-		}
-	}
-
-
-	/**
-	 * Gives translated label of an import dataset
-	 *
-	 * @param   int     $r  Dataset index
-	 *
-	 * @return  string      Translated dataset label
-	 */
-	function getImportDatasetLabel($r)
-	{
-		global $langs;
-
-		$langstring="ImportDataset_".$this->import_code[$r];
-		//print "x".$langstring;
-		if ($langs->trans($langstring) == $langstring)
-		{
-			// Translation not found
-			return $langs->transnoentitiesnoconv($this->import_label[$r]);
-		}
-		else
-		{
-			// Translation found
-			return $langs->transnoentitiesnoconv($langstring);
-		}
-	}
-
-
-	/**
-	 * Gives the last date of activation
-	 *
-	 * @return  timestamp       Date of last activation
-	 */
-	function getLastActivationDate()
-	{
-		global $conf;
-
-		$sql = "SELECT tms FROM ".MAIN_DB_PREFIX."const";
-		$sql.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
-		$sql.= " AND entity IN (0, ".$conf->entity.")";
-
-		dol_syslog(get_class($this)."::getLastActiveDate", LOG_DEBUG);
-		$resql=$this->db->query($sql);
-		if (! $resql) $err++;
-		else
-		{
-			$obj=$this->db->fetch_object($resql);
-			if ($obj) return $this->db->jdate($obj->tms);
-		}
-
-		return '';
-	}
-
-
-	/**
-	 * Gives the last author of activation
-	 *
-	 * @return  array       Array array('authorid'=>Id of last activation user, 'lastactivationdate'=>Date of last activation)
-	 */
-	function getLastActivationInfo()
-	{
-		global $conf;
-
-		$sql = "SELECT tms, note FROM ".MAIN_DB_PREFIX."const";
-		$sql.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
-		$sql.= " AND entity IN (0, ".$conf->entity.")";
-
-		dol_syslog(get_class($this)."::getLastActiveDate", LOG_DEBUG);
-		$resql=$this->db->query($sql);
-		if (! $resql) $err++;
-		else
-		{
-			$obj=$this->db->fetch_object($resql);
-			$tmp=array();
-			if ($obj->note)
-			{
-				$tmp=json_decode($obj->note, true);
-			}
-			if ($obj) return array('authorid'=>$tmp['authorid'], 'ip'=>$tmp['ip'], 'lastactivationdate'=>$this->db->jdate($obj->tms));
-		}
-
-		return array();
-	}
-
-
-	/**
-	 * Insert constants for module activation
-	 *
-	 * @return  int Error count (0 if OK)
-	 */
-	function _active()
-	{
-		global $conf, $user;
-
-		$err = 0;
-
-		// Common module
-		$entity = ((! empty($this->always_enabled) || ! empty($this->core_enabled)) ? 0 : $conf->entity);
-
-		$sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
-		$sql.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
-		$sql.= " AND entity IN (0, ".$entity.")";
-
-		dol_syslog(get_class($this)."::_active delete activation constant", LOG_DEBUG);
-		$resql=$this->db->query($sql);
-		if (! $resql) $err++;
-
-		$note=json_encode(array('authorid'=>(is_object($user)?$user->id:0), 'ip'=>(empty($_SERVER['REMOTE_ADDR'])?'':$_SERVER['REMOTE_ADDR'])));
-
-		$sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name, value, visible, entity, note) VALUES";
-		$sql.= " (".$this->db->encrypt($this->const_name,1);
-		$sql.= ", ".$this->db->encrypt('1',1);
-		$sql.= ", 0, ".$entity;
-		$sql.= ", '".$this->db->escape($note)."')";
-
-		dol_syslog(get_class($this)."::_active insert activation constant", LOG_DEBUG);
-		$resql=$this->db->query($sql);
-		if (! $resql) $err++;
-
-		return $err;
-	}
-
-
-	/**
-	 * Module deactivation
-	 *
-	 * @return  int Error count (0 if OK)
-	 */
-	function _unactive()
-	{
-		global $conf;
-
-		$err = 0;
-
-		// Common module
-		$entity = ((! empty($this->always_enabled) || ! empty($this->core_enabled)) ? 0 : $conf->entity);
-
-		$sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
-		$sql.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
-		$sql.= " AND entity IN (0, ".$entity.")";
-
-		dol_syslog(get_class($this)."::_unactive", LOG_DEBUG);
-		$this->db->query($sql);
-
-		return $err;
-	}
+    /**
+     * @var DoliDb Database handler
+     */
+    public $db;
+
+    /**
+     * @var int Module unique ID
+     * @see https://wiki.dolibarr.org/index.php/List_of_modules_id
+     */
+    public $numero;
+
+    /**
+     * @var   string Publisher name
+     * @since 4.0.0
+     */
+    public $editor_name;
+
+    /**
+     * @var   string URL of module at publisher site
+     * @since 4.0.0
+     */
+    public $editor_url;
+
+    /**
+     * @var string Family
+     * @see familyinfo
+     *
+     * Native values: 'crm', 'financial', 'hr', 'projects', 'products', 'ecm', 'technic', 'other'.
+     * Use familyinfo to declare a custom value.
+     */
+    public $family;
+
+    /**
+     * @var array Custom family informations
+     * @see family
+     *
+     * e.g.:
+     * array(
+     *     'myownfamily' => array(
+     *         'position' => '001',
+     *         'label' => $langs->trans("MyOwnFamily")
+     *     )
+     * );
+     */
+    public $familyinfo;
+
+    /**
+     * @var string    Module position on 2 digits
+     */
+    public $module_position='50';
+
+    /**
+     * @var string Module name
+     *
+     * Only used if Module[ID]Name translation string is not found.
+     *
+     * You can use the following code to automatically derive it from your module's class name:
+     * preg_replace('/^mod/i', '', get_class($this))
+     */
+    public $name;
+
+    /**
+     * @var string[] Paths to create when module is activated
+     *
+     * e.g.: array('/mymodule/temp')
+     */
+    public $dirs = array();
+
+    /**
+     * @var array Module boxes
+     */
+    public $boxes = array();
+
+    /**
+     * @var array Module constants
+     */
+    public $const = array();
+
+    /**
+     * @var array Module cron jobs entries
+     */
+    public $cronjobs = array();
+
+    /**
+     * @var array Module access rights
+     */
+    public $rights;
+
+    /**
+     * @var string Module access rights family
+     */
+    public $rights_class;
+
+    /**
+     * @var array Module menu entries
+     */
+    public $menu = array();
+
+    /**
+     * @var array Module parts
+     *  array(
+     *      // Set this to 1 if module has its own trigger directory (/mymodule/core/triggers)
+     *      'triggers' => 0,
+     *      // Set this to 1 if module has its own login method directory (/mymodule/core/login)
+     *      'login' => 0,
+     *      // Set this to 1 if module has its own substitution function file (/mymodule/core/substitutions)
+     *      'substitutions' => 0,
+     *      // Set this to 1 if module has its own menus handler directory (/mymodule/core/menus)
+     *      'menus' => 0,
+     *      // Set this to 1 if module has its own theme directory (/mymodule/theme)
+     *      'theme' => 0,
+     *      // Set this to 1 if module overwrite template dir (/mymodule/core/tpl)
+     *      'tpl' => 0,
+     *      // Set this to 1 if module has its own barcode directory (/mymodule/core/modules/barcode)
+     *      'barcode' => 0,
+     *      // Set this to 1 if module has its own models directory (/mymodule/core/modules/xxx)
+     *      'models' => 0,
+     *      // Set this to relative path of css file if module has its own css file
+     *      'css' => '/mymodule/css/mymodule.css.php',
+     *      // Set this to relative path of js file if module must load a js on all pages
+     *      'js' => '/mymodule/js/mymodule.js',
+     *      // Set here all hooks context managed by module
+     *      'hooks' => array('hookcontext1','hookcontext2')
+     *  )
+     */
+    public $module_parts = array();
+
+    /**
+     * @var        string Module documents ?
+     * @deprecated Seems unused anywhere
+     */
+    public $docs;
+
+    /**
+     * @var        string ?
+     * @deprecated Seems unused anywhere
+     */
+    public $dbversion = "-";
+
+    /**
+     * @var string Error message
+     */
+    public $error;
+
+    /**
+     * @var string Module version
+     * @see http://semver.org
+     *
+     * The following keywords can also be used:
+     * 'development'
+     * 'experimental'
+     * 'dolibarr': only for core modules that share its version
+     * 'dolibarr_deprecated': only for deprecated core modules
+     */
+    public $version;
+
+    /**
+     * @var string Module description (short text)
+     *
+     * Only used if Module[ID]Desc translation string is not found.
+     */
+    public $description;
+
+    /**
+     * @var   string Module description (long text)
+     * @since 4.0.0
+     *
+     * HTML content supported.
+     */
+    public $descriptionlong;
+
+
+    // For exports
+
+    /**
+     * @var string Module export code
+     */
+    public $export_code;
+
+    /**
+     * @var string Module export label
+     */
+    public $export_label;
+
+    public $export_permission;
+    public $export_fields_array;
+    public $export_TypeFields_array;
+    public $export_entities_array;
+    public $export_special_array;           // special or computed field
+    public $export_dependencies_array;
+    public $export_sql_start;
+    public $export_sql_end;
+    public $export_sql_order;
+
+
+    // For import
+
+    /**
+     * @var string Module import code
+     */
+    public $import_code;
+
+    /**
+     * @var string Module import label
+     */
+    public $import_label;
+
+
+    /**
+     * @var string Module constant name
+     */
+    public $const_name;
+
+    /**
+     * @var bool Module can't be disabled
+     */
+    public $always_enabled;
+
+    /**
+     * @var int Module is enabled globally (Multicompany support)
+     */
+    public $core_enabled;
+
+    /**
+     * @var        string Relative path to module style sheet
+     * @deprecated
+     * @see        module_parts
+     */
+    public $style_sheet = '';
+
+    /**
+     * @var        0|1|2|3 Where to display the module in setup page
+     * @deprecated @since 4.0.0
+     * @see        family
+     * @see        familyinfo
+     *
+     * 0: common
+     * 1: interface
+     * 2: others
+     * 3: very specific
+     */
+    public $special;
+
+    /**
+     * @var string Name of image file used for this module
+     *
+     * If file is in theme/yourtheme/img directory under name object_pictoname.png use 'pictoname'
+     * If file is in module/img directory under name object_pictoname.png use 'pictoname@module'
+     */
+    public $picto;
+
+    /**
+     * @var string[] List of config pages
+     *
+     * Name of php pages stored into module/admin directory, used to setup module.
+     * e.g.: "admin.php@module"
+     */
+    public $config_page_url;
+
+
+    /**
+     * @var string[] List of module class names that must be enabled if this module is enabled.
+     *
+     * e.g.: array('modAnotherModule', 'FR'=>'modYetAnotherModule')
+     */
+    public $depends;
+
+    /**
+     * @var int[] List of module ids to disable if this one is disabled.
+     */
+    public $requiredby;
+
+    /**
+     * @var string[] List of module class names as string this module is in conflict with.
+     * @see depends
+     */
+    public $conflictwith;
+
+    /**
+     * @var string[] Module language files
+     */
+    public $langfiles;
+
+    /**
+     * @var array<string,string> Array of warnings to show when we activate the module
+     *
+     * array('always'='text') or array('FR'='text')
+     */
+    public $warnings_activation;
+
+    /**
+     * @var array<string,string> Array of warnings to show when we activate an external module
+     *
+     * array('always'='text') or array('FR'='text')
+     */
+    public $warnings_activation_ext;
+
+
+    /**
+     * @var array() Minimum version of PHP required by module.
+     * e.g.: PHP ≥ 5.4 = array(5, 4)
+     */
+    public $phpmin;
+
+    /**
+     * @var array Minimum version of Dolibarr required by module.
+     * e.g.: Dolibarr ≥ 3.6 = array(3, 6)
+     */
+    public $need_dolibarr_version;
+
+    /**
+     * @var bool Whether to hide the module.
+     */
+    public $hidden = false;
+
+
+
+
+
+    /**
+     * Constructor. Define names, constants, directories, boxes, permissions
+     *
+     * @param DoliDB $db Database handler
+     */
+    public function __construct($db)
+    {
+        $this->db = $db;
+    }
+    // We should but can't set this as abstract because this will make dolibarr hang
+    // after migration due to old module not implementing. We must wait PHP is able to make
+    // a try catch on Fatal error to manage this correctly.
+    // We need constructor into function unActivateModule into admin.lib.php
+
+
+    /**
+     * Enables a module.
+     * Inserts all informations into database
+     *
+     * @param array  $array_sql SQL requests to be executed when enabling module
+     * @param string $options   String with options when disabling module:
+     *                          - 'noboxes' = Do not insert boxes -
+     *                          'newboxdefonly' = For boxes, insert def of
+     *                          boxes only and not boxes activation
+     *
+     * @return int                         1 if OK, 0 if KO
+     */
+    function _init($array_sql, $options='')
+    {
+        global $conf;
+        $err=0;
+
+        $this->db->begin();
+
+        // Insert activation module constant
+        if (! $err) { $err+=$this->_active();
+        }
+
+        // Insert new pages for tabs (into llx_const)
+        if (! $err) { $err+=$this->insert_tabs();
+        }
+
+        // Insert activation of module's parts
+        if (! $err) { $err+=$this->insert_module_parts();
+        }
+
+        // Insert constant defined by modules (into llx_const)
+        if (! $err && ! preg_match('/newboxdefonly/', $options)) { $err+=$this->insert_const();    // Test on newboxdefonly to avoid to erase value during upgrade
+        }
+
+        // Insert boxes def into llx_boxes_def and boxes setup (into llx_boxes)
+        if (! $err && ! preg_match('/noboxes/', $options)) { $err+=$this->insert_boxes($options);
+        }
+
+        // Insert cron job entries (entry in llx_cronjobs)
+        if (! $err) { $err+=$this->insert_cronjobs();
+        }
+
+        // Insert permission definitions of module into llx_rights_def. If user is admin, grant this permission to user.
+        if (! $err) { $err+=$this->insert_permissions(1, null, 1);
+        }
+
+        // Insert specific menus entries into database
+        if (! $err) { $err+=$this->insert_menus();
+        }
+
+        // Create module's directories
+        if (! $err) { $err+=$this->create_dirs();
+        }
+
+        // Execute addons requests
+        $num=count($array_sql);
+        for ($i = 0; $i < $num; $i++)
+        {
+            if (! $err) {
+                $val=$array_sql[$i];
+                $sql=$val;
+                $ignoreerror=0;
+                if (is_array($val)) {
+                    $sql=$val['sql'];
+                    $ignoreerror=$val['ignoreerror'];
+                }
+                // Add current entity id
+                $sql=str_replace('__ENTITY__', $conf->entity, $sql);
+
+                dol_syslog(get_class($this)."::_init ignoreerror=".$ignoreerror."", LOG_DEBUG);
+                $result=$this->db->query($sql, $ignoreerror);
+                if (! $result) {
+                    if (! $ignoreerror) {
+                         $this->error=$this->db->lasterror();
+                         $err++;
+                    }
+                    else
+                    {
+                         dol_syslog(get_class($this)."::_init Warning ".$this->db->lasterror(), LOG_WARNING);
+                    }
+                }
+            }
+        }
+
+        // Return code
+        if (! $err) {
+            $this->db->commit();
+            return 1;
+        }
+        else
+        {
+            $this->db->rollback();
+            return 0;
+        }
+    }
+
+    /**
+     * Disable function. Deletes the module constants and boxes from the database.
+     *
+     * @param string[] $array_sql SQL requests to be executed when module is disabled
+     * @param string   $options   Options when disabling module:
+     *
+     * @return int                     1 if OK, 0 if KO
+     */
+    function _remove($array_sql, $options='')
+    {
+        $err=0;
+
+        $this->db->begin();
+
+        // Remove activation module line (constant MAIN_MODULE_MYMODULE in llx_const)
+        if (! $err) { $err+=$this->_unactive();
+        }
+
+        // Remove activation of module's new tabs (MAIN_MODULE_MYMODULE_TABS_XXX in llx_const)
+        if (! $err) { $err+=$this->delete_tabs();
+        }
+
+        // Remove activation of module's parts (MAIN_MODULE_MYMODULE_XXX in llx_const)
+        if (! $err) { $err+=$this->delete_module_parts();
+        }
+
+        // Remove constants defined by modules
+        if (! $err) { $err+=$this->delete_const();
+        }
+
+        // Remove list of module's available boxes (entry in llx_boxes)
+        if (! $err && ! preg_match('/(newboxdefonly|noboxes)/', $options)) { $err+=$this->delete_boxes();    // We don't have to delete if option ask to keep boxes safe or ask to add new box def only
+        }
+
+        // Remove list of module's cron job entries (entry in llx_cronjobs)
+        if (! $err) { $err+=$this->delete_cronjobs();
+        }
+
+        // Remove module's permissions from list of available permissions (entries in llx_rights_def)
+        if (! $err) { $err+=$this->delete_permissions();
+        }
+
+        // Remove module's menus (entries in llx_menu)
+        if (! $err) { $err+=$this->delete_menus();
+        }
+
+        // Remove module's directories
+        if (! $err) { $err+=$this->delete_dirs();
+        }
+
+        // Run complementary sql requests
+        $num=count($array_sql);
+        for ($i = 0; $i < $num; $i++)
+        {
+            if (! $err) {
+                dol_syslog(get_class($this)."::_remove", LOG_DEBUG);
+                $result=$this->db->query($array_sql[$i]);
+                if (! $result) {
+                    $this->error=$this->db->error();
+                    $err++;
+                }
+            }
+        }
+
+        // Return code
+        if (! $err) {
+            $this->db->commit();
+            return 1;
+        }
+        else
+        {
+            $this->db->rollback();
+            return 0;
+        }
+    }
+
+
+    /**
+     * Gives the translated module name if translation exists in admin.lang or into language files of module.
+     * Otherwise return the module key name.
+     *
+     * @return string  Translated module name
+     */
+    function getName()
+    {
+        global $langs;
+        $langs->load("admin");
+
+        if ($langs->transnoentitiesnoconv("Module".$this->numero."Name") != ("Module".$this->numero."Name")) {
+            // If module name translation exists
+            return $langs->transnoentitiesnoconv("Module".$this->numero."Name");
+        }
+        else
+        {
+            // If module name translation using it's unique id does not exist, we try to use its name to find translation
+            if (is_array($this->langfiles)) {
+                foreach($this->langfiles as $val)
+                {
+                    if ($val) { $langs->load($val);
+                    }
+                }
+            }
+
+            if ($langs->trans("Module".$this->name."Name") != ("Module".$this->name."Name")) {
+                // If module name translation exists
+                return $langs->transnoentitiesnoconv("Module".$this->name."Name");
+            }
+
+            // Last chance with simple label
+            return $langs->transnoentitiesnoconv($this->name);
+        }
+    }
+
+
+    /**
+     * Gives the translated module description if translation exists in admin.lang or the default module description
+     *
+     * @return string  Translated module description
+     */
+    function getDesc()
+    {
+        global $langs;
+        $langs->load("admin");
+
+        if ($langs->transnoentitiesnoconv("Module".$this->numero."Desc") != ("Module".$this->numero."Desc")) {
+            // If module description translation exists
+            return $langs->transnoentitiesnoconv("Module".$this->numero."Desc");
+        }
+        else
+        {
+            // If module description translation does not exist using its unique id, we can use its name to find translation
+            if (is_array($this->langfiles)) {
+                foreach($this->langfiles as $val)
+                {
+                    if ($val) { $langs->load($val);
+                    }
+                }
+            }
+
+            if ($langs->transnoentitiesnoconv("Module".$this->name."Desc") != ("Module".$this->name."Desc")) {
+                // If module name translation exists
+                return $langs->trans("Module".$this->name."Desc");
+            }
+
+            // Last chance with simple label
+            return $langs->trans($this->description);
+        }
+    }
+
+    /**
+     * Gives the long description of a module. First check README-la_LA.md then README.md
+     * If no markdown files found, it returns translated value of the key ->descriptionlong.
+     *
+     * @return string     Long description of a module from README.md of from property.
+     */
+    function getDescLong()
+    {
+        global $langs;
+        $langs->load("admin");
+
+        include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
+        include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
+
+        $pathoffile = $this->getDescLongReadmeFound();
+
+        if ($pathoffile)     // Mostly for external modules
+        {
+            $content = file_get_contents($pathoffile);
+
+            if ((float) DOL_VERSION >= 6.0) {
+                @include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
+
+                $content = dolMd2Html(
+                    $content, 'parsedown',
+                    array(
+                    'doc/'=>dol_buildpath(strtolower($this->name).'/doc/', 1),
+                    'img/'=>dol_buildpath(strtolower($this->name).'/img/', 1),
+                    'images/'=>dol_buildpath(strtolower($this->name).'/imgages/', 1),
+                    )
+                );
+            }
+            else
+            {
+                $content = nl2br($content);
+            }
+        }
+        else                // Mostly for internal modules
+        {
+            if (! empty($this->descriptionlong)) {
+                if (is_array($this->langfiles)) {
+                    foreach($this->langfiles as $val)
+                    {
+                        if ($val) { $langs->load($val);
+                        }
+                    }
+                }
+
+                $content = $langs->transnoentitiesnoconv($this->descriptionlong);
+            }
+        }
+
+        return $content;
+    }
+
+    /**
+     * Return path of file if a README file was found.
+     *
+     * @return string      Path of file if a README file was found.
+     */
+    function getDescLongReadmeFound()
+    {
+        global $langs;
+
+        $filefound= false;
+
+        // Define path to file README.md.
+        // First check README-la_LA.md then README-la.md then README.md
+        $pathoffile = dol_buildpath(strtolower($this->name).'/README-'.$langs->defaultlang.'.md', 0);
+        if (dol_is_file($pathoffile)) {
+            $filefound = true;
+        }
+        if (! $filefound) {
+            $tmp=explode('_', $langs->defaultlang);
+            $pathoffile = dol_buildpath(strtolower($this->name).'/README-'.$tmp[0].'.md', 0);
+            if (dol_is_file($pathoffile)) {
+                $filefound = true;
+            }
+        }
+        if (! $filefound) {
+            $pathoffile = dol_buildpath(strtolower($this->name).'/README.md', 0);
+            if (dol_is_file($pathoffile)) {
+                $filefound = true;
+            }
+        }
+
+        return ($filefound?$pathoffile:'');
+    }
+
+
+    /**
+     * Gives the changelog. First check ChangeLog-la_LA.md then ChangeLog.md
+     *
+     * @return string  Content of ChangeLog
+     */
+    function getChangeLog()
+    {
+        global $langs;
+        $langs->load("admin");
+
+        include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
+        include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
+
+        $filefound= false;
+
+        // Define path to file README.md.
+        // First check README-la_LA.md then README.md
+        $pathoffile = dol_buildpath(strtolower($this->name).'/ChangeLog-'.$langs->defaultlang.'.md', 0);
+        if (dol_is_file($pathoffile)) {
+            $filefound = true;
+        }
+        if (! $filefound) {
+            $pathoffile = dol_buildpath(strtolower($this->name).'/ChangeLog.md', 0);
+            if (dol_is_file($pathoffile)) {
+                $filefound = true;
+            }
+        }
+
+        if ($filefound)     // Mostly for external modules
+        {
+            $content = file_get_contents($pathoffile);
+
+            if ((float) DOL_VERSION >= 6.0) {
+                @include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
+                $content = dolMd2Html($content, 'parsedown', array('doc/'=>dol_buildpath(strtolower($this->name).'/doc/', 1)));
+            }
+            else
+            {
+                $content = nl2br($content);
+            }
+        }
+
+        return $content;
+    }
+
+    /**
+     * Gives the publisher name
+     *
+     * @return string  Publisher name
+     */
+    function getPublisher()
+    {
+        return $this->editor_name;
+    }
+
+    /**
+     * Gives the publisher url
+     *
+     * @return string  Publisher url
+     */
+    function getPublisherUrl()
+    {
+        return $this->editor_url;
+    }
+
+    /**
+     * Gives module version (translated if param $translated is on)
+     * For 'experimental' modules, gives 'experimental' translation
+     * For 'dolibarr' modules, gives Dolibarr version
+     *
+     * @param  int $translated 1=Special version keys are translated, 0=Special version keys are not translated
+     * @return string                  Module version
+     */
+    function getVersion($translated=1)
+    {
+        global $langs;
+        $langs->load("admin");
+
+        $ret='';
+
+        $newversion=preg_replace('/_deprecated/', '', $this->version);
+        if ($newversion == 'experimental') { $ret=($translated?$langs->transnoentitiesnoconv("VersionExperimental"):$newversion);
+        } elseif ($newversion == 'development') { $ret=($translated?$langs->transnoentitiesnoconv("VersionDevelopment"):$newversion);
+        } elseif ($newversion == 'dolibarr') { $ret=DOL_VERSION;
+        } elseif ($newversion) { $ret=$newversion;
+        } else { $ret=($translated?$langs->transnoentitiesnoconv("VersionUnknown"):'unknown');
+        }
+
+        if (preg_match('/_deprecated/', $this->version)) { $ret.=($translated?' ('.$langs->transnoentitiesnoconv("Deprecated").')':$this->version);
+        }
+        return $ret;
+    }
+
+
+    /**
+     * Tells if module is core or external
+     *
+     * @return string  'core', 'external' or 'unknown'
+     */
+    function isCoreOrExternalModule()
+    {
+        if ($this->version == 'dolibarr' || $this->version == 'dolibarr_deprecated') { return 'core';
+        }
+        if (! empty($this->version) && ! in_array($this->version, array('experimental','development'))) { return 'external';
+        }
+        if (! empty($this->editor_name) || ! empty($this->editor_url)) { return 'external';
+        }
+        if ($this->numero >= 100000) { return 'external';
+        }
+        return 'unknown';
+    }
+
+
+    /**
+     * Gives module related language files list
+     *
+     * @return string[]    Language files list
+     */
+    function getLangFilesArray()
+    {
+        return $this->langfiles;
+    }
+
+    /**
+     * Gives translated label of an export dataset
+     *
+     * @param int $r Dataset index
+     *
+     * @return string       Translated databaset label
+     */
+    function getExportDatasetLabel($r)
+    {
+        global $langs;
+
+        $langstring="ExportDataset_".$this->export_code[$r];
+        if ($langs->trans($langstring) == $langstring) {
+            // Translation not found
+            return $langs->trans($this->export_label[$r]);
+        }
+        else
+        {
+            // Translation found
+            return $langs->trans($langstring);
+        }
+    }
+
+
+    /**
+     * Gives translated label of an import dataset
+     *
+     * @param int $r Dataset index
+     *
+     * @return string      Translated dataset label
+     */
+    function getImportDatasetLabel($r)
+    {
+        global $langs;
+
+        $langstring="ImportDataset_".$this->import_code[$r];
+        //print "x".$langstring;
+        if ($langs->trans($langstring) == $langstring) {
+            // Translation not found
+            return $langs->transnoentitiesnoconv($this->import_label[$r]);
+        }
+        else
+        {
+            // Translation found
+            return $langs->transnoentitiesnoconv($langstring);
+        }
+    }
+
+
+    /**
+     * Gives the last date of activation
+     *
+     * @return timestamp       Date of last activation
+     */
+    function getLastActivationDate()
+    {
+        global $conf;
+
+        $sql = "SELECT tms FROM ".MAIN_DB_PREFIX."const";
+        $sql.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
+        $sql.= " AND entity IN (0, ".$conf->entity.")";
+
+        dol_syslog(get_class($this)."::getLastActiveDate", LOG_DEBUG);
+        $resql=$this->db->query($sql);
+        if (! $resql) { $err++;
+        } else
+        {
+            $obj=$this->db->fetch_object($resql);
+            if ($obj) { return $this->db->jdate($obj->tms);
+            }
+        }
+
+        return '';
+    }
+
+
+    /**
+     * Gives the last author of activation
+     *
+     * @return array       Array array('authorid'=>Id of last activation user, 'lastactivationdate'=>Date of last activation)
+     */
+    function getLastActivationInfo()
+    {
+        global $conf;
+
+        $sql = "SELECT tms, note FROM ".MAIN_DB_PREFIX."const";
+        $sql.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
+        $sql.= " AND entity IN (0, ".$conf->entity.")";
+
+        dol_syslog(get_class($this)."::getLastActiveDate", LOG_DEBUG);
+        $resql=$this->db->query($sql);
+        if (! $resql) { $err++;
+        } else
+        {
+            $obj=$this->db->fetch_object($resql);
+            $tmp=array();
+            if ($obj->note) {
+                $tmp=json_decode($obj->note, true);
+            }
+            if ($obj) { return array('authorid'=>$tmp['authorid'], 'ip'=>$tmp['ip'], 'lastactivationdate'=>$this->db->jdate($obj->tms));
+            }
+        }
+
+        return array();
+    }
+
+
+    /**
+     * Insert constants for module activation
+     *
+     * @return int Error count (0 if OK)
+     */
+    function _active()
+    {
+        global $conf, $user;
+
+        $err = 0;
+
+        // Common module
+        $entity = ((! empty($this->always_enabled) || ! empty($this->core_enabled)) ? 0 : $conf->entity);
+
+        $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
+        $sql.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
+        $sql.= " AND entity IN (0, ".$entity.")";
+
+        dol_syslog(get_class($this)."::_active delete activation constant", LOG_DEBUG);
+        $resql=$this->db->query($sql);
+        if (! $resql) { $err++;
+        }
+
+        $note=json_encode(array('authorid'=>(is_object($user)?$user->id:0), 'ip'=>(empty($_SERVER['REMOTE_ADDR'])?'':$_SERVER['REMOTE_ADDR'])));
+
+        $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name, value, visible, entity, note) VALUES";
+        $sql.= " (".$this->db->encrypt($this->const_name, 1);
+        $sql.= ", ".$this->db->encrypt('1', 1);
+        $sql.= ", 0, ".$entity;
+        $sql.= ", '".$this->db->escape($note)."')";
+
+        dol_syslog(get_class($this)."::_active insert activation constant", LOG_DEBUG);
+        $resql=$this->db->query($sql);
+        if (! $resql) { $err++;
+        }
+
+        return $err;
+    }
+
+
+    /**
+     * Module deactivation
+     *
+     * @return int Error count (0 if OK)
+     */
+    function _unactive()
+    {
+        global $conf;
+
+        $err = 0;
+
+        // Common module
+        $entity = ((! empty($this->always_enabled) || ! empty($this->core_enabled)) ? 0 : $conf->entity);
+
+        $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
+        $sql.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
+        $sql.= " AND entity IN (0, ".$entity.")";
+
+        dol_syslog(get_class($this)."::_unactive", LOG_DEBUG);
+        $this->db->query($sql);
+
+        return $err;
+    }
 
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Create tables and keys required by module.
-	 * Files module.sql and module.key.sql with create table and create keys
-	 * commands must be stored in directory reldir='/module/sql/'
-	 * This function is called by this->init
-	 *
-	 * @param   string  $reldir Relative directory where to scan files
-	 * @return  int             <=0 if KO, >0 if OK
-	 */
-	function _load_tables($reldir)
-	{
+    /**
+     * Create tables and keys required by module.
+     * Files module.sql and module.key.sql with create table and create keys
+     * commands must be stored in directory reldir='/module/sql/'
+     * This function is called by this->init
+     *
+     * @param  string $reldir Relative directory where to scan files
+     * @return int             <=0 if KO, >0 if OK
+     */
+    function _load_tables($reldir)
+    {
         // phpcs:enable
-		global $conf;
-
-		$error=0;
-		$dirfound=0;
-
-		if (empty($reldir)) return 1;
-
-		include_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php';
-
-		$ok = 1;
-		foreach($conf->file->dol_document_root as $dirroot)
-		{
-			if ($ok)
-			{
-				$dir = $dirroot.$reldir;
-				$ok = 0;
-
-				$handle=@opendir($dir);         // Dir may not exists
-				if (is_resource($handle))
-				{
-					$dirfound++;
-
-					// Run llx_mytable.sql files, then llx_mytable_*.sql
-					$files = array();
-					while (($file = readdir($handle))!==false)
-					{
-						$files[] = $file;
-					}
-					sort($files);
-					foreach ($files as $file)
-					{
-						if (preg_match('/\.sql$/i',$file) && ! preg_match('/\.key\.sql$/i',$file) && substr($file,0,4) == 'llx_' && substr($file,0,4) != 'data')
-						{
-							$result=run_sql($dir.$file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG)?1:0, '', 1);
-							if ($result <= 0) $error++;
-						}
-					}
-
-					rewinddir($handle);
-
-					// Run llx_mytable.key.sql files (Must be done after llx_mytable.sql) then then llx_mytable_*.key.sql
-					$files = array();
-					while (($file = readdir($handle))!==false)
-					{
-						$files[] = $file;
-					}
-					sort($files);
-					foreach ($files as $file)
-					{
-						if (preg_match('/\.key\.sql$/i',$file) && substr($file,0,4) == 'llx_' && substr($file,0,4) != 'data')
-						{
-							$result=run_sql($dir.$file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG)?1:0, '', 1);
-							if ($result <= 0) $error++;
-						}
-					}
-
-					rewinddir($handle);
-
-					// Run data_xxx.sql files (Must be done after llx_mytable.key.sql)
-					$files = array();
-					while (($file = readdir($handle))!==false)
-					{
-						$files[] = $file;
-					}
-					sort($files);
-					foreach ($files as $file)
-					{
-						if (preg_match('/\.sql$/i',$file) && ! preg_match('/\.key\.sql$/i',$file) && substr($file,0,4) == 'data')
-						{
-							$result=run_sql($dir.$file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG)?1:0, '', 1);
-							if ($result <= 0) $error++;
-						}
-					}
-
-					rewinddir($handle);
-
-					// Run update_xxx.sql files
-					$files = array();
-					while (($file = readdir($handle))!==false)
-					{
-						$files[] = $file;
-					}
-					sort($files);
-					foreach ($files as $file)
-					{
-						if (preg_match('/\.sql$/i',$file) && ! preg_match('/\.key\.sql$/i',$file) && substr($file,0,6) == 'update')
-						{
-							$result=run_sql($dir.$file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG)?1:0, '', 1);
-							if ($result <= 0) $error++;
-						}
-					}
-
-					closedir($handle);
-				}
-
-				if ($error == 0)
-				{
-					$ok = 1;
-				}
-			}
-		}
-
-		if (! $dirfound) dol_syslog("A module ask to load sql files into ".$reldir." but this directory was not found.", LOG_WARNING);
-		return $ok;
-	}
+        global $conf;
+
+        $error=0;
+        $dirfound=0;
+
+        if (empty($reldir)) { return 1;
+        }
+
+        include_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php';
+
+        $ok = 1;
+        foreach($conf->file->dol_document_root as $dirroot)
+        {
+            if ($ok) {
+                $dir = $dirroot.$reldir;
+                $ok = 0;
+
+                $handle=@opendir($dir);         // Dir may not exists
+                if (is_resource($handle)) {
+                    $dirfound++;
+
+                    // Run llx_mytable.sql files, then llx_mytable_*.sql
+                    $files = array();
+                    while (($file = readdir($handle))!==false)
+                    {
+                        $files[] = $file;
+                    }
+                    sort($files);
+                    foreach ($files as $file)
+                    {
+                        if (preg_match('/\.sql$/i', $file) && ! preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_' && substr($file, 0, 4) != 'data') {
+                            $result=run_sql($dir.$file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG)?1:0, '', 1);
+                            if ($result <= 0) { $error++;
+                            }
+                        }
+                    }
+
+                    rewinddir($handle);
+
+                    // Run llx_mytable.key.sql files (Must be done after llx_mytable.sql) then then llx_mytable_*.key.sql
+                    $files = array();
+                    while (($file = readdir($handle))!==false)
+                    {
+                        $files[] = $file;
+                    }
+                    sort($files);
+                    foreach ($files as $file)
+                    {
+                        if (preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'llx_' && substr($file, 0, 4) != 'data') {
+                            $result=run_sql($dir.$file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG)?1:0, '', 1);
+                            if ($result <= 0) { $error++;
+                            }
+                        }
+                    }
+
+                    rewinddir($handle);
+
+                    // Run data_xxx.sql files (Must be done after llx_mytable.key.sql)
+                    $files = array();
+                    while (($file = readdir($handle))!==false)
+                    {
+                               $files[] = $file;
+                    }
+                    sort($files);
+                    foreach ($files as $file)
+                    {
+                        if (preg_match('/\.sql$/i', $file) && ! preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 4) == 'data') {
+                            $result=run_sql($dir.$file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG)?1:0, '', 1);
+                            if ($result <= 0) { $error++;
+                            }
+                        }
+                    }
+
+                    rewinddir($handle);
+
+                    // Run update_xxx.sql files
+                    $files = array();
+                    while (($file = readdir($handle))!==false)
+                    {
+                               $files[] = $file;
+                    }
+                    sort($files);
+                    foreach ($files as $file)
+                    {
+                        if (preg_match('/\.sql$/i', $file) && ! preg_match('/\.key\.sql$/i', $file) && substr($file, 0, 6) == 'update') {
+                            $result=run_sql($dir.$file, empty($conf->global->MAIN_DISPLAY_SQL_INSTALL_LOG)?1:0, '', 1);
+                            if ($result <= 0) { $error++;
+                            }
+                        }
+                    }
+
+                    closedir($handle);
+                }
+
+                if ($error == 0) {
+                    $ok = 1;
+                }
+            }
+        }
+
+        if (! $dirfound) { dol_syslog("A module ask to load sql files into ".$reldir." but this directory was not found.", LOG_WARNING);
+        }
+        return $ok;
+    }
 
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Adds boxes
-	 *
-	 * @param   string  $option Options when disabling module ('newboxdefonly'=insert only boxes definition)
-	 *
-	 * @return  int             Error count (0 if OK)
-	 */
-	function insert_boxes($option='')
-	{
+    /**
+     * Adds boxes
+     *
+     * @param string $option Options when disabling module ('newboxdefonly'=insert only boxes definition)
+     *
+     * @return int             Error count (0 if OK)
+     */
+    function insert_boxes($option='')
+    {
         // phpcs:enable
-		require_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
-
-		global $conf;
-
-		$err=0;
-
-		if (is_array($this->boxes))
-		{
-			dol_syslog(get_class($this)."::insert_boxes", LOG_DEBUG);
-
-			$pos_name = InfoBox::getListOfPagesForBoxes();
-
-			foreach ($this->boxes as $key => $value)
-			{
-				$file  = isset($this->boxes[$key]['file'])?$this->boxes[$key]['file']:'';
-				$note  = isset($this->boxes[$key]['note'])?$this->boxes[$key]['note']:'';
-				$enabledbydefaulton = isset($this->boxes[$key]['enabledbydefaulton'])?$this->boxes[$key]['enabledbydefaulton']:'Home';
-
-				if (empty($file)) $file  = isset($this->boxes[$key][1])?$this->boxes[$key][1]:'';	// For backward compatibility
-				if (empty($note)) $note  = isset($this->boxes[$key][2])?$this->boxes[$key][2]:'';	// For backward compatibility
-
-				// Search if boxes def already present
-				$sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."boxes_def";
-				$sql.= " WHERE file = '".$this->db->escape($file)."'";
-				$sql.= " AND entity = ".$conf->entity;
-				if ($note) $sql.=" AND note ='".$this->db->escape($note)."'";
-
-				$result=$this->db->query($sql);
-				if ($result)
-				{
-					$obj = $this->db->fetch_object($result);
-					if ($obj->nb == 0)
-					{
-						$this->db->begin();
-
-						if (! $err)
-						{
-							$sql = "INSERT INTO ".MAIN_DB_PREFIX."boxes_def (file, entity, note)";
-							$sql.= " VALUES ('".$this->db->escape($file)."', ";
-							$sql.= $conf->entity.", ";
-							$sql.= $note?"'".$this->db->escape($note)."'":"null";
-							$sql.= ")";
-
-							dol_syslog(get_class($this)."::insert_boxes", LOG_DEBUG);
-							$resql=$this->db->query($sql);
-							if (! $resql) $err++;
-						}
-						if (! $err && ! preg_match('/newboxdefonly/',$option))
-						{
-							$lastid=$this->db->last_insert_id(MAIN_DB_PREFIX."boxes_def","rowid");
-
-							foreach ($pos_name as $key2 => $val2)
-							{
-								//print 'key2='.$key2.'-val2='.$val2."<br>\n";
-								if ($enabledbydefaulton && $val2 != $enabledbydefaulton) continue;		// Not enabled by default onto this page.
-
-								$sql = "INSERT INTO ".MAIN_DB_PREFIX."boxes (box_id,position,box_order,fk_user,entity)";
-								$sql.= " VALUES (".$lastid.", ".$key2.", '0', 0, ".$conf->entity.")";
-
-								dol_syslog(get_class($this)."::insert_boxes onto page ".$key2."=".$val2."", LOG_DEBUG);
-								$resql=$this->db->query($sql);
-								if (! $resql) $err++;
-							}
-						}
-
-						if (! $err)
-						{
-							$this->db->commit();
-						}
-						else
-						{
-							$this->error=$this->db->lasterror();
-							$this->db->rollback();
-						}
-					}
-					// else box already registered into database
-				}
-				else
-			  {
-					$this->error=$this->db->lasterror();
-					$err++;
-				}
-			}
-		}
-
-		return $err;
-	}
+        include_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
+
+        global $conf;
+
+        $err=0;
+
+        if (is_array($this->boxes)) {
+            dol_syslog(get_class($this)."::insert_boxes", LOG_DEBUG);
+
+            $pos_name = InfoBox::getListOfPagesForBoxes();
+
+            foreach ($this->boxes as $key => $value)
+            {
+                $file  = isset($this->boxes[$key]['file'])?$this->boxes[$key]['file']:'';
+                $note  = isset($this->boxes[$key]['note'])?$this->boxes[$key]['note']:'';
+                $enabledbydefaulton = isset($this->boxes[$key]['enabledbydefaulton'])?$this->boxes[$key]['enabledbydefaulton']:'Home';
+
+                if (empty($file)) { $file  = isset($this->boxes[$key][1])?$this->boxes[$key][1]:'';    // For backward compatibility
+                }
+                if (empty($note)) { $note  = isset($this->boxes[$key][2])?$this->boxes[$key][2]:'';    // For backward compatibility
+                }
+
+                // Search if boxes def already present
+                $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."boxes_def";
+                $sql.= " WHERE file = '".$this->db->escape($file)."'";
+                $sql.= " AND entity = ".$conf->entity;
+                if ($note) { $sql.=" AND note ='".$this->db->escape($note)."'";
+                }
+
+                $result=$this->db->query($sql);
+                if ($result) {
+                    $obj = $this->db->fetch_object($result);
+                    if ($obj->nb == 0) {
+                        $this->db->begin();
+
+                        if (! $err) {
+                            $sql = "INSERT INTO ".MAIN_DB_PREFIX."boxes_def (file, entity, note)";
+                            $sql.= " VALUES ('".$this->db->escape($file)."', ";
+                            $sql.= $conf->entity.", ";
+                            $sql.= $note?"'".$this->db->escape($note)."'":"null";
+                            $sql.= ")";
+
+                            dol_syslog(get_class($this)."::insert_boxes", LOG_DEBUG);
+                            $resql=$this->db->query($sql);
+                            if (! $resql) { $err++;
+                            }
+                        }
+                        if (! $err && ! preg_match('/newboxdefonly/', $option)) {
+                            $lastid=$this->db->last_insert_id(MAIN_DB_PREFIX."boxes_def", "rowid");
+
+                            foreach ($pos_name as $key2 => $val2)
+                            {
+                                    //print 'key2='.$key2.'-val2='.$val2."<br>\n";
+                                if ($enabledbydefaulton && $val2 != $enabledbydefaulton) { continue;        // Not enabled by default onto this page.
+                                }
+
+                                $sql = "INSERT INTO ".MAIN_DB_PREFIX."boxes (box_id,position,box_order,fk_user,entity)";
+                                $sql.= " VALUES (".$lastid.", ".$key2.", '0', 0, ".$conf->entity.")";
+
+                                dol_syslog(get_class($this)."::insert_boxes onto page ".$key2."=".$val2."", LOG_DEBUG);
+                                $resql=$this->db->query($sql);
+                                if (! $resql) { $err++;
+                                }
+                            }
+                        }
+
+                        if (! $err) {
+                            $this->db->commit();
+                        }
+                        else
+                        {
+                                  $this->error=$this->db->lasterror();
+                                  $this->db->rollback();
+                        }
+                    }
+                    // else box already registered into database
+                }
+                else
+                {
+                    $this->error=$this->db->lasterror();
+                    $err++;
+                }
+            }
+        }
+
+        return $err;
+    }
 
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Removes boxes
-	 *
-	 * @return  int Error count (0 if OK)
-	 */
-	function delete_boxes()
-	{
+    /**
+     * Removes boxes
+     *
+     * @return int Error count (0 if OK)
+     */
+    function delete_boxes()
+    {
         // phpcs:enable
-		global $conf;
-
-		$err=0;
-
-		if (is_array($this->boxes))
-		{
-			foreach ($this->boxes as $key => $value)
-			{
-				//$titre = $this->boxes[$key][0];
-				$file  = $this->boxes[$key]['file'];
-				//$note  = $this->boxes[$key][2];
-
-				// TODO If the box is also included by another module and the other module is still on, we should not remove it.
-				// For the moment, we manage this with hard coded exception
-				//print "Remove box ".$file.'<br>';
-				if ($file == 'box_graph_product_distribution.php')
-				{
-					if (! empty($conf->produit->enabled) || ! empty($conf->service->enabled))
-					{
-						dol_syslog("We discard disabling of module ".$file." because another module still active require it.");
-						continue;
-					}
-				}
-
-				if (empty($file)) $file  = isset($this->boxes[$key][1])?$this->boxes[$key][1]:'';	// For backward compatibility
-
-				if ($this->db->type == 'sqlite3') {
-					// sqlite doesn't support "USING" syntax.
-					// TODO: remove this dependency.
-					$sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes ";
-					$sql .= "WHERE ".MAIN_DB_PREFIX."boxes.box_id IN (";
-					$sql .= "SELECT ".MAIN_DB_PREFIX."boxes_def.rowid ";
-					$sql .= "FROM ".MAIN_DB_PREFIX."boxes_def ";
-					$sql .= "WHERE ".MAIN_DB_PREFIX."boxes_def.file = '".$this->db->escape($file)."') ";
-					$sql .= "AND ".MAIN_DB_PREFIX."boxes.entity = ".$conf->entity;
-				} else {
-					$sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes";
-					$sql.= " USING ".MAIN_DB_PREFIX."boxes, ".MAIN_DB_PREFIX."boxes_def";
-					$sql.= " WHERE ".MAIN_DB_PREFIX."boxes.box_id = ".MAIN_DB_PREFIX."boxes_def.rowid";
-					$sql.= " AND ".MAIN_DB_PREFIX."boxes_def.file = '".$this->db->escape($file)."'";
-					$sql.= " AND ".MAIN_DB_PREFIX."boxes.entity = ".$conf->entity;
-				}
-
-				dol_syslog(get_class($this)."::delete_boxes", LOG_DEBUG);
-				$resql=$this->db->query($sql);
-				if (! $resql)
-				{
-					$this->error=$this->db->lasterror();
-					$err++;
-				}
-
-				$sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes_def";
-				$sql.= " WHERE file = '".$this->db->escape($file)."'";
-				$sql.= " AND entity = ".$conf->entity;
-
-				dol_syslog(get_class($this)."::delete_boxes", LOG_DEBUG);
-				$resql=$this->db->query($sql);
-				if (! $resql)
-				{
-					$this->error=$this->db->lasterror();
-					$err++;
-				}
-			}
-		}
-
-		return $err;
-	}
+        global $conf;
+
+        $err=0;
+
+        if (is_array($this->boxes)) {
+            foreach ($this->boxes as $key => $value)
+            {
+                //$titre = $this->boxes[$key][0];
+                $file  = $this->boxes[$key]['file'];
+                //$note  = $this->boxes[$key][2];
+
+                // TODO If the box is also included by another module and the other module is still on, we should not remove it.
+                // For the moment, we manage this with hard coded exception
+                //print "Remove box ".$file.'<br>';
+                if ($file == 'box_graph_product_distribution.php') {
+                    if (! empty($conf->produit->enabled) || ! empty($conf->service->enabled)) {
+                        dol_syslog("We discard disabling of module ".$file." because another module still active require it.");
+                        continue;
+                    }
+                }
+
+                if (empty($file)) { $file  = isset($this->boxes[$key][1])?$this->boxes[$key][1]:'';    // For backward compatibility
+                }
+
+                if ($this->db->type == 'sqlite3') {
+                    // sqlite doesn't support "USING" syntax.
+                    // TODO: remove this dependency.
+                    $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes ";
+                    $sql .= "WHERE ".MAIN_DB_PREFIX."boxes.box_id IN (";
+                    $sql .= "SELECT ".MAIN_DB_PREFIX."boxes_def.rowid ";
+                    $sql .= "FROM ".MAIN_DB_PREFIX."boxes_def ";
+                    $sql .= "WHERE ".MAIN_DB_PREFIX."boxes_def.file = '".$this->db->escape($file)."') ";
+                    $sql .= "AND ".MAIN_DB_PREFIX."boxes.entity = ".$conf->entity;
+                } else {
+                    $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes";
+                    $sql.= " USING ".MAIN_DB_PREFIX."boxes, ".MAIN_DB_PREFIX."boxes_def";
+                    $sql.= " WHERE ".MAIN_DB_PREFIX."boxes.box_id = ".MAIN_DB_PREFIX."boxes_def.rowid";
+                    $sql.= " AND ".MAIN_DB_PREFIX."boxes_def.file = '".$this->db->escape($file)."'";
+                    $sql.= " AND ".MAIN_DB_PREFIX."boxes.entity = ".$conf->entity;
+                }
+
+                dol_syslog(get_class($this)."::delete_boxes", LOG_DEBUG);
+                $resql=$this->db->query($sql);
+                if (! $resql) {
+                    $this->error=$this->db->lasterror();
+                    $err++;
+                }
+
+                $sql = "DELETE FROM ".MAIN_DB_PREFIX."boxes_def";
+                $sql.= " WHERE file = '".$this->db->escape($file)."'";
+                $sql.= " AND entity = ".$conf->entity;
+
+                dol_syslog(get_class($this)."::delete_boxes", LOG_DEBUG);
+                $resql=$this->db->query($sql);
+                if (! $resql) {
+                    $this->error=$this->db->lasterror();
+                    $err++;
+                }
+            }
+        }
+
+        return $err;
+    }
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Adds cronjobs
-	 *
-	 * @return  int             Error count (0 if OK)
-	 */
-	function insert_cronjobs()
-	{
+    /**
+     * Adds cronjobs
+     *
+     * @return int             Error count (0 if OK)
+     */
+    function insert_cronjobs()
+    {
         // phpcs:enable
-		require_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
-
-		global $conf;
-
-		$err=0;
-
-		if (is_array($this->cronjobs))
-		{
-			dol_syslog(get_class($this)."::insert_cronjobs", LOG_DEBUG);
-
-			foreach ($this->cronjobs as $key => $value)
-			{
-				$entity  = isset($this->cronjobs[$key]['entity'])?$this->cronjobs[$key]['entity']:$conf->entity;
-				$label  = isset($this->cronjobs[$key]['label'])?$this->cronjobs[$key]['label']:'';
-				$jobtype  = isset($this->cronjobs[$key]['jobtype'])?$this->cronjobs[$key]['jobtype']:'';
-				$class  = isset($this->cronjobs[$key]['class'])?$this->cronjobs[$key]['class']:'';
-				$objectname  = isset($this->cronjobs[$key]['objectname'])?$this->cronjobs[$key]['objectname']:'';
-				$method = isset($this->cronjobs[$key]['method'])?$this->cronjobs[$key]['method']:'';
-				$command  = isset($this->cronjobs[$key]['command'])?$this->cronjobs[$key]['command']:'';
-				$parameters  = isset($this->cronjobs[$key]['parameters'])?$this->cronjobs[$key]['parameters']:'';
-				$comment = isset($this->cronjobs[$key]['comment'])?$this->cronjobs[$key]['comment']:'';
-				$frequency = isset($this->cronjobs[$key]['frequency'])?$this->cronjobs[$key]['frequency']:'';
-				$unitfrequency = isset($this->cronjobs[$key]['unitfrequency'])?$this->cronjobs[$key]['unitfrequency']:'';
-				$priority = isset($this->cronjobs[$key]['priority'])?$this->cronjobs[$key]['priority']:'';
-				$datestart = isset($this->cronjobs[$key]['datestart'])?$this->cronjobs[$key]['datestart']:'';
-				$dateend = isset($this->cronjobs[$key]['dateend'])?$this->cronjobs[$key]['dateend']:'';
-				$status = isset($this->cronjobs[$key]['status'])?$this->cronjobs[$key]['status']:'';
-				$test = isset($this->cronjobs[$key]['test'])?$this->cronjobs[$key]['test']:'';					// Line must be enabled or not (so visible or not)
-
-				// Search if cron entry already present
-				$sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."cronjob";
-				$sql.= " WHERE module_name = '".$this->db->escape(empty($this->rights_class)?strtolower($this->name):$this->rights_class)."'";
-				if ($class) $sql.= " AND classesname = '".$this->db->escape($class)."'";
-				if ($objectname) $sql.= " AND objectname = '".$this->db->escape($objectname)."'";
-				if ($method) $sql.= " AND methodename = '".$this->db->escape($method)."'";
-				if ($command) $sql.= " AND command = '".$this->db->escape($command)."'";
-				$sql.= " AND entity = ".$entity;	// Must be exact entity
-
-				$now=dol_now();
-
-				$result=$this->db->query($sql);
-				if ($result)
-				{
-					$obj = $this->db->fetch_object($result);
-					if ($obj->nb == 0)
-					{
-						$this->db->begin();
-
-						if (! $err)
-						{
-							$sql = "INSERT INTO ".MAIN_DB_PREFIX."cronjob (module_name, datec, datestart, dateend, label, jobtype, classesname, objectname, methodename, command, params, note,";
-							if(is_int($frequency)){ $sql.= ' frequency,'; }
-							if(is_int($unitfrequency)){ $sql.= ' unitfrequency,'; }
-							if(is_int($priority)){ $sql.= ' priority,'; }
-							if(is_int($status)){ $sql.= ' status,'; }
-							$sql.= " entity, test)";
-							$sql.= " VALUES (";
-							$sql.= "'".$this->db->escape(empty($this->rights_class)?strtolower($this->name):$this->rights_class)."', ";
-							$sql.= "'".$this->db->idate($now)."', ";
-							$sql.= ($datestart ? "'".$this->db->idate($datestart)."'" : "NULL").", ";
-							$sql.= ($dateend   ? "'".$this->db->idate($dateend)."'"   : "NULL").", ";
-							$sql.= "'".$this->db->escape($label)."', ";
-							$sql.= "'".$this->db->escape($jobtype)."', ";
-							$sql.= ($class?"'".$this->db->escape($class)."'":"null").",";
-							$sql.= ($objectname?"'".$this->db->escape($objectname)."'":"null").",";
-							$sql.= ($method?"'".$this->db->escape($method)."'":"null").",";
-							$sql.= ($command?"'".$this->db->escape($command)."'":"null").",";
-							$sql.= ($parameters?"'".$this->db->escape($parameters)."'":"null").",";
-							$sql.= ($comment?"'".$this->db->escape($comment)."'":"null").",";
-							if(is_int($frequency)){ $sql.= "'".$this->db->escape($frequency)."', "; }
-							if(is_int($unitfrequency)){ $sql.= "'".$this->db->escape($unitfrequency)."', "; }
-							if(is_int($priority)) {$sql.= "'".$this->db->escape($priority)."', ";}
-							if(is_int($status)){ $sql.= "'".$this->db->escape($status)."', "; }
-							$sql.= $entity.",";
-							$sql.= "'".$this->db->escape($test)."'";
-							$sql.= ")";
-
-							$resql=$this->db->query($sql);
-							if (! $resql) $err++;
-						}
-
-						if (! $err)
-						{
-							$this->db->commit();
-						}
-						else
-						{
-							$this->error=$this->db->lasterror();
-							$this->db->rollback();
-						}
-					}
-					// else box already registered into database
-				}
-				else
-				{
-					$this->error=$this->db->lasterror();
-					$err++;
-				}
-			}
-		}
-
-		return $err;
-	}
+        include_once DOL_DOCUMENT_ROOT . '/core/class/infobox.class.php';
+
+        global $conf;
+
+        $err=0;
+
+        if (is_array($this->cronjobs)) {
+            dol_syslog(get_class($this)."::insert_cronjobs", LOG_DEBUG);
+
+            foreach ($this->cronjobs as $key => $value)
+            {
+                $entity  = isset($this->cronjobs[$key]['entity'])?$this->cronjobs[$key]['entity']:$conf->entity;
+                $label  = isset($this->cronjobs[$key]['label'])?$this->cronjobs[$key]['label']:'';
+                $jobtype  = isset($this->cronjobs[$key]['jobtype'])?$this->cronjobs[$key]['jobtype']:'';
+                $class  = isset($this->cronjobs[$key]['class'])?$this->cronjobs[$key]['class']:'';
+                $objectname  = isset($this->cronjobs[$key]['objectname'])?$this->cronjobs[$key]['objectname']:'';
+                $method = isset($this->cronjobs[$key]['method'])?$this->cronjobs[$key]['method']:'';
+                $command  = isset($this->cronjobs[$key]['command'])?$this->cronjobs[$key]['command']:'';
+                $parameters  = isset($this->cronjobs[$key]['parameters'])?$this->cronjobs[$key]['parameters']:'';
+                $comment = isset($this->cronjobs[$key]['comment'])?$this->cronjobs[$key]['comment']:'';
+                $frequency = isset($this->cronjobs[$key]['frequency'])?$this->cronjobs[$key]['frequency']:'';
+                $unitfrequency = isset($this->cronjobs[$key]['unitfrequency'])?$this->cronjobs[$key]['unitfrequency']:'';
+                $priority = isset($this->cronjobs[$key]['priority'])?$this->cronjobs[$key]['priority']:'';
+                $datestart = isset($this->cronjobs[$key]['datestart'])?$this->cronjobs[$key]['datestart']:'';
+                $dateend = isset($this->cronjobs[$key]['dateend'])?$this->cronjobs[$key]['dateend']:'';
+                $status = isset($this->cronjobs[$key]['status'])?$this->cronjobs[$key]['status']:'';
+                $test = isset($this->cronjobs[$key]['test'])?$this->cronjobs[$key]['test']:'';                    // Line must be enabled or not (so visible or not)
+
+                // Search if cron entry already present
+                $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."cronjob";
+                $sql.= " WHERE module_name = '".$this->db->escape(empty($this->rights_class)?strtolower($this->name):$this->rights_class)."'";
+                if ($class) { $sql.= " AND classesname = '".$this->db->escape($class)."'";
+                }
+                if ($objectname) { $sql.= " AND objectname = '".$this->db->escape($objectname)."'";
+                }
+                if ($method) { $sql.= " AND methodename = '".$this->db->escape($method)."'";
+                }
+                if ($command) { $sql.= " AND command = '".$this->db->escape($command)."'";
+                }
+                $sql.= " AND entity = ".$entity;    // Must be exact entity
+
+                $now=dol_now();
+
+                $result=$this->db->query($sql);
+                if ($result) {
+                    $obj = $this->db->fetch_object($result);
+                    if ($obj->nb == 0) {
+                        $this->db->begin();
+
+                        if (! $err) {
+                            $sql = "INSERT INTO ".MAIN_DB_PREFIX."cronjob (module_name, datec, datestart, dateend, label, jobtype, classesname, objectname, methodename, command, params, note,";
+                            if(is_int($frequency)) { $sql.= ' frequency,';
+                            }
+                            if(is_int($unitfrequency)) { $sql.= ' unitfrequency,';
+                            }
+                            if(is_int($priority)) { $sql.= ' priority,';
+                            }
+                            if(is_int($status)) { $sql.= ' status,';
+                            }
+                            $sql.= " entity, test)";
+                            $sql.= " VALUES (";
+                            $sql.= "'".$this->db->escape(empty($this->rights_class)?strtolower($this->name):$this->rights_class)."', ";
+                            $sql.= "'".$this->db->idate($now)."', ";
+                            $sql.= ($datestart ? "'".$this->db->idate($datestart)."'" : "NULL").", ";
+                            $sql.= ($dateend   ? "'".$this->db->idate($dateend)."'"   : "NULL").", ";
+                            $sql.= "'".$this->db->escape($label)."', ";
+                            $sql.= "'".$this->db->escape($jobtype)."', ";
+                            $sql.= ($class?"'".$this->db->escape($class)."'":"null").",";
+                            $sql.= ($objectname?"'".$this->db->escape($objectname)."'":"null").",";
+                            $sql.= ($method?"'".$this->db->escape($method)."'":"null").",";
+                            $sql.= ($command?"'".$this->db->escape($command)."'":"null").",";
+                            $sql.= ($parameters?"'".$this->db->escape($parameters)."'":"null").",";
+                            $sql.= ($comment?"'".$this->db->escape($comment)."'":"null").",";
+                            if(is_int($frequency)) { $sql.= "'".$this->db->escape($frequency)."', ";
+                            }
+                            if(is_int($unitfrequency)) { $sql.= "'".$this->db->escape($unitfrequency)."', ";
+                            }
+                            if(is_int($priority)) {$sql.= "'".$this->db->escape($priority)."', ";
+                            }
+                            if(is_int($status)) { $sql.= "'".$this->db->escape($status)."', ";
+                            }
+                            $sql.= $entity.",";
+                            $sql.= "'".$this->db->escape($test)."'";
+                            $sql.= ")";
+
+                            $resql=$this->db->query($sql);
+                            if (! $resql) { $err++;
+                            }
+                        }
+
+                        if (! $err) {
+                            $this->db->commit();
+                        }
+                        else
+                        {
+                            $this->error=$this->db->lasterror();
+                            $this->db->rollback();
+                        }
+                    }
+                    // else box already registered into database
+                }
+                else
+                {
+                    $this->error=$this->db->lasterror();
+                    $err++;
+                }
+            }
+        }
+
+        return $err;
+    }
 
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Removes boxes
-	 *
-	 * @return  int Error count (0 if OK)
-	 */
-	function delete_cronjobs()
-	{
+    /**
+     * Removes boxes
+     *
+     * @return int Error count (0 if OK)
+     */
+    function delete_cronjobs()
+    {
         // phpcs:enable
-		global $conf;
-
-		$err=0;
-
-		if (is_array($this->cronjobs))
-		{
-			$sql = "DELETE FROM ".MAIN_DB_PREFIX."cronjob";
-			$sql.= " WHERE module_name = '".$this->db->escape(empty($this->rights_class)?strtolower($this->name):$this->rights_class)."'";
-			$sql.= " AND entity = ".$conf->entity;
-			$sql.= " AND test = '1'";		// We delete on lines that are not set with a complete test that is '$conf->module->enabled' so when module is disabled, the cron is also removed.
-											// For crons declared with a '$conf->module->enabled', there is no need to delete the line, so we don't loose setup if we reenable module.
-
-			dol_syslog(get_class($this)."::delete_cronjobs", LOG_DEBUG);
-			$resql=$this->db->query($sql);
-			if (! $resql)
-			{
-				$this->error=$this->db->lasterror();
-				$err++;
-			}
-		}
-
-		return $err;
-	}
+        global $conf;
+
+        $err=0;
+
+        if (is_array($this->cronjobs)) {
+            $sql = "DELETE FROM ".MAIN_DB_PREFIX."cronjob";
+            $sql.= " WHERE module_name = '".$this->db->escape(empty($this->rights_class)?strtolower($this->name):$this->rights_class)."'";
+            $sql.= " AND entity = ".$conf->entity;
+            $sql.= " AND test = '1'";        // We delete on lines that are not set with a complete test that is '$conf->module->enabled' so when module is disabled, the cron is also removed.
+              // For crons declared with a '$conf->module->enabled', there is no need to delete the line, so we don't loose setup if we reenable module.
+
+            dol_syslog(get_class($this)."::delete_cronjobs", LOG_DEBUG);
+            $resql=$this->db->query($sql);
+            if (! $resql) {
+                $this->error=$this->db->lasterror();
+                $err++;
+            }
+        }
+
+        return $err;
+    }
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Removes tabs
-	 *
-	 * @return  int Error count (0 if OK)
-	 */
-	function delete_tabs()
-	{
+    /**
+     * Removes tabs
+     *
+     * @return int Error count (0 if OK)
+     */
+    function delete_tabs()
+    {
         // phpcs:enable
-		global $conf;
+        global $conf;
 
-		$err=0;
+        $err=0;
 
-		$sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
-		$sql.= " WHERE ".$this->db->decrypt('name')." like '".$this->db->escape($this->const_name)."_TABS_%'";
-		$sql.= " AND entity = ".$conf->entity;
+        $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
+        $sql.= " WHERE ".$this->db->decrypt('name')." like '".$this->db->escape($this->const_name)."_TABS_%'";
+        $sql.= " AND entity = ".$conf->entity;
 
-		dol_syslog(get_class($this)."::delete_tabs", LOG_DEBUG);
-		if (! $this->db->query($sql))
-		{
-			$this->error=$this->db->lasterror();
-			$err++;
-		}
+        dol_syslog(get_class($this)."::delete_tabs", LOG_DEBUG);
+        if (! $this->db->query($sql)) {
+            $this->error=$this->db->lasterror();
+            $err++;
+        }
 
-		return $err;
-	}
+        return $err;
+    }
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Adds tabs
-	 *
-	 * @return int  Error count (0 if ok)
-	 */
-	function insert_tabs()
-	{
+    /**
+     * Adds tabs
+     *
+     * @return int  Error count (0 if ok)
+     */
+    function insert_tabs()
+    {
         // phpcs:enable
-		global $conf;
-
-		$err=0;
-
-		if (! empty($this->tabs))
-		{
-			dol_syslog(get_class($this)."::insert_tabs", LOG_DEBUG);
-
-			$i=0;
-			foreach ($this->tabs as $key => $value)
-			{
-				if (is_array($value) && count($value) == 0) continue;	// Discard empty arrays
-
-				$entity=$conf->entity;
-				$newvalue = $value;
-
-				if (is_array($value))
-				{
-					$newvalue = $value['data'];
-					if (isset($value['entity'])) $entity = $value['entity'];
-				}
-
-				if ($newvalue)
-				{
-					$sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
-					$sql.= "name";
-					$sql.= ", type";
-					$sql.= ", value";
-					$sql.= ", note";
-					$sql.= ", visible";
-					$sql.= ", entity";
-					$sql.= ")";
-					$sql.= " VALUES (";
-					$sql.= $this->db->encrypt($this->const_name."_TABS_".$i,1);
-					$sql.= ", 'chaine'";
-					$sql.= ", ".$this->db->encrypt($newvalue,1);
-					$sql.= ", null";
-					$sql.= ", '0'";
-					$sql.= ", ".$entity;
-					$sql.= ")";
-
-					$resql = $this->db->query($sql);
-					if (! $resql)
-					{
-						dol_syslog($this->db->lasterror(), LOG_ERR);
-						if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS')
-						{
-							$this->error = $this->db->lasterror();
-							$this->errors[] = $this->db->lasterror();
-							$err++;
-							break;
-						}
-					}
-				}
-				$i++;
-			}
-		}
-		return $err;
-	}
+        global $conf;
+
+        $err=0;
+
+        if (! empty($this->tabs)) {
+            dol_syslog(get_class($this)."::insert_tabs", LOG_DEBUG);
+
+            $i=0;
+            foreach ($this->tabs as $key => $value)
+            {
+                if (is_array($value) && count($value) == 0) { continue;    // Discard empty arrays
+                }
+
+                $entity=$conf->entity;
+                $newvalue = $value;
+
+                if (is_array($value)) {
+                    $newvalue = $value['data'];
+                    if (isset($value['entity'])) { $entity = $value['entity'];
+                    }
+                }
+
+                if ($newvalue) {
+                    $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
+                    $sql.= "name";
+                    $sql.= ", type";
+                    $sql.= ", value";
+                    $sql.= ", note";
+                    $sql.= ", visible";
+                    $sql.= ", entity";
+                    $sql.= ")";
+                    $sql.= " VALUES (";
+                    $sql.= $this->db->encrypt($this->const_name."_TABS_".$i, 1);
+                    $sql.= ", 'chaine'";
+                    $sql.= ", ".$this->db->encrypt($newvalue, 1);
+                    $sql.= ", null";
+                    $sql.= ", '0'";
+                    $sql.= ", ".$entity;
+                    $sql.= ")";
+
+                    $resql = $this->db->query($sql);
+                    if (! $resql) {
+                         dol_syslog($this->db->lasterror(), LOG_ERR);
+                        if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
+                            $this->error = $this->db->lasterror();
+                            $this->errors[] = $this->db->lasterror();
+                            $err++;
+                            break;
+                        }
+                    }
+                }
+                $i++;
+            }
+        }
+        return $err;
+    }
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Adds constants
-	 *
-	 * @return  int Error count (0 if OK)
-	 */
-	function insert_const()
-	{
+    /**
+     * Adds constants
+     *
+     * @return int Error count (0 if OK)
+     */
+    function insert_const()
+    {
         // phpcs:enable
-		global $conf;
-
-		$err=0;
-
-		if (empty($this->const)) return 0;
-
-		dol_syslog(get_class($this)."::insert_const", LOG_DEBUG);
-
-		foreach ($this->const as $key => $value)
-		{
-			$name      = $this->const[$key][0];
-			$type      = $this->const[$key][1];
-			$val       = $this->const[$key][2];
-			$note      = isset($this->const[$key][3])?$this->const[$key][3]:'';
-			$visible   = isset($this->const[$key][4])?$this->const[$key][4]:0;
-			$entity    = (! empty($this->const[$key][5]) && $this->const[$key][5]!='current')?0:$conf->entity;
-
-			// Clean
-			if (empty($visible)) $visible='0';
-			if (empty($val) && $val != '0') $val='';
-
-			$sql = "SELECT count(*)";
-			$sql.= " FROM ".MAIN_DB_PREFIX."const";
-			$sql.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
-			$sql.= " AND entity = ".$entity;
-
-			$result=$this->db->query($sql);
-			if ($result)
-			{
-				$row = $this->db->fetch_row($result);
-
-				if ($row[0] == 0)   // If not found
-				{
-					$sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name,type,value,note,visible,entity)";
-					$sql.= " VALUES (";
-					$sql.= $this->db->encrypt($name,1);
-					$sql.= ",'".$type."'";
-					$sql.= ",".(($val != '')?$this->db->encrypt($val,1):"''");
-					$sql.= ",".($note?"'".$this->db->escape($note)."'":"null");
-					$sql.= ",'".$visible."'";
-					$sql.= ",".$entity;
-					$sql.= ")";
-
-					if (! $this->db->query($sql) )
-					{
-						$err++;
-					}
-				}
-				else
-				{
-					dol_syslog(get_class($this)."::insert_const constant '".$name."' already exists", LOG_WARNING);
-				}
-			}
-			else
-			{
-				$err++;
-			}
-		}
-
-		return $err;
-	}
+        global $conf;
+
+        $err=0;
+
+        if (empty($this->const)) { return 0;
+        }
+
+        dol_syslog(get_class($this)."::insert_const", LOG_DEBUG);
+
+        foreach ($this->const as $key => $value)
+        {
+            $name      = $this->const[$key][0];
+            $type      = $this->const[$key][1];
+            $val       = $this->const[$key][2];
+            $note      = isset($this->const[$key][3])?$this->const[$key][3]:'';
+            $visible   = isset($this->const[$key][4])?$this->const[$key][4]:0;
+            $entity    = (! empty($this->const[$key][5]) && $this->const[$key][5]!='current')?0:$conf->entity;
+
+            // Clean
+            if (empty($visible)) { $visible='0';
+            }
+            if (empty($val) && $val != '0') { $val='';
+            }
+
+            $sql = "SELECT count(*)";
+            $sql.= " FROM ".MAIN_DB_PREFIX."const";
+            $sql.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($name)."'";
+            $sql.= " AND entity = ".$entity;
+
+            $result=$this->db->query($sql);
+            if ($result) {
+                $row = $this->db->fetch_row($result);
+
+                if ($row[0] == 0)   // If not found
+                {
+                    $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name,type,value,note,visible,entity)";
+                    $sql.= " VALUES (";
+                    $sql.= $this->db->encrypt($name, 1);
+                    $sql.= ",'".$type."'";
+                    $sql.= ",".(($val != '')?$this->db->encrypt($val, 1):"''");
+                    $sql.= ",".($note?"'".$this->db->escape($note)."'":"null");
+                    $sql.= ",'".$visible."'";
+                    $sql.= ",".$entity;
+                    $sql.= ")";
+
+                    if (! $this->db->query($sql) ) {
+                        $err++;
+                    }
+                }
+                else
+                {
+                    dol_syslog(get_class($this)."::insert_const constant '".$name."' already exists", LOG_WARNING);
+                }
+            }
+            else
+            {
+                $err++;
+            }
+        }
+
+        return $err;
+    }
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Removes constants tagged 'deleteonunactive'
-	 *
-	 * @return  int <0 if KO, 0 if OK
-	 */
-	function delete_const()
-	{
+    /**
+     * Removes constants tagged 'deleteonunactive'
+     *
+     * @return int <0 if KO, 0 if OK
+     */
+    function delete_const()
+    {
         // phpcs:enable
-		global $conf;
-
-		$err=0;
-
-		if (empty($this->const)) return 0;
-
-		foreach ($this->const as $key => $value)
-		{
-			$name      = $this->const[$key][0];
-			$deleteonunactive = (! empty($this->const[$key][6]))?1:0;
-
-			if ($deleteonunactive)
-			{
-				$sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
-				$sql.= " WHERE ".$this->db->decrypt('name')." = '".$name."'";
-				$sql.= " AND entity in (0, ".$conf->entity.")";
-				dol_syslog(get_class($this)."::delete_const", LOG_DEBUG);
-				if (! $this->db->query($sql))
-				{
-					$this->error=$this->db->lasterror();
-					$err++;
-				}
-			}
-		}
-
-		return $err;
-	}
+        global $conf;
+
+        $err=0;
+
+        if (empty($this->const)) { return 0;
+        }
+
+        foreach ($this->const as $key => $value)
+        {
+            $name      = $this->const[$key][0];
+            $deleteonunactive = (! empty($this->const[$key][6]))?1:0;
+
+            if ($deleteonunactive) {
+                $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
+                $sql.= " WHERE ".$this->db->decrypt('name')." = '".$name."'";
+                $sql.= " AND entity in (0, ".$conf->entity.")";
+                dol_syslog(get_class($this)."::delete_const", LOG_DEBUG);
+                if (! $this->db->query($sql)) {
+                    $this->error=$this->db->lasterror();
+                    $err++;
+                }
+            }
+        }
+
+        return $err;
+    }
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Adds access rights
-	 *
-	 * @param   int $reinitadminperms   If 1, we also grant them to all admin users
-	 * @param   int $force_entity       Force current entity
-	 * @param   int	$notrigger			1=Does not execute triggers, 0= execute triggers
-	 * @return  int                     Error count (0 if OK)
-	 */
-	function insert_permissions($reinitadminperms=0, $force_entity=null, $notrigger=0)
-	{
+    /**
+     * Adds access rights
+     *
+     * @param  int $reinitadminperms If 1, we also grant them to all admin users
+     * @param  int $force_entity     Force current entity
+     * @param  int $notrigger        1=Does not execute triggers, 0= execute triggers
+     * @return int                     Error count (0 if OK)
+     */
+    function insert_permissions($reinitadminperms=0, $force_entity=null, $notrigger=0)
+    {
         // phpcs:enable
-		global $conf,$user;
-
-		$err=0;
-		$entity=(! empty($force_entity) ? $force_entity : $conf->entity);
-
-		dol_syslog(get_class($this)."::insert_permissions", LOG_DEBUG);
-
-		// Test if module is activated
-		$sql_del = "SELECT ".$this->db->decrypt('value')." as value";
-		$sql_del.= " FROM ".MAIN_DB_PREFIX."const";
-		$sql_del.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
-		$sql_del.= " AND entity IN (0,".$entity.")";
-
-		$resql=$this->db->query($sql_del);
-
-		if ($resql)
-		{
-			$obj=$this->db->fetch_object($resql);
-			if ($obj !== null && ! empty($obj->value) && ! empty($this->rights))
-			{
-				// If the module is active
-				foreach ($this->rights as $key => $value)
-				{
-					$r_id       = $this->rights[$key][0];
-					$r_desc     = $this->rights[$key][1];
-					$r_type     = isset($this->rights[$key][2])?$this->rights[$key][2]:'';
-					$r_def      = $this->rights[$key][3];
-					$r_perms    = $this->rights[$key][4];
-					$r_subperms = isset($this->rights[$key][5])?$this->rights[$key][5]:'';
-					$r_modul    = empty($this->rights_class)?strtolower($this->name):$this->rights_class;
-
-					if (empty($r_type)) $r_type='w';
-
-					// Search if perm already present
-					$sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."rights_def";
-					$sql.= " WHERE id = ".$r_id." AND entity = ".$entity;
-
-					$resqlselect=$this->db->query($sql);
-					if ($resqlselect)
-					{
-						$objcount = $this->db->fetch_object($resqlselect);
-						if ($objcount && $objcount->nb == 0)
-						{
-							if (dol_strlen($r_perms) )
-							{
-								if (dol_strlen($r_subperms) )
-								{
-									$sql = "INSERT INTO ".MAIN_DB_PREFIX."rights_def";
-									$sql.= " (id, entity, libelle, module, type, bydefault, perms, subperms)";
-									$sql.= " VALUES ";
-									$sql.= "(".$r_id.",".$entity.",'".$this->db->escape($r_desc)."','".$r_modul."','".$r_type."',".$r_def.",'".$r_perms."','".$r_subperms."')";
-								}
-								else
-								{
-									$sql = "INSERT INTO ".MAIN_DB_PREFIX."rights_def";
-									$sql.= " (id, entity, libelle, module, type, bydefault, perms)";
-									$sql.= " VALUES ";
-									$sql.= "(".$r_id.",".$entity.",'".$this->db->escape($r_desc)."','".$r_modul."','".$r_type."',".$r_def.",'".$r_perms."')";
-								}
-							}
-							else
-							{
-								$sql = "INSERT INTO ".MAIN_DB_PREFIX."rights_def ";
-								$sql .= " (id, entity, libelle, module, type, bydefault)";
-								$sql .= " VALUES ";
-								$sql .= "(".$r_id.",".$entity.",'".$this->db->escape($r_desc)."','".$r_modul."','".$r_type."',".$r_def.")";
-							}
-
-							$resqlinsert=$this->db->query($sql,1);
-
-							if (! $resqlinsert)
-							{
-								if ($this->db->errno() != "DB_ERROR_RECORD_ALREADY_EXISTS")
-								{
-									$this->error=$this->db->lasterror();
-									$err++;
-									break;
-								}
-								else dol_syslog(get_class($this)."::insert_permissions record already exists", LOG_INFO);
-							}
-
-							$this->db->free($resqlinsert);
-						}
-
-						$this->db->free($resqlselect);
-					}
-
-					// If we want to init permissions on admin users
-					if ($reinitadminperms)
-					{
-						if (! class_exists('User')) {
-							require_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
-						}
-						$sql="SELECT rowid FROM ".MAIN_DB_PREFIX."user WHERE admin = 1";
-						dol_syslog(get_class($this)."::insert_permissions Search all admin users", LOG_DEBUG);
-						$resqlseladmin=$this->db->query($sql,1);
-						if ($resqlseladmin)
-						{
-							$num=$this->db->num_rows($resqlseladmin);
-							$i=0;
-							while ($i < $num)
-							{
-								$obj2=$this->db->fetch_object($resqlseladmin);
-								dol_syslog(get_class($this)."::insert_permissions Add permission to user id=".$obj2->rowid);
-
-								$tmpuser=new User($this->db);
-								$result = $tmpuser->fetch($obj2->rowid);
-								if ($result > 0) {
-									$tmpuser->addrights($r_id, '', '', 0, 1);
-								}
-								else
-								{
-									dol_syslog(get_class($this)."::insert_permissions Failed to add the permission to user because fetch return an error", LOG_ERR);
-								}
-								$i++;
-							}
-						}
-						else
-						{
-							dol_print_error($this->db);
-						}
-					}
-				}
-
-				if ($reinitadminperms && ! empty($user->admin))  // Reload permission for current user if defined
-				{
-					// We reload permissions
-					$user->clearrights();
-					$user->getrights();
-				}
-			}
-			$this->db->free($resql);
-		}
-		else
-		{
-			$this->error=$this->db->lasterror();
-			$err++;
-		}
-
-		return $err;
-	}
+        global $conf,$user;
+
+        $err=0;
+        $entity=(! empty($force_entity) ? $force_entity : $conf->entity);
+
+        dol_syslog(get_class($this)."::insert_permissions", LOG_DEBUG);
+
+        // Test if module is activated
+        $sql_del = "SELECT ".$this->db->decrypt('value')." as value";
+        $sql_del.= " FROM ".MAIN_DB_PREFIX."const";
+        $sql_del.= " WHERE ".$this->db->decrypt('name')." = '".$this->db->escape($this->const_name)."'";
+        $sql_del.= " AND entity IN (0,".$entity.")";
+
+        $resql=$this->db->query($sql_del);
+
+        if ($resql) {
+            $obj=$this->db->fetch_object($resql);
+            if ($obj !== null && ! empty($obj->value) && ! empty($this->rights)) {
+                // If the module is active
+                foreach ($this->rights as $key => $value)
+                {
+                    $r_id       = $this->rights[$key][0];
+                    $r_desc     = $this->rights[$key][1];
+                    $r_type     = isset($this->rights[$key][2])?$this->rights[$key][2]:'';
+                    $r_def      = $this->rights[$key][3];
+                    $r_perms    = $this->rights[$key][4];
+                    $r_subperms = isset($this->rights[$key][5])?$this->rights[$key][5]:'';
+                    $r_modul    = empty($this->rights_class)?strtolower($this->name):$this->rights_class;
+
+                    if (empty($r_type)) { $r_type='w';
+                    }
+
+                    // Search if perm already present
+                    $sql = "SELECT count(*) as nb FROM ".MAIN_DB_PREFIX."rights_def";
+                    $sql.= " WHERE id = ".$r_id." AND entity = ".$entity;
+
+                    $resqlselect=$this->db->query($sql);
+                    if ($resqlselect) {
+                        $objcount = $this->db->fetch_object($resqlselect);
+                        if ($objcount && $objcount->nb == 0) {
+                            if (dol_strlen($r_perms) ) {
+                                if (dol_strlen($r_subperms) ) {
+                                    $sql = "INSERT INTO ".MAIN_DB_PREFIX."rights_def";
+                                    $sql.= " (id, entity, libelle, module, type, bydefault, perms, subperms)";
+                                    $sql.= " VALUES ";
+                                    $sql.= "(".$r_id.",".$entity.",'".$this->db->escape($r_desc)."','".$r_modul."','".$r_type."',".$r_def.",'".$r_perms."','".$r_subperms."')";
+                                }
+                                else
+                                   {
+                                    $sql = "INSERT INTO ".MAIN_DB_PREFIX."rights_def";
+                                    $sql.= " (id, entity, libelle, module, type, bydefault, perms)";
+                                    $sql.= " VALUES ";
+                                    $sql.= "(".$r_id.",".$entity.",'".$this->db->escape($r_desc)."','".$r_modul."','".$r_type."',".$r_def.",'".$r_perms."')";
+                                }
+                            }
+                            else
+                            {
+                                 $sql = "INSERT INTO ".MAIN_DB_PREFIX."rights_def ";
+                                 $sql .= " (id, entity, libelle, module, type, bydefault)";
+                                 $sql .= " VALUES ";
+                                 $sql .= "(".$r_id.",".$entity.",'".$this->db->escape($r_desc)."','".$r_modul."','".$r_type."',".$r_def.")";
+                            }
+
+                            $resqlinsert=$this->db->query($sql, 1);
+
+                            if (! $resqlinsert) {
+                                if ($this->db->errno() != "DB_ERROR_RECORD_ALREADY_EXISTS") {
+                                    $this->error=$this->db->lasterror();
+                                    $err++;
+                                    break;
+                                }
+                                else { dol_syslog(get_class($this)."::insert_permissions record already exists", LOG_INFO);
+                                }
+                            }
+
+                            $this->db->free($resqlinsert);
+                        }
+
+                        $this->db->free($resqlselect);
+                    }
+
+                    // If we want to init permissions on admin users
+                    if ($reinitadminperms) {
+                        if (! class_exists('User')) {
+                            include_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
+                        }
+                        $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."user WHERE admin = 1";
+                        dol_syslog(get_class($this)."::insert_permissions Search all admin users", LOG_DEBUG);
+                        $resqlseladmin=$this->db->query($sql, 1);
+                        if ($resqlseladmin) {
+                            $num=$this->db->num_rows($resqlseladmin);
+                            $i=0;
+                            while ($i < $num)
+                            {
+                                  $obj2=$this->db->fetch_object($resqlseladmin);
+                                  dol_syslog(get_class($this)."::insert_permissions Add permission to user id=".$obj2->rowid);
+
+                                  $tmpuser=new User($this->db);
+                                  $result = $tmpuser->fetch($obj2->rowid);
+                                if ($result > 0) {
+                                    $tmpuser->addrights($r_id, '', '', 0, 1);
+                                }
+                                else
+                                 {
+                                    dol_syslog(get_class($this)."::insert_permissions Failed to add the permission to user because fetch return an error", LOG_ERR);
+                                }
+                                 $i++;
+                            }
+                        }
+                        else
+                        {
+                            dol_print_error($this->db);
+                        }
+                    }
+                }
+
+                if ($reinitadminperms && ! empty($user->admin))  // Reload permission for current user if defined
+                {
+                    // We reload permissions
+                    $user->clearrights();
+                    $user->getrights();
+                }
+            }
+            $this->db->free($resql);
+        }
+        else
+        {
+            $this->error=$this->db->lasterror();
+            $err++;
+        }
+
+        return $err;
+    }
 
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Removes access rights
-	 *
-	 * @return  int                     Error count (0 if OK)
-	 */
-	function delete_permissions()
-	{
+    /**
+     * Removes access rights
+     *
+     * @return int                     Error count (0 if OK)
+     */
+    function delete_permissions()
+    {
         // phpcs:enable
-		global $conf;
+        global $conf;
 
-		$err=0;
+        $err=0;
 
-		$sql = "DELETE FROM ".MAIN_DB_PREFIX."rights_def";
-		$sql.= " WHERE module = '".$this->db->escape(empty($this->rights_class)?strtolower($this->name):$this->rights_class)."'";
-		$sql.= " AND entity = ".$conf->entity;
-		dol_syslog(get_class($this)."::delete_permissions", LOG_DEBUG);
-		if (! $this->db->query($sql))
-		{
-			$this->error=$this->db->lasterror();
-			$err++;
-		}
+        $sql = "DELETE FROM ".MAIN_DB_PREFIX."rights_def";
+        $sql.= " WHERE module = '".$this->db->escape(empty($this->rights_class)?strtolower($this->name):$this->rights_class)."'";
+        $sql.= " AND entity = ".$conf->entity;
+        dol_syslog(get_class($this)."::delete_permissions", LOG_DEBUG);
+        if (! $this->db->query($sql)) {
+            $this->error=$this->db->lasterror();
+            $err++;
+        }
 
-		return $err;
-	}
+        return $err;
+    }
 
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Adds menu entries
-	 *
-	 * @return  int     Error count (0 if OK)
-	 */
-	function insert_menus()
-	{
+    /**
+     * Adds menu entries
+     *
+     * @return int     Error count (0 if OK)
+     */
+    function insert_menus()
+    {
         // phpcs:enable
-		global $user;
-
-		if (! is_array($this->menu) || empty($this->menu)) return 0;
-
-		require_once DOL_DOCUMENT_ROOT . '/core/class/menubase.class.php';
-
-		dol_syslog(get_class($this)."::insert_menus", LOG_DEBUG);
-
-		$err=0;
-
-		$this->db->begin();
-
-		foreach ($this->menu as $key => $value)
-		{
-			$menu = new Menubase($this->db);
-			$menu->menu_handler='all';
-
-			//$menu->module=strtolower($this->name);	TODO When right_class will be same than module name
-			$menu->module=empty($this->rights_class)?strtolower($this->name):$this->rights_class;
-
-			if (! $this->menu[$key]['fk_menu'])
-			{
-				$menu->fk_menu=0;
-			}
-			else
-			{
-				$foundparent=0;
-				$fk_parent=$this->menu[$key]['fk_menu'];
-				if (preg_match('/^r=/',$fk_parent))	// old deprecated method
-				{
-					$fk_parent=str_replace('r=','',$fk_parent);
-					if (isset($this->menu[$fk_parent]['rowid']))
-					{
-						$menu->fk_menu=$this->menu[$fk_parent]['rowid'];
-						$foundparent=1;
-					}
-				}
-				elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+),fk_leftmenu=([a-zA-Z0-9_]+)$/',$fk_parent,$reg))
-				{
-					$menu->fk_menu=-1;
-					$menu->fk_mainmenu=$reg[1];
-					$menu->fk_leftmenu=$reg[2];
-					$foundparent=1;
-				}
-				elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+)$/',$fk_parent,$reg))
-				{
-					$menu->fk_menu=-1;
-					$menu->fk_mainmenu=$reg[1];
-					$menu->fk_leftmenu='';
-					$foundparent=1;
-				}
-				if (! $foundparent)
-				{
-					$this->error="ErrorBadDefinitionOfMenuArrayInModuleDescriptor";
-					dol_syslog(get_class($this)."::insert_menus ".$this->error." ".$this->menu[$key]['fk_menu'], LOG_ERR);
-					$err++;
-				}
-			}
-			$menu->type=$this->menu[$key]['type'];
-			$menu->mainmenu=isset($this->menu[$key]['mainmenu'])?$this->menu[$key]['mainmenu']:(isset($menu->fk_mainmenu)?$menu->fk_mainmenu:'');
-			$menu->leftmenu=isset($this->menu[$key]['leftmenu'])?$this->menu[$key]['leftmenu']:'';
-			$menu->titre=$this->menu[$key]['titre'];
-			$menu->url=$this->menu[$key]['url'];
-			$menu->langs=$this->menu[$key]['langs'];
-			$menu->position=$this->menu[$key]['position'];
-			$menu->perms=$this->menu[$key]['perms'];
-			$menu->target=isset($this->menu[$key]['target'])?$this->menu[$key]['target']:'';
-			$menu->user=$this->menu[$key]['user'];
-			$menu->enabled=isset($this->menu[$key]['enabled'])?$this->menu[$key]['enabled']:0;
-			$menu->position=$this->menu[$key]['position'];
-
-			if (! $err)
-			{
-				$result=$menu->create($user);	// Save menu entry into table llx_menu
-				if ($result > 0)
-				{
-					$this->menu[$key]['rowid']=$result;
-				}
-				else
-				{
-					$this->error=$menu->error;
-					dol_syslog(get_class($this).'::insert_menus result='.$result." ".$this->error, LOG_ERR);
-					$err++;
-					break;
-				}
-			}
-		}
-
-		if (! $err)
-		{
-			$this->db->commit();
-		}
-		else
-		{
-			dol_syslog(get_class($this)."::insert_menus ".$this->error, LOG_ERR);
-			$this->db->rollback();
-		}
-
-		return $err;
-	}
+        global $user;
+
+        if (! is_array($this->menu) || empty($this->menu)) { return 0;
+        }
+
+        include_once DOL_DOCUMENT_ROOT . '/core/class/menubase.class.php';
+
+        dol_syslog(get_class($this)."::insert_menus", LOG_DEBUG);
+
+        $err=0;
+
+        $this->db->begin();
+
+        foreach ($this->menu as $key => $value)
+        {
+            $menu = new Menubase($this->db);
+            $menu->menu_handler='all';
+
+            //$menu->module=strtolower($this->name);    TODO When right_class will be same than module name
+            $menu->module=empty($this->rights_class)?strtolower($this->name):$this->rights_class;
+
+            if (! $this->menu[$key]['fk_menu']) {
+                $menu->fk_menu=0;
+            }
+            else
+            {
+                $foundparent=0;
+                $fk_parent=$this->menu[$key]['fk_menu'];
+                if (preg_match('/^r=/', $fk_parent))    // old deprecated method
+                {
+                    $fk_parent=str_replace('r=', '', $fk_parent);
+                    if (isset($this->menu[$fk_parent]['rowid'])) {
+                        $menu->fk_menu=$this->menu[$fk_parent]['rowid'];
+                        $foundparent=1;
+                    }
+                }
+                elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+),fk_leftmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
+                    $menu->fk_menu=-1;
+                    $menu->fk_mainmenu=$reg[1];
+                    $menu->fk_leftmenu=$reg[2];
+                    $foundparent=1;
+                }
+                elseif (preg_match('/^fk_mainmenu=([a-zA-Z0-9_]+)$/', $fk_parent, $reg)) {
+                    $menu->fk_menu=-1;
+                    $menu->fk_mainmenu=$reg[1];
+                    $menu->fk_leftmenu='';
+                    $foundparent=1;
+                }
+                if (! $foundparent) {
+                    $this->error="ErrorBadDefinitionOfMenuArrayInModuleDescriptor";
+                    dol_syslog(get_class($this)."::insert_menus ".$this->error." ".$this->menu[$key]['fk_menu'], LOG_ERR);
+                    $err++;
+                }
+            }
+            $menu->type=$this->menu[$key]['type'];
+            $menu->mainmenu=isset($this->menu[$key]['mainmenu'])?$this->menu[$key]['mainmenu']:(isset($menu->fk_mainmenu)?$menu->fk_mainmenu:'');
+            $menu->leftmenu=isset($this->menu[$key]['leftmenu'])?$this->menu[$key]['leftmenu']:'';
+            $menu->titre=$this->menu[$key]['titre'];
+            $menu->url=$this->menu[$key]['url'];
+            $menu->langs=$this->menu[$key]['langs'];
+            $menu->position=$this->menu[$key]['position'];
+            $menu->perms=$this->menu[$key]['perms'];
+            $menu->target=isset($this->menu[$key]['target'])?$this->menu[$key]['target']:'';
+            $menu->user=$this->menu[$key]['user'];
+            $menu->enabled=isset($this->menu[$key]['enabled'])?$this->menu[$key]['enabled']:0;
+            $menu->position=$this->menu[$key]['position'];
+
+            if (! $err) {
+                $result=$menu->create($user);    // Save menu entry into table llx_menu
+                if ($result > 0) {
+                    $this->menu[$key]['rowid']=$result;
+                }
+                else
+                {
+                    $this->error=$menu->error;
+                    dol_syslog(get_class($this).'::insert_menus result='.$result." ".$this->error, LOG_ERR);
+                    $err++;
+                    break;
+                }
+            }
+        }
+
+        if (! $err) {
+            $this->db->commit();
+        }
+        else
+        {
+            dol_syslog(get_class($this)."::insert_menus ".$this->error, LOG_ERR);
+            $this->db->rollback();
+        }
+
+        return $err;
+    }
 
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Removes menu entries
-	 *
-	 * @return  int Error count (0 if OK)
-	 */
-	function delete_menus()
-	{
+    /**
+     * Removes menu entries
+     *
+     * @return int Error count (0 if OK)
+     */
+    function delete_menus()
+    {
         // phpcs:enable
-		global $conf;
+        global $conf;
 
-		$err=0;
+        $err=0;
 
-		//$module=strtolower($this->name);		TODO When right_class will be same than module name
-		$module=empty($this->rights_class)?strtolower($this->name):$this->rights_class;
+        //$module=strtolower($this->name);        TODO When right_class will be same than module name
+        $module=empty($this->rights_class)?strtolower($this->name):$this->rights_class;
 
-		$sql = "DELETE FROM ".MAIN_DB_PREFIX."menu";
-		$sql.= " WHERE module = '".$this->db->escape($module)."'";
-		$sql.= " AND entity = ".$conf->entity;
+        $sql = "DELETE FROM ".MAIN_DB_PREFIX."menu";
+        $sql.= " WHERE module = '".$this->db->escape($module)."'";
+        $sql.= " AND entity = ".$conf->entity;
 
-		dol_syslog(get_class($this)."::delete_menus", LOG_DEBUG);
-		$resql=$this->db->query($sql);
-		if (! $resql)
-		{
-			$this->error=$this->db->lasterror();
-			$err++;
-		}
+        dol_syslog(get_class($this)."::delete_menus", LOG_DEBUG);
+        $resql=$this->db->query($sql);
+        if (! $resql) {
+            $this->error=$this->db->lasterror();
+            $err++;
+        }
 
-		return $err;
-	}
+        return $err;
+    }
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Creates directories
-	 *
-	 * @return  int Error count (0 if OK)
-	 */
-	function create_dirs()
-	{
+    /**
+     * Creates directories
+     *
+     * @return int Error count (0 if OK)
+     */
+    function create_dirs()
+    {
         // phpcs:enable
-		global $langs, $conf;
-
-		$err=0;
-
-		if (isset($this->dirs) && is_array($this->dirs))
-		{
-			foreach ($this->dirs as $key => $value)
-			{
-				$addtodatabase=0;
-
-				if (! is_array($value)) $dir=$value;    // Default simple mode
-				else {
-					$constname = $this->const_name."_DIR_";
-					$dir       = $this->dirs[$key][1];
-					$addtodatabase = empty($this->dirs[$key][2])?'':$this->dirs[$key][2]; // Create constante in llx_const
-					$subname   = empty($this->dirs[$key][3])?'':strtoupper($this->dirs[$key][3]); // Add submodule name (ex: $conf->module->submodule->dir_output)
-					$forcename = empty($this->dirs[$key][4])?'':strtoupper($this->dirs[$key][4]); // Change the module name if different
-
-					if (! empty($forcename)) $constname = 'MAIN_MODULE_'.$forcename."_DIR_";
-					if (! empty($subname))   $constname = $constname.$subname."_";
-
-					$name = $constname.strtoupper($this->dirs[$key][0]);
-				}
-
-				// Define directory full path ($dir must start with "/")
-				if (empty($conf->global->MAIN_MODULE_MULTICOMPANY) || $conf->entity == 1) $fulldir = DOL_DATA_ROOT.$dir;
-				else $fulldir = DOL_DATA_ROOT."/".$conf->entity.$dir;
-				// Create dir if it does not exists
-				if (! empty($fulldir) && ! file_exists($fulldir))
-				{
-					if (dol_mkdir($fulldir, DOL_DATA_ROOT) < 0)
-					{
-						$this->error = $langs->trans("ErrorCanNotCreateDir",$fulldir);
-						dol_syslog(get_class($this)."::_init ".$this->error, LOG_ERR);
-						$err++;
-					}
-				}
-
-				// Define the constant in database if requested (not the default mode)
-				if (! empty($addtodatabase))
-				{
-					$result = $this->insert_dirs($name, $dir);
-					if ($result) $err++;
-				}
-			}
-		}
-
-		return $err;
-	}
+        global $langs, $conf;
+
+        $err=0;
+
+        if (isset($this->dirs) && is_array($this->dirs)) {
+            foreach ($this->dirs as $key => $value)
+            {
+                $addtodatabase=0;
+
+                if (! is_array($value)) { $dir=$value;    // Default simple mode
+                } else {
+                    $constname = $this->const_name."_DIR_";
+                    $dir       = $this->dirs[$key][1];
+                    $addtodatabase = empty($this->dirs[$key][2])?'':$this->dirs[$key][2]; // Create constante in llx_const
+                    $subname   = empty($this->dirs[$key][3])?'':strtoupper($this->dirs[$key][3]); // Add submodule name (ex: $conf->module->submodule->dir_output)
+                    $forcename = empty($this->dirs[$key][4])?'':strtoupper($this->dirs[$key][4]); // Change the module name if different
+
+                    if (! empty($forcename)) { $constname = 'MAIN_MODULE_'.$forcename."_DIR_";
+                    }
+                    if (! empty($subname)) {   $constname = $constname.$subname."_";
+                    }
+
+                    $name = $constname.strtoupper($this->dirs[$key][0]);
+                }
+
+                // Define directory full path ($dir must start with "/")
+                if (empty($conf->global->MAIN_MODULE_MULTICOMPANY) || $conf->entity == 1) { $fulldir = DOL_DATA_ROOT.$dir;
+                } else { $fulldir = DOL_DATA_ROOT."/".$conf->entity.$dir;
+                }
+                // Create dir if it does not exists
+                if (! empty($fulldir) && ! file_exists($fulldir)) {
+                    if (dol_mkdir($fulldir, DOL_DATA_ROOT) < 0) {
+                         $this->error = $langs->trans("ErrorCanNotCreateDir", $fulldir);
+                         dol_syslog(get_class($this)."::_init ".$this->error, LOG_ERR);
+                         $err++;
+                    }
+                }
+
+                // Define the constant in database if requested (not the default mode)
+                if (! empty($addtodatabase)) {
+                    $result = $this->insert_dirs($name, $dir);
+                    if ($result) { $err++;
+                    }
+                }
+            }
+        }
+
+        return $err;
+    }
 
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Adds directories definitions
-	 *
-	 * @param   string  $name   Name
-	 * @param   string  $dir    Directory
-	 *
-	 * @return  int             Error count (0 if OK)
-	 */
-	function insert_dirs($name,$dir)
-	{
+    /**
+     * Adds directories definitions
+     *
+     * @param string $name Name
+     * @param string $dir  Directory
+     *
+     * @return int             Error count (0 if OK)
+     */
+    function insert_dirs($name,$dir)
+    {
         // phpcs:enable
-		global $conf;
+        global $conf;
 
-		$err=0;
+        $err=0;
 
-		$sql = "SELECT count(*)";
-		$sql.= " FROM ".MAIN_DB_PREFIX."const";
-		$sql.= " WHERE ".$this->db->decrypt('name')." = '".$name."'";
-		$sql.= " AND entity = ".$conf->entity;
+        $sql = "SELECT count(*)";
+        $sql.= " FROM ".MAIN_DB_PREFIX."const";
+        $sql.= " WHERE ".$this->db->decrypt('name')." = '".$name."'";
+        $sql.= " AND entity = ".$conf->entity;
 
-		dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
-		$result=$this->db->query($sql);
-		if ($result)
-		{
-			$row = $this->db->fetch_row($result);
+        dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
+        $result=$this->db->query($sql);
+        if ($result) {
+            $row = $this->db->fetch_row($result);
 
-			if ($row[0] == 0)
-			{
-				$sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name,type,value,note,visible,entity)";
-				$sql.= " VALUES (".$this->db->encrypt($name,1).",'chaine',".$this->db->encrypt($dir,1).",'Directory for module ".$this->name."','0',".$conf->entity.")";
+            if ($row[0] == 0) {
+                $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (name,type,value,note,visible,entity)";
+                $sql.= " VALUES (".$this->db->encrypt($name, 1).",'chaine',".$this->db->encrypt($dir, 1).",'Directory for module ".$this->name."','0',".$conf->entity.")";
 
-				dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
-				$this->db->query($sql);
-			}
-		}
-		else
-		{
-			$this->error=$this->db->lasterror();
-			$err++;
-		}
+                dol_syslog(get_class($this)."::insert_dirs", LOG_DEBUG);
+                $this->db->query($sql);
+            }
+        }
+        else
+        {
+            $this->error=$this->db->lasterror();
+            $err++;
+        }
 
-		return $err;
-	}
+        return $err;
+    }
 
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Removes directories
-	 *
-	 * @return  int Error count (0 if OK)
-	 */
-	function delete_dirs()
-	{
+    /**
+     * Removes directories
+     *
+     * @return int Error count (0 if OK)
+     */
+    function delete_dirs()
+    {
         // phpcs:enable
-		global $conf;
+        global $conf;
 
-		$err=0;
+        $err=0;
 
-		$sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
-		$sql.= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_DIR_%'";
-		$sql.= " AND entity = ".$conf->entity;
+        $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
+        $sql.= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_DIR_%'";
+        $sql.= " AND entity = ".$conf->entity;
 
-		dol_syslog(get_class($this)."::delete_dirs", LOG_DEBUG);
-		if (! $this->db->query($sql))
-		{
-			$this->error=$this->db->lasterror();
-			$err++;
-		}
+        dol_syslog(get_class($this)."::delete_dirs", LOG_DEBUG);
+        if (! $this->db->query($sql)) {
+            $this->error=$this->db->lasterror();
+            $err++;
+        }
 
-		return $err;
-	}
+        return $err;
+    }
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Adds generic parts
-	 *
-	 * @return  int Error count (0 if OK)
-	 */
-	function insert_module_parts()
-	{
+    /**
+     * Adds generic parts
+     *
+     * @return int Error count (0 if OK)
+     */
+    function insert_module_parts()
+    {
         // phpcs:enable
-		global $conf;
-
-		$error=0;
-
-		if (is_array($this->module_parts) && ! empty($this->module_parts))
-		{
-			foreach($this->module_parts as $key => $value)
-			{
-				if (is_array($value) && count($value) == 0) continue;	// Discard empty arrays
-
-				$entity=$conf->entity; // Reset the current entity
-				$newvalue = $value;
-
-				// Serialize array parameters
-				if (is_array($value))
-				{
-					// Can defined other parameters
-					// Example when $key='hooks', then $value is an array('data'=>array('hookcontext1','hookcontext2'), 'entity'=>X)
-					if (isset($value['data']) && is_array($value['data']))
-					{
-						$newvalue = json_encode($value['data']);
-						if (isset($value['entity'])) $entity = $value['entity'];
-					}
-					else if (isset($value['data']) && !is_array($value['data']))
-					{
-						$newvalue = $value['data'];
-						if (isset($value['entity'])) $entity = $value['entity'];
-					}
-					else	// when hook is declared with syntax 'hook'=>array('hookcontext1','hookcontext2',...)
-					{
-						$newvalue = json_encode($value);
-					}
-				}
-
-				$sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
-				$sql.= "name";
-				$sql.= ", type";
-				$sql.= ", value";
-				$sql.= ", note";
-				$sql.= ", visible";
-				$sql.= ", entity";
-				$sql.= ")";
-				$sql.= " VALUES (";
-				$sql.= $this->db->encrypt($this->const_name."_".strtoupper($key), 1);
-				$sql.= ", 'chaine'";
-				$sql.= ", ".$this->db->encrypt($newvalue, 1);
-				$sql.= ", null";
-				$sql.= ", '0'";
-				$sql.= ", ".$entity;
-				$sql.= ")";
-
-				dol_syslog(get_class($this)."::insert_module_parts for key=".$this->const_name."_".strtoupper($key), LOG_DEBUG);
-
-				$resql=$this->db->query($sql,1);
-				if (! $resql)
-				{
-					if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS')
-					{
-						$error++;
-						$this->error=$this->db->lasterror();
-					}
-					else
-					{
-						dol_syslog(get_class($this)."::insert_module_parts for ".$this->const_name."_".strtoupper($key)." Record already exists.", LOG_WARNING);
-					}
-				}
-			}
-		}
-		return $error;
-	}
+        global $conf;
+
+        $error=0;
+
+        if (is_array($this->module_parts) && ! empty($this->module_parts)) {
+            foreach($this->module_parts as $key => $value)
+            {
+                if (is_array($value) && count($value) == 0) { continue;    // Discard empty arrays
+                }
+
+                $entity=$conf->entity; // Reset the current entity
+                $newvalue = $value;
+
+                // Serialize array parameters
+                if (is_array($value)) {
+                    // Can defined other parameters
+                    // Example when $key='hooks', then $value is an array('data'=>array('hookcontext1','hookcontext2'), 'entity'=>X)
+                    if (isset($value['data']) && is_array($value['data'])) {
+                        $newvalue = json_encode($value['data']);
+                        if (isset($value['entity'])) { $entity = $value['entity'];
+                        }
+                    }
+                    else if (isset($value['data']) && !is_array($value['data'])) {
+                        $newvalue = $value['data'];
+                        if (isset($value['entity'])) { $entity = $value['entity'];
+                        }
+                    }
+                    else    // when hook is declared with syntax 'hook'=>array('hookcontext1','hookcontext2',...)
+                    {
+                        $newvalue = json_encode($value);
+                    }
+                }
+
+                $sql = "INSERT INTO ".MAIN_DB_PREFIX."const (";
+                $sql.= "name";
+                $sql.= ", type";
+                $sql.= ", value";
+                $sql.= ", note";
+                $sql.= ", visible";
+                $sql.= ", entity";
+                $sql.= ")";
+                $sql.= " VALUES (";
+                $sql.= $this->db->encrypt($this->const_name."_".strtoupper($key), 1);
+                $sql.= ", 'chaine'";
+                $sql.= ", ".$this->db->encrypt($newvalue, 1);
+                $sql.= ", null";
+                $sql.= ", '0'";
+                $sql.= ", ".$entity;
+                $sql.= ")";
+
+                dol_syslog(get_class($this)."::insert_module_parts for key=".$this->const_name."_".strtoupper($key), LOG_DEBUG);
+
+                $resql=$this->db->query($sql, 1);
+                if (! $resql) {
+                    if ($this->db->lasterrno() != 'DB_ERROR_RECORD_ALREADY_EXISTS') {
+                         $error++;
+                         $this->error=$this->db->lasterror();
+                    }
+                    else
+                    {
+                         dol_syslog(get_class($this)."::insert_module_parts for ".$this->const_name."_".strtoupper($key)." Record already exists.", LOG_WARNING);
+                    }
+                }
+            }
+        }
+        return $error;
+    }
 
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
-	/**
-	 * Removes generic parts
-	 *
-	 * @return  int Error count (0 if OK)
-	 */
-	function delete_module_parts()
-	{
+    /**
+     * Removes generic parts
+     *
+     * @return int Error count (0 if OK)
+     */
+    function delete_module_parts()
+    {
         // phpcs:enable
-		global $conf;
-
-		$err=0;
-		$entity=$conf->entity;
-
-		if (is_array($this->module_parts) && ! empty($this->module_parts))
-		{
-			foreach($this->module_parts as $key => $value)
-			{
-				// If entity is defined
-				if (is_array($value) && isset($value['entity'])) $entity = $value['entity'];
-
-				$sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
-				$sql.= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_".strtoupper($key)."'";
-				$sql.= " AND entity = ".$entity;
-
-				dol_syslog(get_class($this)."::delete_const_".$key."", LOG_DEBUG);
-				if (! $this->db->query($sql))
-				{
-					$this->error=$this->db->lasterror();
-					$err++;
-				}
-			}
-		}
-		return $err;
-	}
-
-	/**
-	 * Function called when module is enabled.
-	 * The init function adds tabs, constants, boxes, permissions and menus (defined in constructor) into Dolibarr database.
-	 * It also creates data directories
-	 *
-	 * @param string $options   Options when enabling module ('', 'newboxdefonly', 'noboxes')
-	 *                          'noboxes' = Do not insert boxes
-	 *                          'newboxdefonly' = For boxes, insert def of boxes only and not boxes activation
-	 * @return int				1 if OK, 0 if KO
-	 */
-	public function init($options = '')
-	{
-		return $this->_init(array(), $options);
-	}
-
-	/**
-	 * Function called when module is disabled.
-	 * The remove function removes tabs, constants, boxes, permissions and menus from Dolibarr database.
-	 * Data directories are not deleted
-	 *
-	 * @param      string	$options    Options when enabling module ('', 'noboxes')
-	 * @return     int             		1 if OK, 0 if KO
-	 */
-	public function remove($options = '')
-	{
-		return $this->_remove(array(), $options);
-	}
+        global $conf;
+
+        $err=0;
+        $entity=$conf->entity;
+
+        if (is_array($this->module_parts) && ! empty($this->module_parts)) {
+            foreach($this->module_parts as $key => $value)
+            {
+                // If entity is defined
+                if (is_array($value) && isset($value['entity'])) { $entity = $value['entity'];
+                }
+
+                $sql = "DELETE FROM ".MAIN_DB_PREFIX."const";
+                $sql.= " WHERE ".$this->db->decrypt('name')." LIKE '".$this->db->escape($this->const_name)."_".strtoupper($key)."'";
+                $sql.= " AND entity = ".$entity;
+
+                dol_syslog(get_class($this)."::delete_const_".$key."", LOG_DEBUG);
+                if (! $this->db->query($sql)) {
+                    $this->error=$this->db->lasterror();
+                    $err++;
+                }
+            }
+        }
+        return $err;
+    }
+
+    /**
+     * Function called when module is enabled.
+     * The init function adds tabs, constants, boxes, permissions and menus (defined in constructor) into Dolibarr database.
+     * It also creates data directories
+     *
+     * @param  string $options Options when enabling module ('', 'newboxdefonly', 'noboxes')
+     *                         'noboxes' = Do not insert boxes 'newboxdefonly' = For boxes,
+     *                         insert def of boxes only and not boxes activation
+     * @return int                1 if OK, 0 if KO
+     */
+    public function init($options = '')
+    {
+        return $this->_init(array(), $options);
+    }
+
+    /**
+     * Function called when module is disabled.
+     * The remove function removes tabs, constants, boxes, permissions and menus from Dolibarr database.
+     * Data directories are not deleted
+     *
+     * @param  string $options Options when enabling module ('', 'noboxes')
+     * @return int                     1 if OK, 0 if KO
+     */
+    public function remove($options = '')
+    {
+        return $this->_remove(array(), $options);
+    }
 }

+ 1 - 1
htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php

@@ -379,7 +379,7 @@ class doc_generic_order_odt extends ModelePDFCommandes
 				$array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs);
 				$array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs);
 				$array_other=$this->get_substitutionarray_other($outputlangs);
-				// retrieve contact information for use in order as contact_xxx tags
+				// retrieve contact information for use in object as contact_xxx tags
 				$array_thirdparty_contact = array();
 				if ($usecontact && is_object($contactobject)) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact');
 

+ 1 - 1
htdocs/core/modules/contract/doc/doc_generic_contract_odt.modules.php

@@ -317,7 +317,7 @@ class doc_generic_contract_odt extends ModelePDFContract
 				$array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs);
 				$array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs);
 				$array_other=$this->get_substitutionarray_other($outputlangs);
-				// retrieve contact information for use in contract as contact_xxx tags
+				// retrieve contact information for use in order as contact_xxx tags
 				$array_thirdparty_contact = array();
 				if ($usecontact && is_object($contactobject)) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact');
 

+ 55 - 38
htdocs/core/modules/expedition/doc/pdf_rouget.modules.php

@@ -146,8 +146,7 @@ class pdf_rouget extends ModelePdfExpedition
 		$this->posxqtytoship=$this->page_largeur - $this->marge_droite - 28;
 		$this->posxpuht=$this->page_largeur - $this->marge_droite;
 
-		if (!empty($conf->global->MAIN_PDF_SHIPPING_DISPLAY_AMOUNT_HT)) {
-
+		if (!empty($conf->global->SHIPPING_PDF_DISPLAY_AMOUNT_HT)) {	// Show also the prices
 			$this->posxweightvol=$this->page_largeur - $this->marge_droite - 118;
 			$this->posxqtyordered=$this->page_largeur - $this->marge_droite - 96;
 			$this->posxqtytoship=$this->page_largeur - $this->marge_droite - 68;
@@ -173,7 +172,7 @@ class pdf_rouget extends ModelePdfExpedition
 		}
 	}
 
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
 	/**
 	 *	Function to build pdf onto disk
 	 *
@@ -187,7 +186,7 @@ class pdf_rouget extends ModelePdfExpedition
 	 */
 	function write_file($object,$outputlangs,$srctemplatepath='',$hidedetails=0,$hidedesc=0,$hideref=0)
 	{
-        // phpcs:enable
+		// phpcs:enable
 		global $user,$conf,$langs,$hookmanager;
 
 		$object->fetch_thirdparty();
@@ -533,8 +532,11 @@ class pdf_rouget extends ModelePdfExpedition
 					    $voltxt=round($object->lines[$i]->volume * $object->lines[$i]->qty_shipped, 5).' '.measuring_units_string($object->lines[$i]->volume_units?$object->lines[$i]->volume_units:0,"volume");
 					}
 
-					$pdf->writeHTMLCell($this->posxqtyordered - $this->posxweightvol + 2, 3, $this->posxweightvol - 1, $curY, $weighttxt.(($weighttxt && $voltxt)?'<br>':'').$voltxt, 0, 0, false, true, 'C');
-					//$pdf->MultiCell(($this->posxqtyordered - $this->posxweightvol), 3, $weighttxt.(($weighttxt && $voltxt)?'<br>':'').$voltxt,'','C');
+					if (empty($conf->global->SHIPPING_PDF_HIDE_WEIGHT_AND_VOLUME))
+					{
+						$pdf->writeHTMLCell($this->posxqtyordered - $this->posxweightvol + 2, 3, $this->posxweightvol - 1, $curY, $weighttxt.(($weighttxt && $voltxt)?'<br>':'').$voltxt, 0, 0, false, true, 'C');
+						//$pdf->MultiCell(($this->posxqtyordered - $this->posxweightvol), 3, $weighttxt.(($weighttxt && $voltxt)?'<br>':'').$voltxt,'','C');
+					}
 
 					if (empty($conf->global->SHIPPING_PDF_HIDE_ORDERED))
 					{
@@ -542,10 +544,13 @@ class pdf_rouget extends ModelePdfExpedition
 					   $pdf->MultiCell(($this->posxqtytoship - $this->posxqtyordered), 3, $object->lines[$i]->qty_asked,'','C');
 					}
 
-					$pdf->SetXY($this->posxqtytoship, $curY);
-					$pdf->MultiCell(($this->posxpuht - $this->posxqtytoship), 3, $object->lines[$i]->qty_shipped,'','C');
+					if (empty($conf->global->SHIPPING_PDF_HIDE_QTYTOSHIP))
+					{
+						$pdf->SetXY($this->posxqtytoship, $curY);
+						$pdf->MultiCell(($this->posxpuht - $this->posxqtytoship), 3, $object->lines[$i]->qty_shipped,'','C');
+					}
 
-					if(!empty($conf->global->MAIN_PDF_SHIPPING_DISPLAY_AMOUNT_HT))
+					if(!empty($conf->global->SHIPPING_PDF_DISPLAY_AMOUNT_HT))
 					{
 						$pdf->SetXY($this->posxpuht, $curY);
 						$pdf->MultiCell(($this->posxtotalht - $this->posxpuht-1), 3, price($object->lines[$i]->subprice, 0, $outputlangs),'','R');
@@ -653,7 +658,7 @@ class pdf_rouget extends ModelePdfExpedition
 		}
 	}
 
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
 	/**
 	 *	Show total to pay
 	 *
@@ -666,7 +671,7 @@ class pdf_rouget extends ModelePdfExpedition
 	 */
 	function _tableau_tot(&$pdf, $object, $deja_regle, $posy, $outputlangs)
 	{
-        // phpcs:enable
+		// phpcs:enable
 		global $conf,$mysoc;
 
         $sign=1;
@@ -720,11 +725,14 @@ class pdf_rouget extends ModelePdfExpedition
         	$pdf->MultiCell($this->posxqtytoship - $this->posxqtyordered, $tab2_hl, $totalOrdered, 0, 'C', 1);
         }
 
-    	$pdf->SetXY($this->posxqtytoship, $tab2_top + $tab2_hl * $index);
-    	$pdf->MultiCell($this->posxpuht - $this->posxqtytoship, $tab2_hl, $totalToShip, 0, 'C', 1);
-
-		if(!empty($conf->global->MAIN_PDF_SHIPPING_DISPLAY_AMOUNT_HT)) {
+        if (empty($conf->global->SHIPPING_PDF_HIDE_QTYTOSHIP))
+        {
+        	$pdf->SetXY($this->posxqtytoship, $tab2_top + $tab2_hl * $index);
+        	$pdf->MultiCell($this->posxpuht - $this->posxqtytoship, $tab2_hl, $totalToShip, 0, 'C', 1);
+        }
 
+		if (!empty($conf->global->SHIPPING_PDF_DISPLAY_AMOUNT_HT))
+		{
 	    	$pdf->SetXY($this->posxpuht, $tab2_top + $tab2_hl * $index);
 	    	$pdf->MultiCell($this->posxtotalht - $this->posxpuht, $tab2_hl, '', 0, 'C', 1);
 
@@ -732,22 +740,25 @@ class pdf_rouget extends ModelePdfExpedition
 	    	$pdf->MultiCell($this->page_largeur - $this->marge_droite - $this->posxtotalht, $tab2_hl, price($object->total_ht, 0, $outputlangs), 0, 'C', 1);
 		}
 
-		// Total Weight
-		if ($totalWeighttoshow)
+		if (empty($conf->global->SHIPPING_PDF_HIDE_WEIGHT_AND_VOLUME))
 		{
-    		$pdf->SetXY($this->posxweightvol, $tab2_top + $tab2_hl * $index);
-    		$pdf->MultiCell(($this->posxqtyordered - $this->posxweightvol), $tab2_hl, $totalWeighttoshow, 0, 'C', 1);
+			// Total Weight
+			if ($totalWeighttoshow)
+			{
+	    		$pdf->SetXY($this->posxweightvol, $tab2_top + $tab2_hl * $index);
+	    		$pdf->MultiCell(($this->posxqtyordered - $this->posxweightvol), $tab2_hl, $totalWeighttoshow, 0, 'C', 1);
 
-    		$index++;
-		}
-		if ($totalVolumetoshow)
-		{
-    		$pdf->SetXY($this->posxweightvol, $tab2_top + $tab2_hl * $index);
-    		$pdf->MultiCell(($this->posxqtyordered - $this->posxweightvol), $tab2_hl, $totalVolumetoshow, 0, 'C', 1);
+	    		$index++;
+			}
+			if ($totalVolumetoshow)
+			{
+	    		$pdf->SetXY($this->posxweightvol, $tab2_top + $tab2_hl * $index);
+	    		$pdf->MultiCell(($this->posxqtyordered - $this->posxweightvol), $tab2_hl, $totalVolumetoshow, 0, 'C', 1);
 
-		    $index++;
+			    $index++;
+			}
+			if (! $totalWeighttoshow && ! $totalVolumetoshow) $index++;
 		}
-		if (! $totalWeighttoshow && ! $totalVolumetoshow) $index++;
 
 		$pdf->SetTextColor(0,0,0);
 
@@ -794,11 +805,14 @@ class pdf_rouget extends ModelePdfExpedition
 			$pdf->MultiCell($this->posxqtyordered - $this->posxdesc, 2, $outputlangs->transnoentities("Description"), '', 'L');
 		}
 
-		$pdf->line($this->posxweightvol-1, $tab_top, $this->posxweightvol-1, $tab_top + $tab_height);
-		if (empty($hidetop))
+		if (empty($conf->global->SHIPPING_PDF_HIDE_WEIGHT_AND_VOLUME))
 		{
-			$pdf->SetXY($this->posxweightvol-1, $tab_top+1);
-			$pdf->MultiCell(($this->posxqtyordered - $this->posxweightvol), 2, $outputlangs->transnoentities("WeightVolShort"),'','C');
+			$pdf->line($this->posxweightvol-1, $tab_top, $this->posxweightvol-1, $tab_top + $tab_height);
+			if (empty($hidetop))
+			{
+				$pdf->SetXY($this->posxweightvol-1, $tab_top+1);
+				$pdf->MultiCell(($this->posxqtyordered - $this->posxweightvol), 2, $outputlangs->transnoentities("WeightVolShort"),'','C');
+			}
 		}
 
         if (empty($conf->global->SHIPPING_PDF_HIDE_ORDERED))
@@ -811,14 +825,17 @@ class pdf_rouget extends ModelePdfExpedition
     		}
         }
 
-		$pdf->line($this->posxqtytoship-1, $tab_top, $this->posxqtytoship-1, $tab_top + $tab_height);
-		if (empty($hidetop))
-		{
-			$pdf->SetXY($this->posxqtytoship, $tab_top+1);
-			$pdf->MultiCell(($this->posxpuht - $this->posxqtytoship), 2, $outputlangs->transnoentities("QtyToShip"),'','C');
-		}
+        if (empty($conf->global->SHIPPING_PDF_HIDE_QTYTOSHIP))
+        {
+			$pdf->line($this->posxqtytoship-1, $tab_top, $this->posxqtytoship-1, $tab_top + $tab_height);
+			if (empty($hidetop))
+			{
+				$pdf->SetXY($this->posxqtytoship, $tab_top+1);
+				$pdf->MultiCell(($this->posxpuht - $this->posxqtytoship), 2, $outputlangs->transnoentities("QtyToShip"),'','C');
+			}
+        }
 
-		if(!empty($conf->global->MAIN_PDF_SHIPPING_DISPLAY_AMOUNT_HT)) {
+		if (!empty($conf->global->SHIPPING_PDF_DISPLAY_AMOUNT_HT)) {
 
 			$pdf->line($this->posxpuht-1, $tab_top, $this->posxpuht-1, $tab_top + $tab_height);
 			if (empty($hidetop))

+ 1 - 1
htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php

@@ -387,7 +387,7 @@ class doc_generic_invoice_odt extends ModelePDFFactures
 				$array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs);
 				$array_propal=is_object($propal_object)?$this->get_substitutionarray_object($propal_object,$outputlangs,'propal'):array();
 				$array_other=$this->get_substitutionarray_other($outputlangs);
-				// retrieve contact information for use in invoice as contact_xxx tags
+				// retrieve contact information for use in object as contact_xxx tags
 				$array_thirdparty_contact = array();
 				if ($usecontact && is_object($contactobject)) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact');
 

+ 3 - 3
htdocs/core/modules/modAdherent.class.php

@@ -355,13 +355,13 @@ class modAdherent extends DolibarrModules
 				'jobtype'=>'method', 'class'=>'adherents/class/adherent.class.php',
 				'objectname'=>'Adherent',
 				'method'=>'sendReminderForExpiredSubscription',
-				'parameters'=>'10',
+				'parameters'=>'10;0',
 				'comment'=>'SendReminderForExpiredSubscription',
 				'frequency'=>1,
 				'unitfrequency'=> 3600 * 24,
 				'priority'=>50,
-				'status'=>0,
-				'test'=>true,
+				'status'=>1,
+				'test'=>'$conf->adherent->enabled',
 				'datestart'=>$datestart
 			),
         );

+ 6 - 18
htdocs/core/modules/modDataPolicy.class.php

@@ -57,9 +57,9 @@ class modDataPolicy extends DolibarrModules {
 
         // Family can be 'base' (core modules),'crm','financial','hr','projects','products','ecm','technic' (transverse modules),'interface' (link with external tools),'other','...'
         // It is used to group modules by family in module setup page
-        $this->family = "hr";
+        $this->family = "technic";
         // Module position in the family on 2 digits ('01', '10', '20', ...)
-        $this->module_position = '70';
+        $this->module_position = '81';
         // Gives the possibility to the module, to provide his own family info and position of this family (Overwrite $this->family and $this->module_position. Avoid this)
         //$this->familyinfo = array('myownfamily' => array('position' => '01', 'label' => $langs->trans("MyOwnFamily")));
         // Module label (no space allowed), used if translation string 'ModuledatapolicyName' not found (MyModue is name of module).
@@ -103,7 +103,7 @@ class modDataPolicy extends DolibarrModules {
 
         // Dependencies
         $this->hidden = false;   // A condition to hide module
-        $this->depends = array();  // List of module class names as string that must be enabled if this module is enabled
+        $this->depends = array('always'=>'modCron');  // List of module class names as string that must be enabled if this module is enabled
         $this->requiredby = array(); // List of module ids to disable if this one is disabled
         $this->conflictwith = array(); // List of module class names as string this module is in conflict with
         $this->langfiles = array("datapolicy@datapolicy");
@@ -173,22 +173,10 @@ class modDataPolicy extends DolibarrModules {
         // 'stock'            to add a tab in stock view
         // 'thirdparty'       to add a tab in third party view
         // 'user'             to add a tab in user view
+
+
         // Dictionaries
         $this->dictionaries = array();
-        /* Example:
-          $this->dictionaries=array(
-          'langs'=>'mylangfile@datapolicy',
-          'tabname'=>array(MAIN_DB_PREFIX."table1",MAIN_DB_PREFIX."table2",MAIN_DB_PREFIX."table3"),		// List of tables we want to see into dictonnary editor
-          'tablib'=>array("Table1","Table2","Table3"),													// Label of tables
-          'tabsql'=>array('SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table1 as f','SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table2 as f','SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table3 as f'),	// Request to select fields
-          'tabsqlsort'=>array("label ASC","label ASC","label ASC"),																					// Sort order
-          'tabfield'=>array("code,label","code,label","code,label"),																					// List of fields (result of select to show dictionary)
-          'tabfieldvalue'=>array("code,label","code,label","code,label"),																				// List of fields (list of fields to edit a record)
-          'tabfieldinsert'=>array("code,label","code,label","code,label"),																			// List of fields (list of fields for insert)
-          'tabrowid'=>array("rowid","rowid","rowid"),																									// Name of columns with primary key (try to always name it 'rowid')
-          'tabcond'=>array($conf->datapolicy->enabled,$conf->datapolicy->enabled,$conf->datapolicy->enabled)												// Condition to show each dictionary
-          );
-         */
 
 
         // Boxes/Widgets
@@ -199,7 +187,7 @@ class modDataPolicy extends DolibarrModules {
         // Cronjobs (List of cron jobs entries to add when module is enabled)
         // unit_frequency must be 60 for minute, 3600 for hour, 86400 for day, 604800 for week
         $this->cronjobs = array(
-            0 => array('label' => 'DATAPOLICY Cron', 'jobtype' => 'method', 'class' => '/datapolicy/class/datapolicyCron.class.php', 'objectname' => 'RgpdCron', 'method' => 'exec', 'parameters' => '', 'comment' => 'Comment', 'frequency' => 1, 'unitfrequency' => 86400, 'status' => 1, 'test' => true),
+            0 => array('label' => 'DATAPOLICY Cron', 'jobtype' => 'method', 'class' => '/datapolicy/class/datapolicyCron.class.php', 'objectname' => 'DataPolicyCron', 'method' => 'exec', 'parameters' => '', 'comment' => 'Clean data', 'frequency' => 1, 'unitfrequency' => 86400, 'status' => 1, 'test' => '$conf->datapolicy->enabled'),
             //1 => array('label' => 'DATAPOLICY Mailing', 'jobtype' => 'method', 'class' => '/datapolicy/class/datapolicyCron.class.php', 'objectname' => 'RgpdCron', 'method' => 'sendMailing', 'parameters' => '', 'comment' => 'Comment', 'frequency' => 1, 'unitfrequency' => 86400, 'status' => 0, 'test' => true)
         );
         // Example: $this->cronjobs=array(0=>array('label'=>'My label', 'jobtype'=>'method', 'class'=>'/dir/class/file.class.php', 'objectname'=>'MyClass', 'method'=>'myMethod', 'parameters'=>'param1, param2', 'comment'=>'Comment', 'frequency'=>2, 'unitfrequency'=>3600, 'status'=>0, 'test'=>true),

+ 7 - 7
htdocs/core/modules/modEmailCollector.class.php

@@ -52,7 +52,7 @@ class modEmailCollector extends DolibarrModules
 		// It is used to group modules by family in module setup page
 		$this->family = "interface";
 		// Module position in the family on 2 digits ('01', '10', '20', ...)
-		$this->module_position = '75';
+		$this->module_position = '12';
 		// Gives the possibility to the module, to provide his own family info and position of this family (Overwrite $this->family and $this->module_position. Avoid this)
 		//$this->familyinfo = array('myownfamily' => array('position' => '01', 'label' => $langs->trans("MyOwnFamily")));
 
@@ -70,7 +70,7 @@ class modEmailCollector extends DolibarrModules
 		// Name of image file used for this module.
 		// If file is in theme/yourtheme/img directory under name object_pictovalue.png, use this->picto='pictovalue'
 		// If file is in module/img directory under name object_pictovalue.png, use this->picto='pictovalue@module'
-		$this->picto='generic';
+		$this->picto='email';
 
 		// Defined all module parts (triggers, login, substitutions, menus, css, etc...)
 		// for default path (eg: /dav/core/xxxxx) (0=disable, 1=enable)
@@ -83,11 +83,11 @@ class modEmailCollector extends DolibarrModules
 		$this->dirs = array();
 
 		// Config pages. Put here list of php page, stored into dav/admin directory, to use to setup module.
-		$this->config_page_url = array("emailcollector.php");
+		$this->config_page_url = array("emailcollector_list.php");
 
 		// Dependencies
 		$this->hidden = false;			// A condition to hide module
-		$this->depends = array();		// List of module class names as string that must be enabled if this module is enabled
+		$this->depends = array('always'=>'modCron');		// List of module class names as string that must be enabled if this module is enabled
 		$this->requiredby = array();	// List of module ids to disable if this one is disabled
 		$this->conflictwith = array();	// List of module class names as string this module is in conflict with
 		$this->langfiles = array("admin");
@@ -173,9 +173,9 @@ class modEmailCollector extends DolibarrModules
 
 		// Cronjobs (List of cron jobs entries to add when module is enabled)
 		// unit_frequency must be 60 for minute, 3600 for hour, 86400 for day, 604800 for week
-		//$this->cronjobs = array(
-			//0=>array('label'=>'MyJob label', 'jobtype'=>'method', 'class'=>'/dav/class/myobject.class.php', 'objectname'=>'MyObject', 'method'=>'doScheduledJob', 'parameters'=>'', 'comment'=>'Comment', 'frequency'=>2, 'unitfrequency'=>3600, 'status'=>0, 'test'=>true)
-		//);
+		$this->cronjobs = array(
+			0=>array('label'=>'Email collector', 'jobtype'=>'method', 'class'=>'/emailcollector/class/emailcollector.class.php', 'objectname'=>'EmailCollector', 'method'=>'doCollect', 'parameters'=>'', 'comment'=>'Comment', 'frequency'=>5, 'unitfrequency'=>60, 'status'=>1, 'test'=>'$conf->emailcollector->enabled')
+		);
 		// Example: $this->cronjobs=array(0=>array('label'=>'My label', 'jobtype'=>'method', 'class'=>'/dir/class/file.class.php', 'objectname'=>'MyClass', 'method'=>'myMethod', 'parameters'=>'param1, param2', 'comment'=>'Comment', 'frequency'=>2, 'unitfrequency'=>3600, 'status'=>0, 'test'=>true),
 		//                                1=>array('label'=>'My label', 'jobtype'=>'command', 'command'=>'', 'parameters'=>'param1, param2', 'comment'=>'Comment', 'frequency'=>1, 'unitfrequency'=>3600*24, 'status'=>0, 'test'=>true)
 		// );

+ 2 - 0
htdocs/core/modules/modGravatar.class.php

@@ -49,6 +49,8 @@ class modGravatar extends DolibarrModules
 		// Family can be 'crm','financial','hr','projects','products','ecm','technic','other'
 		// It is used to group modules in module setup page
 		$this->family = "interface";
+		// Module position in the family on 2 digits ('01', '10', '20', ...)
+		$this->module_position = '75';
 		// Module label (no space allowed), used if translation string 'ModuleXXXName' not found (where XXX is value of numeric property 'numero' of module)
 		$this->name = preg_replace('/^mod/i', '', get_class($this));
 		// Module description, used if translation string 'ModuleXXXDesc' not found (where XXX is value of numeric property 'numero' of module)

+ 2 - 0
htdocs/core/modules/modLdap.class.php

@@ -43,6 +43,8 @@ class modLdap extends DolibarrModules
 		$this->numero = 200;
 
 		$this->family = "interface";
+		// Module position in the family on 2 digits ('01', '10', '20', ...)
+		$this->module_position = '30';
 		// Module label (no space allowed), used if translation string 'ModuleXXXName' not found (where XXX is value of numeric property 'numero' of module)
 		$this->name = preg_replace('/^mod/i','',get_class($this));
 		$this->description = "Synchronisation Ldap";

+ 6 - 1
htdocs/core/modules/modMailing.class.php

@@ -44,7 +44,12 @@ class modMailing extends DolibarrModules
 		$this->db = $db;
 		$this->numero = 22;
 
-		$this->family = "technic";
+		// Family can be 'base' (core modules),'crm','financial','hr','projects','products','ecm','technic' (transverse modules),'interface' (link with external tools),'other','...'
+		// It is used to group modules by family in module setup page
+		$this->family = "interface";
+		// Module position in the family on 2 digits ('01', '10', '20', ...)
+		$this->module_position = '11';
+
 		// Module label (no space allowed), used if translation string 'ModuleXXXName' not found (where XXX is value of numeric property 'numero' of module)
 		$this->name = preg_replace('/^mod/i','',get_class($this));
 		$this->description = "Gestion des EMailings";

+ 4 - 0
htdocs/core/modules/modMailmanSpip.class.php

@@ -43,7 +43,11 @@ class modMailmanSpip extends DolibarrModules
 		$this->db = $db;
 		$this->numero = 105;
 
+		// Family can be 'crm','financial','hr','projects','products','ecm','technic','other'
+		// It is used to group modules in module setup page
 		$this->family = "interface";
+		// Module position in the family on 2 digits ('01', '10', '20', ...)
+		$this->module_position = '70';
 		// Module label (no space allowed), used if translation string 'ModuleXXXName' not found (where XXX is value of numeric property 'numero' of module)
 		$this->name = preg_replace('/^mod/i','',get_class($this));
 		$this->description = "Mailman or Spip interface for member module";

+ 4 - 0
htdocs/core/modules/modNotification.class.php

@@ -41,7 +41,11 @@ class modNotification extends DolibarrModules
 		$this->db = $db;
 		$this->numero = 600;
 
+		// Family can be 'crm','financial','hr','projects','products','ecm','technic','other'
+		// It is used to group modules in module setup page
 		$this->family = "interface";
+		// Module position in the family on 2 digits ('01', '10', '20', ...)
+		$this->module_position = '01';
 		// Module label (no space allowed), used if translation string 'ModuleXXXName' not found (where XXX is value of numeric property 'numero' of module)
 		$this->name = preg_replace('/^mod/i','',get_class($this));
 		$this->description = "EMail notifications (push) on business Dolibarr events";

+ 1 - 1
htdocs/core/modules/modOauth.class.php

@@ -47,7 +47,7 @@ class modOauth extends DolibarrModules
         // Family can be 'crm','financial','hr','projects','products','ecm','technic','other'
         // It is used to group modules in module setup page
         $this->family = "interface";
-        $this->module_position = '51';
+        $this->module_position = '31';
         // Module label (no space allowed), used if translation string 'ModuleXXXName' not found (where XXX is value of numeric property 'numero' of module)
         $this->name = preg_replace('/^mod/i','',get_class($this));
         // Module description, used if translation string 'ModuleXXXDesc' not found (where XXX is value of numeric property 'numero' of module)

+ 14 - 14
htdocs/core/modules/modPaypal.class.php

@@ -107,21 +107,21 @@ class modPaypal extends DolibarrModules
         // Main menu entries
         $this->menus = array();			// List of menus to add
         $r=0;
-        $this->menu[$r]=array(
-        'fk_menu'=>'fk_mainmenu=billing,fk_leftmenu=customers_bills_payment',		    // Use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode
-        'mainmenu'=>'billing',
-        'leftmenu'=>'customers_bills_payment_paypal',
-        'type'=>'left',			                // This is a Left menu entry
-        'titre'=>'PaypalImportPayment',
-        'url'=>'/paypal/importpayments.php',
-        'langs'=>'paypal',	        // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory.
-        'position'=>501,
-        'enabled'=>'$conf->paypal->enabled && $conf->banque->enabled && $conf->global->MAIN_FEATURES_LEVEL >= 2',  // Define condition to show or hide menu entry. Use '$conf->mymodule->enabled' if entry must be visible if module is enabled. Use '$leftmenu==\'system\'' to show if leftmenu system is selected.
-        'perms'=>'$user->rights->banque->consolidate',	// Use 'perms'=>'$user->rights->mymodule->level1->level2' if you want your menu with a permission rules
-        'target'=>'',
-        'user'=>2
+        /*$this->menu[$r]=array(
+	        'fk_menu'=>'fk_mainmenu=billing,fk_leftmenu=customers_bills_payment',		    // Use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode
+	        'mainmenu'=>'billing',
+	        'leftmenu'=>'customers_bills_payment_paypal',
+	        'type'=>'left',			                // This is a Left menu entry
+	        'titre'=>'PaypalImportPayment',
+	        'url'=>'/paypal/importpayments.php',
+	        'langs'=>'paypal',	        // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory.
+	        'position'=>501,
+	        'enabled'=>'$conf->paypal->enabled && $conf->banque->enabled && $conf->global->MAIN_FEATURES_LEVEL >= 2',  // Define condition to show or hide menu entry. Use '$conf->mymodule->enabled' if entry must be visible if module is enabled. Use '$leftmenu==\'system\'' to show if leftmenu system is selected.
+	        'perms'=>'$user->rights->banque->consolidate',	// Use 'perms'=>'$user->rights->mymodule->level1->level2' if you want your menu with a permission rules
+	        'target'=>'',
+	        'user'=>2
         );				                // 0=Menu for internal users, 1=external users, 2=both
-        $r++;
+        $r++;*/
 
         // Add here entries to declare new menus
         // Example to declare the Top Menu entry:

+ 1 - 1
htdocs/core/modules/modProduct.class.php

@@ -239,7 +239,7 @@ class modProduct extends DolibarrModules
 				'pr.date_price'=>"product");
 			$this->export_sql_start[$r]='SELECT DISTINCT ';
 			$this->export_sql_end[$r]  =' FROM '.MAIN_DB_PREFIX.'product as p';
-			$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'product_price as pr ON p.rowid = pr.fk_product';
+			$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'product_price as pr ON p.rowid = pr.fk_product AND pr.entity = '.$conf->entity; // export prices only for the current entity
 			$this->export_sql_end[$r] .=' WHERE p.fk_product_type = 0 AND p.entity IN ('.getEntity('product').')';
 		}
 

+ 2 - 0
htdocs/core/modules/modSocialNetworks.class.php

@@ -46,6 +46,8 @@ class modSocialNetworks extends DolibarrModules
 		// Family can be 'crm','financial','hr','projects','products','ecm','technic','other'
 		// It is used to group modules in module setup page
         $this->family = "interface";
+        // Module position in the family on 2 digits ('01', '10', '20', ...)
+        $this->module_position = '20';
         // Module label (no space allowed), used if translation string 'ModuleXXXName' not found (where XXX is value of numeric property 'numero' of module)
         $this->name = preg_replace('/^mod/i','',get_class($this));
         $this->description = "Enable Social Networks fields into third parties and addresses (skype, twitter, facebook, ...)";

+ 5 - 5
htdocs/core/modules/modSociete.class.php

@@ -261,7 +261,7 @@ class modSociete extends DolibarrModules
 		$this->export_icon[$r]='company';
 		$this->export_permission[$r]=array(array("societe","export"));
 		$this->export_fields_array[$r]=array(
-			's.rowid'=>"Id",'s.nom'=>"Name",'s.status'=>"Status",'s.client'=>"Customer",'s.fournisseur'=>"Supplier",'s.datec'=>"DateCreation",'s.tms'=>"DateLastModification",
+			's.rowid'=>"Id",'s.nom'=>"Name",'s.name_alias'=>"AliasNames",'s.status'=>"Status",'s.client'=>"Customer",'s.fournisseur'=>"Supplier",'s.datec'=>"DateCreation",'s.tms'=>"DateLastModification",
 			's.code_client'=>"CustomerCode",'s.code_fournisseur'=>"SupplierCode",'s.code_compta'=>"AccountancyCode",'s.code_compta_fournisseur'=>"SupplierAccountancyCode",
 			's.address'=>"Address",'s.zip'=>"Zip",'s.town'=>"Town",'d.nom'=>'State','c.label'=>"Country",'c.code'=>"CountryCode",'s.phone'=>"Phone",'s.fax'=>"Fax",
 			's.url'=>"Url",'s.email'=>"Email",'s.default_lang'=>"DefaultLang",'s.siren'=>"ProfId1",'s.siret'=>"ProfId2",'s.ape'=>"ProfId3",'s.idprof4'=>"ProfId4",
@@ -280,7 +280,7 @@ class modSociete extends DolibarrModules
 		include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php';
 		$this->export_fields_array[$r]+=array('u.login'=>'SaleRepresentativeLogin','u.firstname'=>'SaleRepresentativeFirstname', 'u.lastname'=>'SaleRepresentativeLastname');
 		//$this->export_TypeFields_array[$r]=array(
-		//	's.rowid'=>"List:societe:nom",'s.nom'=>"Text",'s.status'=>"Text",'s.client'=>"Boolean",'s.fournisseur'=>"Boolean",'s.datec'=>"Date",'s.tms'=>"Date",
+		//	's.rowid'=>"List:societe:nom", 's.nom'=>"Text", 's.name_alias'=>"Text", 's.status'=>"Text",'s.client'=>"Boolean",'s.fournisseur'=>"Boolean",'s.datec'=>"Date",'s.tms'=>"Date",
 		//	's.code_client'=>"Text",'s.code_fournisseur'=>"Text",'s.address'=>"Text",'s.zip'=>"Text",'s.town'=>"Text",'c.label'=>"List:c_country:label:label",
 		//	'c.code'=>"Text",'s.phone'=>"Text",'s.fax'=>"Text",'s.url'=>"Text",'s.email'=>"Text",'s.default_lang'=>"Text",'s.siret'=>"Text",'s.siren'=>"Text",
 		//	's.ape'=>"Text",'s.idprof4'=>"Text",'s.idprof5'=>"Text",'s.idprof6'=>"Text",'s.tva_intra'=>"Text",'s.capital'=>"Numeric",'s.note'=>"Text",
@@ -288,7 +288,7 @@ class modSociete extends DolibarrModules
 		//	's.fk_stcomm'=>'List:c_stcomm:libelle:code','d.nom'=>'List:c_departements:nom:rowid'
 		//);
 		$this->export_TypeFields_array[$r]=array(
-			's.rowid'=>"Numeric", 's.nom'=>"Text",'s.status'=>"Numeric",'s.client'=>"Numeric",'s.fournisseur'=>"Boolean",'s.datec'=>"Date",'s.tms'=>"Date",
+			's.rowid'=>"Numeric", 's.nom'=>"Text", 's.name_alias'=>"Text", 's.status'=>"Numeric",'s.client'=>"Numeric",'s.fournisseur'=>"Boolean",'s.datec'=>"Date",'s.tms'=>"Date",
 			's.code_client'=>"Text",'s.code_fournisseur'=>"Text",'s.code_compta'=>"Text",'s.code_compta_fournisseur'=>"Text",'s.address'=>"Text",'s.zip'=>"Text",
 			's.town'=>"Text",'c.label'=>"List:c_country:label:label",'c.code'=>"Text",'s.phone'=>"Text",'s.fax'=>"Text",'s.url'=>"Text",'s.email'=>"Text",
 			's.default_lang'=>"Text",'s.siret'=>"Text",'s.siren'=>"Text",'s.ape'=>"Text",'s.idprof4'=>"Text",'s.idprof5'=>"Text",'s.idprof6'=>"Text",
@@ -386,7 +386,7 @@ class modSociete extends DolibarrModules
 		$this->import_entities_array[$r]=array();		// We define here only fields that use another icon that the one defined into import_icon
 		$this->import_tables_array[$r]=array('s'=>MAIN_DB_PREFIX.'societe','extra'=>MAIN_DB_PREFIX.'societe_extrafields');	// List of tables to insert into (insert done in same order)
 		$this->import_fields_array[$r]=array(
-			's.nom'=>"Name*",'s.status'=>"Status",'s.client'=>"Customer*",'s.fournisseur'=>"Supplier*",'s.code_client'=>"CustomerCode",'s.code_fournisseur'=>"SupplierCode",
+			's.nom'=>"Name*", 's.name_alias'=>"Alias", 's.status'=>"Status",'s.client'=>"Customer*",'s.fournisseur'=>"Supplier*",'s.code_client'=>"CustomerCode",'s.code_fournisseur'=>"SupplierCode",
 			's.code_compta'=>"CustomerAccountancyCode",'s.code_compta_fournisseur'=>"SupplierAccountancyCode",'s.address'=>"Address",'s.zip'=>"Zip",'s.town'=>"Town",
 			's.fk_departement'=>"StateId",'s.fk_pays'=>"CountryCode",'s.phone'=>"Phone",'s.fax'=>"Fax",'s.url'=>"Url",'s.email'=>"Email",'s.siren'=>"ProfId1",
 			's.siret'=>"ProfId2",'s.ape'=>"ProfId3",'s.idprof4'=>"ProfId4",'s.idprof5'=>"ProfId5",'s.idprof6'=>"ProfId6",'s.tva_intra'=>"VATIntraShort",
@@ -422,7 +422,7 @@ class modSociete extends DolibarrModules
 		//$this->import_convertvalue_array[$r]=array('s.fk_soc'=>array('rule'=>'lastrowid',table='t');
 		$this->import_regex_array[$r]=array('s.status'=>'^[0|1]','s.client'=>'^[0|1|2|3]','s.fournisseur'=>'^[0|1]','s.fk_typent'=>'id@'.MAIN_DB_PREFIX.'c_typent','s.datec'=>'^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]( [0-9][0-9]:[0-9][0-9]:[0-9][0-9])?$');
 		$this->import_examplevalues_array[$r]=array(
-			's.nom'=>"MyBigCompany",'s.status'=>"0 (closed) or 1 (active)",'s.client'=>'0 (no customer no prospect)/1 (customer)/2 (prospect)/3 (customer and prospect)',
+			's.nom'=>"MyBigCompany", 's.name_alias'=>"MyBigAlias", 's.status'=>"0 (closed) or 1 (active)",'s.client'=>'0 (no customer no prospect)/1 (customer)/2 (prospect)/3 (customer and prospect)',
 			's.fournisseur'=>'0 or 1','s.datec'=>dol_print_date(dol_now(),'%Y-%m-%d'),'s.code_client'=>'CU01-0001 or empty or "auto"','s.code_fournisseur'=>'SU01-0001 or empty or "auto"',
 			's.address'=>"61 jump street",'s.zip'=>"123456",'s.town'=>"Big town",'s.fk_pays'=>'US, FR, DE...','s.phone'=>"0101010101",'s.fax'=>"0101010102",
 			's.url'=>"http://mycompany.com",'s.email'=>"test@mycompany.com",'s.siret'=>"",'s.siren'=>"",'s.ape'=>"",'s.idprof4'=>"",'s.idprof5'=>"",'s.idprof6'=>"",

+ 2 - 2
htdocs/core/modules/modStripe.class.php

@@ -92,7 +92,7 @@ class modStripe extends DolibarrModules
 
         // Main menu entries
         $r=0;
-        $this->menu[$r]=array(
+       /* $this->menu[$r]=array(
         	'fk_menu'=>'fk_mainmenu=billing,fk_leftmenu=customers_bills_payment',		    // Use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode
 	        'mainmenu'=>'billing',
         	'leftmenu'=>'customers_bills_payment_stripe',
@@ -106,7 +106,7 @@ class modStripe extends DolibarrModules
 	        'target'=>'',
 	        'user'=>2
         );				                // 0=Menu for internal users, 1=external users, 2=both
-        $r++;
+        $r++;*/
 
         $this->menu[$r] = array(
         	'fk_menu'=>'fk_mainmenu=bank',

+ 1 - 1
htdocs/core/modules/product/doc/doc_generic_product_odt.modules.php

@@ -397,7 +397,7 @@ class doc_generic_product_odt extends ModelePDFProduct
 				$array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs);
 				$array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs);
 				$array_other=$this->get_substitutionarray_other($outputlangs);
-				// retrieve contact information for use in product as contact_xxx tags
+				// retrieve contact information for use in object as contact_xxx tags
 				$array_thirdparty_contact = array();
 				if ($usecontact && is_object($contactobject)) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact');
 

+ 1 - 1
htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php

@@ -617,7 +617,7 @@ class doc_generic_project_odt extends ModelePDFProjects
 				$array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs);
 				$array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs);
 				$array_other=$this->get_substitutionarray_other($outputlangs);
-				// retrieve contact information for use in project as contact_xxx tags
+				// retrieve contact information for use in object as contact_xxx tags
 				$array_project_contact = array();
 				if ($usecontact && is_object($contactobject)) $array_project_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact');
 

+ 1 - 1
htdocs/core/modules/propale/doc/doc_generic_proposal_odt.modules.php

@@ -410,7 +410,7 @@ class doc_generic_proposal_odt extends ModelePDFPropales
 				$array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs);
 				$array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs);
 				$array_other=$this->get_substitutionarray_other($outputlangs);
-				// retrieve contact information for use in proposal as contact_xxx tags
+				// retrieve contact information for use in object as contact_xxx tags
 				$array_thirdparty_contact = array();
 				if ($usecontact && is_object($contactobject)) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact');
 

+ 1 - 1
htdocs/core/modules/stock/doc/pdf_stdmovement.modules.php

@@ -247,7 +247,7 @@ class pdf_stdmovement extends ModelePDFMovement
 
 		// fetch optionals attributes and labels
 		$extralabels = $extrafields->fetch_name_optionals_label('movement');
-		$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_');
+		$search_array_options=$extrafields->getOptionalsFromPost('movement','','search_');
 
 		$productlot=new ProductLot($db);
 		$productstatic=new Product($db);

+ 3 - 4
htdocs/core/modules/user/doc/doc_generic_user_odt.modules.php

@@ -353,10 +353,9 @@ class doc_generic_user_odt extends ModelePDFUser
 				$array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs);
 				$array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs);
 				$array_other=$this->get_substitutionarray_other($outputlangs);
-		                // retrieve contact information for use in user as contact_xxx tags
-                		$array_thirdparty_contact = array();
-                		if ($usecontact)
-                    			$array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact');
+				// retrieve contact information for use in object as contact_xxx tags
+				$array_thirdparty_contact = array();
+				if ($usecontact && is_object($contactobject)) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact');
 
 				$tmparray = array_merge($array_user,$array_soc,$array_thirdparty,$array_other,$array_thirdparty_contact);
 				complete_substitutions_array($tmparray, $outputlangs, $object);

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels