diff --git a/htdocs/admin/mails_templates.php b/htdocs/admin/mails_templates.php
index b62009c2ba6..b5f22376c16 100644
--- a/htdocs/admin/mails_templates.php
+++ b/htdocs/admin/mails_templates.php
@@ -50,7 +50,7 @@ $confirm = GETPOST('confirm', 'alpha'); // Result of a confirmatio
$id = GETPOST('id', 'int');
$rowid = GETPOST('rowid', 'alpha');
-$search_label=GETPOST('search_label', 'alpha');
+$search_label=GETPOST('search_label', 'alphanohtml'); // Must allow value like 'Abc Def' or '(MyTemplateName)'
$search_type_template=GETPOST('search_type_template', 'alpha');
$search_lang=GETPOST('search_lang', 'alpha');
$search_fk_user=GETPOST('search_fk_user', 'intcomma');
@@ -263,6 +263,7 @@ if (empty($reshook))
{
//var_dump($i.' - '.$listfieldvalue[$i].' - '.$_POST[$listfieldvalue[$i]].' - '.$value);
$keycode=$listfieldvalue[$i];
+ if ($value == 'label') $_POST[$keycode] = dol_escape_htmltag($_POST[$keycode]);
if ($value == 'lang') $keycode='langcode';
if ($value == 'entity') $_POST[$keycode] = $conf->entity;
if ($i) $sql.=",";
@@ -667,8 +668,6 @@ if ($resql)
print '
';
foreach ($fieldlist as $field => $value)
{
- // Determine le nom du champ par rapport aux noms possibles
- // dans les dictionnaires de donnees
$showfield=1; // By defaut
$align="left";
$sortable=1;
@@ -695,7 +694,7 @@ if ($resql)
if ($fieldlist[$field]=='content') { $valuetoshow=$langs->trans("Content"); $showfield=0;}
if ($fieldlist[$field]=='content_lines') { $valuetoshow=$langs->trans("ContentLines"); $showfield=0; }
- // Affiche nom du champ
+ // Show fields
if ($showfield)
{
if (! empty($tabhelp[$id][$value]))
@@ -813,6 +812,10 @@ if ($resql)
$showfield=1;
$align="left";
$valuetoshow=$obj->{$fieldlist[$field]};
+ if ($value == 'label' || $value == 'topic')
+ {
+ $valuetoshow = dol_escape_htmltag($valuetoshow);
+ }
if ($value == 'type_template')
{
$valuetoshow = isset($elementList[$valuetoshow])?$elementList[$valuetoshow]:$valuetoshow;
diff --git a/htdocs/comm/action/class/actioncomm.class.php b/htdocs/comm/action/class/actioncomm.class.php
index 12926f9b360..3c138043d26 100644
--- a/htdocs/comm/action/class/actioncomm.class.php
+++ b/htdocs/comm/action/class/actioncomm.class.php
@@ -657,7 +657,7 @@ class ActionComm extends CommonObject
$this->socid = $obj->fk_soc; // To have fetch_thirdparty method working
$this->contactid = $obj->fk_contact; // To have fetch_contact method working
- $this->fk_project = $obj->fk_project; // To have fetch_project method working
+ $this->fk_project = $obj->fk_project; // To have fetch_projet method working
//$this->societe->id = $obj->fk_soc; // deprecated
//$this->contact->id = $obj->fk_contact; // deprecated
diff --git a/htdocs/core/actions_massactions.inc.php b/htdocs/core/actions_massactions.inc.php
index d54d2dd02ff..6cd98f00e48 100644
--- a/htdocs/core/actions_massactions.inc.php
+++ b/htdocs/core/actions_massactions.inc.php
@@ -384,6 +384,10 @@ if (! $error && $massaction == 'confirm_presend')
foreach ($looparray as $objectid => $objecttmp) // $objecttmp is a real object or an empty object if we choose to send one email per thirdparty instead of one per object
{
// Make substitution in email content
+ if (! empty($conf->projet->enabled) && method_exists($objecttmp, 'fetch_projet') && is_null($objecttmp->project))
+ {
+ $objecttmp->fetch_projet();
+ }
$substitutionarray=getCommonSubstitutionArray($langs, 0, null, $objecttmp);
$substitutionarray['__ID__'] = ($oneemailperrecipient ? join(', ', array_keys($listofqualifiedobj)) : $objecttmp->id);
$substitutionarray['__REF__'] = ($oneemailperrecipient ? join(', ', $listofqualifiedref) : $objecttmp->ref);
diff --git a/htdocs/core/actions_sendmails.inc.php b/htdocs/core/actions_sendmails.inc.php
index 836652ea819..de3a43d9ce6 100644
--- a/htdocs/core/actions_sendmails.inc.php
+++ b/htdocs/core/actions_sendmails.inc.php
@@ -113,8 +113,8 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO
{
$result=$object->fetch($id);
- $sendtosocid=0; // Thirdparty on object
- if (method_exists($object, "fetch_thirdparty") && ! in_array($object->element, array('societe','member','user','expensereport', 'contact')))
+ $sendtosocid=0; // Id of related thirdparty
+ if (method_exists($object, "fetch_thirdparty") && ! in_array($object->element, array('societe', 'member', 'user', 'expensereport', 'contact')))
{
$result=$object->fetch_thirdparty();
if ($object->element == 'user' && $result == 0) $result=1; // Even if not found, we consider ok
@@ -124,7 +124,14 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO
elseif ($object->element == 'member' || $object->element == 'user')
{
$thirdparty=$object;
- if ($thirdparty->id > 0) $sendtosocid=$thirdparty->id;
+ if ($object->socid > 0) $sendtosocid=$object->socid;
+ }
+ elseif ($object->element == 'expensereport')
+ {
+ $tmpuser=new User($db);
+ $tmpuser->fetch($object->fk_user_author);
+ $thirdparty=$tmpuser;
+ if ($object->socid > 0) $sendtosocid=$object->socid;
}
elseif ($object->element == 'societe')
{
@@ -136,7 +143,7 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO
$contact=$object;
if ($contact->id > 0) $sendtosocid=$contact->fetch_thirdparty()->id;
}
- else dol_print_error('', 'Use actions_sendmails.in.php for an element/object that is not supported');
+ else dol_print_error('', "Use actions_sendmails.in.php for an element/object '".$object->element."' that is not supported");
if (is_object($hookmanager))
{
@@ -162,6 +169,7 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO
if ($receiver == '-1') $receiver=array();
else $receiver=array($receiver);
}
+
$tmparray=array();
if (trim($_POST['sendto']))
{
@@ -173,22 +181,23 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO
foreach($receiver as $key=>$val)
{
// Recipient was provided from combo list
- if ($val == 'thirdparty') // Id of third party
+ if ($val == 'thirdparty') // Key selected means currentthird party (may be usd for current member or current user too)
{
- $tmparray[] = dol_string_nospecial($thirdparty->name, ' ', array(",")).' <'.$thirdparty->email.'>';
+ $tmparray[] = dol_string_nospecial($thirdparty->getFullName($langs), ' ', array(",")).' <'.$thirdparty->email.'>';
}
// Recipient was provided from combo list
- elseif ($val == 'contact') // Id of contact
+ elseif ($val == 'contact') // Key selected means current contact
{
- $tmparray[] = dol_string_nospecial($contact->name, ' ', array(",")).' <'.$contact->email.'>';
+ $tmparray[] = dol_string_nospecial($contact->getFullName($langs), ' ', array(",")).' <'.$contact->email.'>';
}
- elseif ($val) // Id du contact
+ elseif ($val) // $val is the Id of a contact
{
$tmparray[] = $thirdparty->contact_get_property((int) $val, 'email');
$sendtoid[] = $val;
}
}
}
+
if (!empty($conf->global->MAIN_MAIL_ENABLED_USER_DEST_SELECT))
{
$receiveruser=$_POST['receiveruser'];
@@ -222,16 +231,16 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO
foreach($receivercc as $key=>$val)
{
// Recipient was provided from combo list
- if ($val == 'thirdparty') // Id of third party
+ if ($val == 'thirdparty') // Key selected means currentthird party (may be usd for current member or current user too)
{
$tmparray[] = dol_string_nospecial($thirdparty->name, ' ', array(",")).' <'.$thirdparty->email.'>';
}
// Recipient was provided from combo list
- elseif ($val == 'contact') // Id of contact
+ elseif ($val == 'contact') // Key selected means current contact
{
$tmparray[] = dol_string_nospecial($contact->name, ' ', array(",")).' <'.$contact->email.'>';
}
- elseif ($val) // Id du contact
+ elseif ($val) // $val is the Id of a contact
{
$tmparray[] = $thirdparty->contact_get_property((int) $val, 'email');
//$sendtoid[] = $val; TODO Add also id of contact in CC ?
@@ -414,27 +423,6 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO
$result=$mailfile->sendfile();
if ($result)
{
- // Two hooks are available into method $mailfile->sendfile, so dedicated code is no more required
- /*
- if (! empty($conf->dolimail->enabled))
- {
- $mid = (GETPOST('mid','int') ? GETPOST('mid','int') : 0); // Original mail id is set ?
- if ($mid)
- {
- // set imap flag answered if it is an answered mail
- $dolimail=new DoliMail($db);
- $dolimail->id = $mid;
- $res=$dolimail->set_prop($user, 'answered',1);
- }
- if ($imap==1)
- {
- // write mail to IMAP Server
- $movemail = $mailboxconfig->putMail($subject,$sendto,$from,$message,$filepath,$mimetype,$filename,$sendtocc,$folder,$deliveryreceipt,$mailfile);
- if ($movemail) setEventMessages($langs->trans("MailMovedToImapFolder",$folder), null, 'mesgs');
- else setEventMessages($langs->trans("MailMovedToImapFolder_Warning",$folder), null, 'warnings');
- }
- }*/
-
// Initialisation of datas of object to call trigger
if (is_object($object))
{
diff --git a/htdocs/core/class/translate.class.php b/htdocs/core/class/translate.class.php
index fe00f803a6b..eca41a55301 100644
--- a/htdocs/core/class/translate.class.php
+++ b/htdocs/core/class/translate.class.php
@@ -88,11 +88,12 @@ class Translate
if (empty($srclang) || $srclang == 'auto')
{
+ // $_SERVER['HTTP_ACCEPT_LANGUAGE'] can be 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7,it;q=0.6' but can contains also malicious content
$langpref=empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])?'':$_SERVER['HTTP_ACCEPT_LANGUAGE'];
- $langpref=preg_replace("/;([^,]*)/i", "", $langpref);
+ $langpref=preg_replace("/;([^,]*)/i", "", $langpref); // Remove the 'q=x.y,' part
$langpref=str_replace("-", "_", $langpref);
$langlist=preg_split("/[;,]/", $langpref);
- $codetouse=$langlist[0];
+ $codetouse=preg_replace('/[^_a-zA-Z]/', '', $langlist[0]);
}
else $codetouse=$srclang;
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index 1130f136354..d234195d8cd 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -5897,11 +5897,13 @@ $substitutionarray=array_merge($substitutionarray, array(
$substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
$substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
- $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
- $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
- $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
- $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
-
+ if (! empty($conf->societe->enabled))
+ {
+ $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
+ $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
+ $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
+ $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
+ }
if (! empty($conf->adherent->enabled))
{
$substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
@@ -5909,15 +5911,19 @@ $substitutionarray=array_merge($substitutionarray, array(
$substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
$substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
}
- $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
- $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
- $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
-
- $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
- $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
- $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
- $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
-
+ if (! empty($conf->projet->enabled))
+ {
+ $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
+ $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
+ $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
+ }
+ if (! empty($conf->contrat->enabled))
+ {
+ $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
+ $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
+ $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
+ $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
+ }
$substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
$substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
$substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
@@ -5995,7 +6001,13 @@ $substitutionarray=array_merge($substitutionarray, array(
$substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty)?$object->thirdparty->email:'');
}
- if (is_object($object->projet) && $object->projet->id > 0)
+ if (is_object($object->project) && $object->project->id > 0)
+ {
+ $substitutionarray['__PROJECT_ID__'] = (is_object($object->project)?$object->project->id:'');
+ $substitutionarray['__PROJECT_REF__'] = (is_object($object->project)?$object->project->ref:'');
+ $substitutionarray['__PROJECT_NAME__'] = (is_object($object->project)?$object->project->title:'');
+ }
+ if (is_object($object->projet) && $object->projet->id > 0) // Deprecated, for backward compatibility
{
$substitutionarray['__PROJECT_ID__'] = (is_object($object->projet)?$object->projet->id:'');
$substitutionarray['__PROJECT_REF__'] = (is_object($object->projet)?$object->projet->ref:'');
diff --git a/htdocs/core/modules/modHoliday.class.php b/htdocs/core/modules/modHoliday.class.php
index ba792ad28ad..61217d91686 100644
--- a/htdocs/core/modules/modHoliday.class.php
+++ b/htdocs/core/modules/modHoliday.class.php
@@ -202,13 +202,13 @@ class modHoliday extends DolibarrModules
$this->export_permission[$r]=array(array("holiday","read_all"));
$this->export_fields_array[$r]=array(
'd.rowid'=>"LeaveId",'d.fk_type'=>'TypeOfLeaveId','t.code'=>'TypeOfLeaveCode','t.label'=>'TypeOfLeaveLabel','d.fk_user'=>'UserID',
- 'u.lastname'=>'Lastname','u.firstname'=>'Firstname','u.login'=>"Login",'d.date_debut'=>'DateStart','d.date_fin'=>'DateEnd','d.halfday'=>'HalfDay',
+ 'u.lastname'=>'Lastname','u.firstname'=>'Firstname','u.login'=>"Login",'d.date_debut'=>'DateStart','d.date_fin'=>'DateEnd','d.halfday'=>'HalfDay','none.num_open_days'=>'NbUseDaysCP',
'd.date_valid'=>'DateApprove','d.fk_validator'=>"UserForApprovalID",'ua.lastname'=>"UserForApprovalLastname",'ua.firstname'=>"UserForApprovalFirstname",
'ua.login'=>"UserForApprovalLogin",'d.description'=>'Description','d.statut'=>'Status'
);
$this->export_TypeFields_array[$r]=array(
'd.rowid'=>"Numeric",'t.code'=>'Text', 't.label'=>'Text','d.fk_user'=>'Numeric',
- 'u.lastname'=>'Text','u.firstname'=>'Text','u.login'=>"Text",'d.date_debut'=>'Date','d.date_fin'=>'Date',
+ 'u.lastname'=>'Text','u.firstname'=>'Text','u.login'=>"Text",'d.date_debut'=>'Date','d.date_fin'=>'Date','none.num_open_days'=>'NumericCompute',
'd.date_valid'=>'Date','d.fk_validator'=>"Numeric",'ua.lastname'=>"Text",'ua.firstname'=>"Text",
'ua.login'=>"Text",'d.description'=>'Text','d.statut'=>'Numeric'
);
@@ -216,6 +216,7 @@ class modHoliday extends DolibarrModules
'u.lastname'=>'user','u.firstname'=>'user','u.login'=>'user','ua.lastname'=>'user','ua.firstname'=>'user','ua.login'=>'user'
);
$this->export_alias_array[$r]=array('d.rowid'=>"idholiday");
+ $this->export_special_array[$r] = array('none.num_open_days'=>'getNumOpenDays');
$this->export_dependencies_array[$r]=array(); // To add unique key if we ask a field of a child to avoid the DISTINCT to discard them
$this->export_sql_start[$r]='SELECT DISTINCT ';
diff --git a/htdocs/document.php b/htdocs/document.php
index 8e86d6c3e6f..84fb0492554 100644
--- a/htdocs/document.php
+++ b/htdocs/document.php
@@ -161,6 +161,8 @@ if (! empty($conf->global->MAIN_DISABLE_FORCE_SAVEAS)) $attachment=false;
$type = 'application/octet-stream';
if (GETPOST('type', 'alpha')) $type=GETPOST('type', 'alpha');
else $type=dol_mimetype($original_file);
+// Security: Force to octet-stream if file is a dangerous file
+if (preg_match('/\.noexe$/i', $original_file)) $type = 'application/octet-stream';
// Security: Delete string ../ into $original_file
$original_file = str_replace("../", "/", $original_file);
@@ -217,7 +219,7 @@ if (! $accessallowed)
}
// Security:
-// On interdit les remontees de repertoire ainsi que les pipe dans les noms de fichiers.
+// We refuse directory transversal change and pipes in file names
if (preg_match('/\.\./', $fullpath_original_file) || preg_match('/[<>|]/', $fullpath_original_file))
{
dol_syslog("Refused to deliver file ".$fullpath_original_file);
@@ -229,6 +231,7 @@ if (preg_match('/\.\./', $fullpath_original_file) || preg_match('/[<>|]/', $full
clearstatcache();
$filename = basename($fullpath_original_file);
+$filename = preg_replace('/\.noexe$/i', '', $filename);
// Output file on browser
dol_syslog("document.php download $fullpath_original_file filename=$filename content-type=$type");
diff --git a/htdocs/exports/class/export.class.php b/htdocs/exports/class/export.class.php
index 51a16f717b2..da184f27574 100644
--- a/htdocs/exports/class/export.class.php
+++ b/htdocs/exports/class/export.class.php
@@ -540,7 +540,7 @@ class Export
public function build_file($user, $model, $datatoexport, $array_selected, $array_filterValue, $sqlquery = '')
{
// phpcs:enable
- global $conf,$langs;
+ global $conf,$langs,$mysoc;
$indice=0;
asort($array_selected);
@@ -634,6 +634,14 @@ class Export
$alias=str_replace(array('.', '-','(',')'), '_', $key);
if ($obj->$alias < 0) $obj->$alias='0';
}
+ // Operation GETNUMOPENDAYS (for Holiday module)
+ elseif ($this->array_export_special[$indice][$key]=='getNumOpenDays')
+ {
+ include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
+ //$alias=$this->array_export_alias[$indice][$key];
+ $alias=str_replace(array('.', '-','(',')'), '_', $key);
+ $obj->$alias=num_open_day(dol_stringtotime($obj->d_date_debut, 1), dol_stringtotime($obj->d_date_fin, 1), 0, 1, $obj->d_halfday, $mysoc->country_code);
+ }
// Operation INVOICEREMAINTOPAY
elseif ($this->array_export_special[$indice][$key]=='getRemainToPay')
{
diff --git a/htdocs/fourn/class/fournisseur.product.class.php b/htdocs/fourn/class/fournisseur.product.class.php
index 10233924d4a..febfda0a419 100644
--- a/htdocs/fourn/class/fournisseur.product.class.php
+++ b/htdocs/fourn/class/fournisseur.product.class.php
@@ -207,7 +207,7 @@ class ProductFournisseur extends Product
/**
* Modify the purchase price for a supplier
*
- * @param int $qty Min quantity for which price is valid
+ * @param float $qty Min quantity for which price is valid
* @param float $buyprice Purchase price for the quantity min
* @param User $user Object user user made changes
* @param string $price_base_type HT or TTC
@@ -230,7 +230,7 @@ class ProductFournisseur extends Product
* @param string $desc_fourn Custom description for product_fourn_price
* @param string $barcode Barcode
* @param int $fk_barcode_type Barcode type
- * @return int <0 if KO, >=0 if OK
+ * @return int <0 if KO, >=0 if OK
*/
public function update_buyprice($qty, $buyprice, $user, $price_base_type, $fourn, $availability, $ref_fourn, $tva_tx, $charges = 0, $remise_percent = 0, $remise = 0, $newnpr = 0, $delivery_time_days = 0, $supplier_reputation = '', $localtaxes_array = array(), $newdefaultvatcode = '', $multicurrency_buyprice = 0, $multicurrency_price_base_type = 'HT', $multicurrency_tx = 1, $multicurrency_code = '', $desc_fourn = '', $barcode = '', $fk_barcode_type = '')
{
@@ -272,7 +272,7 @@ class ProductFournisseur extends Product
$buyprice=price2num($buyprice, 'MU');
$charges=price2num($charges, 'MU');
- $qty=price2num($qty);
+ $qty=price2num($qty, 'MS');
$unitBuyPrice = price2num($buyprice/$qty, 'MU');
$error=0;
diff --git a/htdocs/holiday/card.php b/htdocs/holiday/card.php
index be441976f67..2f0b9be9954 100644
--- a/htdocs/holiday/card.php
+++ b/htdocs/holiday/card.php
@@ -1134,6 +1134,7 @@ else
if ($action == 'edit' && $object->statut == Holiday::STATUS_DRAFT) $edit = true;
print '