Merge branch 'develop' into develop

This commit is contained in:
Laurent Destailleur 2024-12-19 17:07:28 +01:00 committed by GitHub
commit 575c59f2e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 907 additions and 678 deletions

View File

@ -1546,8 +1546,8 @@ if ($action == 'create') {
$listofuserid[$firstelem['id']]['transparency'] = (GETPOSTISSET('transparency') ? GETPOST('transparency', 'alpha') : 0); // 0 by default when refreshing
}
}
print '<div class="assignedtouser">';
print $form->select_dolusers_forevent(($action == 'create' ? 'add' : 'update'), 'assignedtouser', 1, array(), 0, '', array(), 0, 0, 0, 'AND u.statut != 0', 1, $listofuserid, $listofcontactid, $listofotherid);
print '<!-- list of user to assign --><div class="assignedtouser">';
print $form->select_dolusers_forevent(($action == 'create' ? 'add' : 'update'), 'assignedtouser', 1, array(), 0, '', array(), 0, 0, 0, 'u.statut:<>:0', 1, $listofuserid, $listofcontactid, $listofotherid);
print '</div>';
print '</td></tr>';
@ -1811,7 +1811,7 @@ if ($action == 'create') {
print "\n".'<script type="text/javascript">';
print '$(document).ready(function () {
const reminderDefaultEventTypes = '.$reminderDefaultEventTypes.';
const reminderDefaultEventTypes = \''.dol_escape_js($reminderDefaultEventTypes).'\';
$("#actioncode").change(function(){
var selected_event_type = $("#actioncode option:selected").val();
@ -1820,9 +1820,9 @@ if ($action == 'create') {
$("#addreminder").prop("checked", true);
// Set period with default reminder period
$("[name=\"offsetvalue\"]").val("' . $reminderDefaultOffset . '");
$("[name=\"offsetvalue\"]").val(\'' . dol_escape_js($reminderDefaultOffset) . '\');
$("#select_offsetunittype_duration").select2("destroy");
$("#select_offsetunittype_duration").val("'.$reminderDefaultUnit.'");
$("#select_offsetunittype_duration").val(\''.dol_escape_js($reminderDefaultUnit).'\');
$("#select_offsetunittype_duration").select2();
$("#selectremindertype").select2("destroy");
@ -1832,7 +1832,7 @@ if ($action == 'create') {
// Set default reminder mail model
$("#select_actioncommsendmodel_mail").closest("tr").show();
$("#select_actioncommsendmodel_mail").select2("destroy");
$("#select_actioncommsendmodel_mail").val("'.$reminderDefaultEmailModel.'");
$("#select_actioncommsendmodel_mail").val(\''.dol_escape_js($reminderDefaultEmailModel).'\');
$("#select_actioncommsendmodel_mail").select2();
}
});
@ -1955,6 +1955,8 @@ if ($id > 0) {
}
if ($action == 'edit') {
$caneditdateorowner = ($object->type != 'systemauto');
if (!empty($conf->use_javascript_ajax)) {
print "\n".'<script type="text/javascript">';
print '$(document).ready(function () {
@ -1972,7 +1974,9 @@ if ($id > 0) {
$(".fulldayendmin").prop("disabled", true).val("59");
}
}
setdatefields();
'.($caneditdateorowner ? ' setdatefields();' : '').'
$("#fullday").change(function() {
setdatefields();
});
@ -2052,7 +2056,8 @@ if ($id > 0) {
print '<tr><td'.(!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? ' class="fieldrequired titlefieldcreate"' : '').'>'.$langs->trans("Title").'</td><td colspan="3"><input type="text" name="label" class="soixantepercent" value="'.$object->label.'"></td></tr>';
// Full day event
print '<tr><td><span class="fieldrequired">'.$langs->trans("Date").'</span></td><td colspan="3" class="valignmiddle height30 small"><input type="checkbox" id="fullday" name="fullday" '.($object->fulldayevent ? ' checked' : '').'>';
print '<tr><td><span class="fieldrequired">'.$langs->trans("Date").'</span></td><td colspan="3" class="valignmiddle height30 small">';
print '<input '.($caneditdateorowner ? '' : ' disabled').' type="checkbox" id="fullday" name="fullday" '.($object->fulldayevent ? ' checked' : '').'>';
print '<label for="fullday">'.$langs->trans("EventOnFullDay").'</label>';
// // Recurring event
@ -2123,9 +2128,9 @@ if ($id > 0) {
*/
print '</td><td td colspan="3">';
$tzforfullday = getDolGlobalString('MAIN_STORE_FULL_EVENT_IN_GMT');
print $form->selectDate($datep ? $datep : $object->datep, 'ap', 1, 1, 0, "action", 1, 2, 0, 'fulldaystart', '', '', '', 1, '', '', $object->fulldayevent ? ($tzforfullday ? $tzforfullday : 'tzuserrel') : 'tzuserrel');
print $form->selectDate($datep ? $datep : $object->datep, 'ap', 1, 1, 0, "action", 1, 2, ($caneditdateorowner ? 0 : 1), 'fulldaystart', '', '', '', 1, '', '', $object->fulldayevent ? ($tzforfullday ? $tzforfullday : 'tzuserrel') : 'tzuserrel');
print ' <span class="hideonsmartphone">&nbsp; &nbsp; - &nbsp; &nbsp;</span> ';
print $form->selectDate($datef ? $datef : $object->datef, 'p2', 1, 1, 1, "action", 1, 2, 0, 'fulldayend', '', '', '', 1, '', '', $object->fulldayevent ? ($tzforfullday ? $tzforfullday : 'tzuserrel') : 'tzuserrel');
print $form->selectDate($datef ? $datef : $object->datef, 'p2', 1, 1, 1, "action", 1, 2, ($caneditdateorowner ? 0 : 1), 'fulldayend', '', '', '', 1, '', '', $object->fulldayevent ? ($tzforfullday ? $tzforfullday : 'tzuserrel') : 'tzuserrel');
print '</td></tr>';
print '<tr><td class="">&nbsp;</td><td></td></tr>';
@ -2164,7 +2169,7 @@ if ($id > 0) {
print '<tr><td class="tdtop nowrap fieldrequired">'.$langs->trans("ActionAssignedTo").'</td><td colspan="3">';
print '<div class="assignedtouser">';
print $form->select_dolusers_forevent(($action == 'create' ? 'add' : 'update'), 'assignedtouser', 1, array(), 0, '', array(), 0, 0, 0, 'AND u.statut != 0', 1, $listofuserid, $listofcontactid, $listofotherid);
print $form->select_dolusers_forevent(($action == 'create' ? 'add' : 'update'), 'assignedtouser', 1, array(), 0, '', array(), 0, 0, 0, 'u.statut:<>:0', 1, $listofuserid, $listofcontactid, $listofotherid, $caneditdateorowner);
print '</div>';
/*if (in_array($user->id,array_keys($listofuserid)))
{
@ -2414,7 +2419,7 @@ if ($id > 0) {
print "\n".'<script type="text/javascript">';
print '$(document).ready(function () {
const reminderDefaultEventTypes = '.$reminderDefaultEventTypes.';
const reminderDefaultEventTypes = \''.dol_escape_js($reminderDefaultEventTypes).'\';
$("#actioncode").change(function(){
var selected_event_type = $("#actioncode option:selected").val();
@ -2423,9 +2428,9 @@ if ($id > 0) {
$("#addreminder").prop("checked", true);
// Set period with default reminder period
$("#offsetvalue").val('.$reminderDefaultOffset.');
$("#offsetvalue").val(\''.dol_escape_js($reminderDefaultOffset).'\');
$("#select_offsetunittype_duration").select2("destroy");
$("#select_offsetunittype_duration").val("'.$reminderDefaultUnit.'");
$("#select_offsetunittype_duration").val(\''.dol_escape_js($reminderDefaultUnit).'\');
$("#select_offsetunittype_duration").select2();
$("#selectremindertype").select2("destroy");
@ -2435,7 +2440,7 @@ if ($id > 0) {
// Set default reminder mail model
$("#select_actioncommsendmodel_mail").closest("tr").show();
$("#select_actioncommsendmodel_mail").select2("destroy");
$("#select_actioncommsendmodel_mail").val("'.$reminderDefaultEmailModel.'");
$("#select_actioncommsendmodel_mail").val(\''.dol_escape_js($reminderDefaultEmailModel).'\');
$("#select_actioncommsendmodel_mail").select2();
}
});

View File

@ -569,12 +569,12 @@ $viewmode .= '<span class="marginrightonly"></span>'; // To add a space before t
$newparam = '';
$newcardbutton = '';
if ($user->hasRight('agenda', 'myactions', 'create') || $user->hasRight('agenda', 'allactions', 'create')) {
$tmpforcreatebutton = dol_getdate(dol_now(), true);
$tmpforcreatebutton = dol_getdate(dol_now('tzuserrel'), true);
$newparam .= '&month='.((int) $month).'&year='.((int) $tmpforcreatebutton['year']).'&mode='.urlencode($mode);
//$param='month='.$monthshown.'&year='.$year;
$hourminsec = dol_print_date(dol_mktime(10, 0, 0, 1, 1, 1970, 'gmt'), '%H', 'gmt').'0000'; // Set $hourminsec to '100000' to auto set hour to 10:00 at creation
//$hourminsec = dol_print_date(dol_mktime(10, 0, 0, 1, 1, 1970, 'gmt'), '%H', 'gmt').'0000'; // Set $hourminsec to '100000' to auto set hour to 10:00 at creation
$urltocreateaction = DOL_URL_ROOT.'/comm/action/card.php?action=create';
$urltocreateaction .= '&apyear='.$tmpforcreatebutton['year'].'&apmonth='.$tmpforcreatebutton['mon'].'&apday='.$tmpforcreatebutton['mday'].'&aphour='.$tmpforcreatebutton['hours'].'&apmin='.$tmpforcreatebutton['minutes'];

View File

@ -736,13 +736,13 @@ $viewmode .= '</div>';
$viewmode .= '<span class="marginrightonly"></span>';
$tmpforcreatebutton = dol_getdate(dol_now(), true);
$tmpforcreatebutton = dol_getdate(dol_now('tzuserrel'), true);
$newparam = '&month='.str_pad((string) $month, 2, "0", STR_PAD_LEFT).'&year='.$tmpforcreatebutton['year'];
$url = DOL_URL_ROOT.'/comm/action/card.php?action=create';
$url .= '&apyear='.$tmpforcreatebutton['year'].'&apmonth='.$tmpforcreatebutton['mon'].'&apday='.$tmpforcreatebutton['mday'].'&aphour='.$tmpforcreatebutton['hours'].'&apmin='.$tmpforcreatebutton['minutes'];
$url .= '&apyear='.$tmpforcreatebutton['year'].'&apmonth='.$tmpforcreatebutton['mon'].'&apday='.$tmpforcreatebutton['mday'];
$url .= '&aphour='.$tmpforcreatebutton['hours'].'&apmin='.$tmpforcreatebutton['minutes'];
$url .= '&backtopage='.urlencode($_SERVER["PHP_SELF"].($newparam ? '?'.$newparam : ''));
$newcardbutton = dolGetButtonTitle($langs->trans('AddAction'), '', 'fa fa-plus-circle', $url, '', (int) ($user->hasRight('agenda', 'myactions', 'create') || $user->hasRight('agenda', 'allactions', 'create')));

View File

@ -487,7 +487,7 @@ $viewmode .= '<span class="marginrightonly"></span>';
$newparam = '';
$newcardbutton = '';
if ($user->hasRight('agenda', 'myactions', 'create') || $user->hasRight('agenda', 'allactions', 'create')) {
$tmpforcreatebutton = dol_getdate(dol_now(), true);
$tmpforcreatebutton = dol_getdate(dol_now('tzuserrel'), true);
$newparam .= '&month='.str_pad($month, 2, "0", STR_PAD_LEFT).'&year='.$tmpforcreatebutton['year'];

View File

@ -492,7 +492,7 @@ $viewmode .= '<span class="marginrightonly"></span>';
$newparam = '';
$newcardbutton = '';
if ($user->hasRight('agenda', 'myactions', 'create') || $user->hasRight('agenda', 'allactions', 'create')) {
$tmpforcreatebutton = dol_getdate(dol_now(), true);
$tmpforcreatebutton = dol_getdate(dol_now('tzuserrel'), true);
$newparam .= '&month='.urlencode(str_pad((string) $month, 2, "0", STR_PAD_LEFT)).'&year='.((int) $tmpforcreatebutton['year']);
if ($begin_h !== '') {

View File

@ -1712,35 +1712,37 @@ if ($object->id > 0) {
}
// Add invoice
if ($user->socid == 0) {
if (isModEnabled('deplacement') && $object->status == 1) {
$langs->load("trips");
print '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/compta/deplacement/card.php?socid='.$object->id.'&amp;action=create">'.$langs->trans("AddTrip").'</a></div>';
}
if (isModEnabled('deplacement') && $object->status == 1) {
$langs->load("trips");
print '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/compta/deplacement/card.php?socid='.$object->id.'&amp;action=create">'.$langs->trans("AddTrip").'</a></div>';
}
if (isModEnabled('invoice') && $object->status == 1) {
if (!$user->hasRight('facture', 'creer')) {
$langs->load("bills");
print '<div class="inline-block divButAction"><a class="butActionRefused classfortooltip" title="'.dol_escape_js($langs->trans("NotAllowed")).'" href="#">'.$langs->trans("AddBill").'</a></div>';
if (isModEnabled('invoice') && $object->status == 1) {
if (!$user->hasRight('facture', 'creer')) {
$langs->load("bills");
print '<div class="inline-block divButAction"><a class="butActionRefused classfortooltip" title="'.dol_escape_js($langs->trans("NotAllowed")).'" href="#">'.$langs->trans("AddBill").'</a></div>';
} else {
$langs->loadLangs(array("orders", "bills"));
if ($object->client != 0 && $object->client != 2) {
print '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture/card.php?action=create&socid='.$object->id.'">'.$langs->trans("AddBill").'</a></div>';
} else {
$langs->loadLangs(array("orders", "bills"));
if (isModEnabled('order')) {
if ($object->client != 0 && $object->client != 2) {
if (!empty($orders2invoice) && $orders2invoice > 0) {
print '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/commande/list.php?socid='.$object->id.'&search_billed=0&autoselectall=1">'.$langs->trans("CreateInvoiceForThisCustomer").'</a></div>';
} else {
print '<div class="inline-block divButAction"><a class="butActionRefused classfortooltip" title="'.dol_escape_js($langs->trans("NoOrdersToInvoice")).'" href="#">'.$langs->trans("CreateInvoiceForThisCustomer").'</a></div>';
}
} else {
print '<div class="inline-block divButAction"><a class="butActionRefused classfortooltip" title="'.dol_escape_js($langs->trans("ThirdPartyMustBeEditAsCustomer")).'" href="#">'.$langs->trans("CreateInvoiceForThisCustomer").'</a></div>';
}
}
print '<div class="inline-block divButAction"><a class="butActionRefused classfortooltip" title="'.dol_escape_js($langs->trans("ThirdPartyMustBeEditAsCustomer")).'" href="#">'.$langs->trans("AddBill").'</a></div>';
}
}
}
if (isModEnabled('invoice') && $object->status == 1) {
if ($user->hasRight('facture', 'creer')) {
if (isModEnabled('order')) {
if ($object->client != 0 && $object->client != 2) {
print '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture/card.php?action=create&socid='.$object->id.'">'.$langs->trans("AddBill").'</a></div>';
if (!empty($orders2invoice) && $orders2invoice > 0) {
print '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/commande/list.php?socid='.$object->id.'&search_billed=0&autoselectall=1">'.$langs->trans("CreateInvoiceForThisCustomer").'</a></div>';
} else {
print '<div class="inline-block divButAction"><a class="butActionRefused classfortooltip" title="'.dol_escape_js($langs->trans("NoOrdersToInvoice")).'" href="#">'.$langs->trans("CreateInvoiceForThisCustomer").'</a></div>';
}
} else {
print '<div class="inline-block divButAction"><a class="butActionRefused classfortooltip" title="'.dol_escape_js($langs->trans("ThirdPartyMustBeEditAsCustomer")).'" href="#">'.$langs->trans("AddBill").'</a></div>';
print '<div class="inline-block divButAction"><a class="butActionRefused classfortooltip" title="'.dol_escape_js($langs->trans("ThirdPartyMustBeEditAsCustomer")).'" href="#">'.$langs->trans("CreateInvoiceForThisCustomer").'</a></div>';
}
}
}

View File

@ -1149,15 +1149,16 @@ class Form
* Return list of types of lines (product or service)
* Example: 0=product, 1=service, 9=other (for external module)
*
* @param string $selected Preselected type
* @param string $htmlname Name of field in html form
* @param int<0,1>|string $showempty Add an empty field
* @param int $hidetext Do not show label 'Type' before combo box (used only if there is at least 2 choices to select)
* @param integer $forceall 1=Force to show products and services in combo list, whatever are activated modules, 0=No force, 2=Force to show only Products, 3=Force to show only services, -1=Force none (and set hidden field to 'service')
* @param string $morecss More css
* @param string $selected Preselected type
* @param string $htmlname Name of field in html form
* @param int<0,1>|string $showempty Add an empty field
* @param int $hidetext Do not show label 'Type' before combo box (used only if there is at least 2 choices to select)
* @param integer $forceall 1=Force to show products and services in combo list, whatever are activated modules, 0=No force, 2=Force to show only Products, 3=Force to show only services, -1=Force none (and set hidden field to 'service')
* @param string $morecss More css
* @param int $useajaxcombo 1=Use ajaxcombo
* @return void
*/
public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0, $morecss = "")
public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0, $morecss = "", $useajaxcombo = 1)
{
// phpcs:enable
global $langs;
@ -1166,11 +1167,11 @@ class Form
if ($forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
|| (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))) {
if (empty($hidetext)) {
print $langs->trans("Type") . ': ';
print $langs->trans("Type").'...';
}
print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $htmlname . '" name="' . $htmlname . '">';
if ($showempty) {
print '<option value="-1"';
print '<option value="-1" class="opacitymedium"'.($useajaxcombo ? '' : ' disabled="disabled"');
if ($selected == -1) {
print ' selected';
}
@ -1196,7 +1197,10 @@ class Form
print '>' . $langs->trans("Service");
print '</select>';
print ajax_combobox('select_' . $htmlname);
if ($useajaxcombo) {
print ajax_combobox('select_' . $htmlname);
}
//if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
}
if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
@ -2228,7 +2232,13 @@ class Form
$sql .= " AND u.fk_soc IS NULL";
}
if (!empty($morefilter)) {
$sql .= " " . $morefilter;
$errormessage = '';
$sql .= forgeSQLFromUniversalSearchCriteria($morefilter, $errormessage);
if ($errormessage) {
$this->errors[] = $errormessage;
dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
return -1;
}
}
//Add hook to filter on user (for example on usergroup define in custom modules)
@ -2425,28 +2435,29 @@ class Form
* Return select list of users. Selected users are stored into session.
* List of users are provided into $_SESSION['assignedtouser'].
*
* @param string $action Value for $action
* @param string $htmlname Field name in form
* @param int<0,1> $show_empty 0=list without the empty value, 1=add empty value
* @param int[] $exclude Array list of users id to exclude
* @param int<0,1> $disabled If select list must be disabled
* @param int[]|string $include Array list of users id to include or 'hierarchy' to have only supervised users
* @param int[]|int $enableonly Array list of users id to be enabled. All other must be disabled
* @param string $force_entity '0' or Ids of environment to force
* @param int $maxlength Maximum length of string into list (0=no limit)
* @param int<0,1> $showstatus 0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
* @param string $morefilter Add more filters into sql request
* @param int $showproperties Show properties of each attendees
* @param int[] $listofuserid Array with properties of each user
* @param int[] $listofcontactid Array with properties of each contact
* @param int[] $listofotherid Array with properties of each other contact
* @return string HTML select string
* @param string $action Value for $action
* @param string $htmlname Field name in form
* @param int<0,1> $show_empty 0=list without the empty value, 1=add empty value
* @param int[] $exclude Array list of users id to exclude
* @param int<0,1> $disabled If select list must be disabled
* @param int[]|''|'hierarchy'|'hierarchyme' $include Array list of users id to include or 'hierarchy' to have only supervised users
* @param int[]|int $enableonly Array list of users id to be enabled. All other must be disabled
* @param string $force_entity '0' or Ids of environment to force
* @param int $maxlength Maximum length of string into list (0=no limit)
* @param int<0,1> $showstatus 0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status
* @param string $morefilter Add more filters into sql request (Example: '(employee:=:1)'). This value must not come from user input.
* @param int $showproperties Show properties of each attendees
* @param int[] $listofuserid Array with properties of each user
* @param int[] $listofcontactid Array with properties of each contact
* @param int[] $listofotherid Array with properties of each other contact
* @param int $canremoveowner 1 if we can remove owner, 0=no way
* @return string HTML select string
* @see select_dolgroups()
*/
public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array())
public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array(), $canremoveowner = 1)
{
// phpcs:enable
global $langs;
global $langs, $user;
$userstatic = new User($this->db);
$out = '';
@ -2479,8 +2490,25 @@ class Form
$ownerid = $value['id'];
$out .= ' (' . $langs->trans("Owner") . ')';
}
// Add picto to delete owner/assignee
if ($nbassignetouser > 1 && $action != 'view') {
$out .= ' <input type="image" style="border: 0px;" src="' . img_picto($langs->trans("Remove"), 'delete', '', 0, 1) . '" value="' . $userstatic->id . '" class="removedassigned reposition" id="removedassigned_' . $userstatic->id . '" name="removedassigned_' . $userstatic->id . '">';
$canremoveassignee = 1;
if ($i == 0) {
// We are on the owner of the event
if (!$canremoveowner) {
$canremoveassignee = 0;
}
if (!$user->hasRight('agenda', 'allactions', 'create')) {
$canremoveassignee = 0; // Can't remove the owner
}
} else {
// We are not on the owner of the event but on a secondary assignee
}
if ($canremoveassignee) {
// If user has all permission, he should be ableto remove a assignee.
// If user has not all permission, he can onlyremove assignee of other (he can't remove itself)
$out .= ' <input type="image" style="border: 0px;" src="' . img_picto($langs->trans("Remove"), 'delete', '', 0, 1) . '" value="' . $userstatic->id . '" class="removedassigned reposition" id="removedassigned_' . $userstatic->id . '" name="removedassigned_' . $userstatic->id . '">';
}
}
// Show my availability
if ($showproperties) {
@ -7404,6 +7432,7 @@ class Form
$hourend = $hourstart;
}
}
// Show hour
$retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50 ' . ($fullday ? $fullday . 'hour' : '') . '" id="' . $prefix . 'hour" name="' . $prefix . 'hour">';
if ($emptyhours) {
@ -7426,7 +7455,7 @@ class Form
if ($m) {
// Show minutes
$retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50 ' . ($fullday ? $fullday . 'min' : '') . '" id="' . $prefix . 'min" name="' . $prefix . 'min">';
$retstring .= '<select ' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50 ' . ($fullday ? $fullday . 'min' : '') . '" id="' . $prefix . 'min" name="' . $prefix . 'min">';
if ($emptyhours) {
$retstring .= '<option value="-1">&nbsp;</option>';
}
@ -7444,7 +7473,7 @@ class Form
}
// Add a "Now" link
if (!empty($conf->use_javascript_ajax) && $addnowlink) {
if (!empty($conf->use_javascript_ajax) && $addnowlink && !$disabled) {
// Script which will be inserted in the onClick of the "Now" link
$reset_scripts = "";
if ($addnowlink == 2) { // local computer time
@ -7531,7 +7560,7 @@ class Form
}
// Add a "Plus one hour" link
if ($conf->use_javascript_ajax && $addplusone) {
if ($conf->use_javascript_ajax && $addplusone && !$disabled) {
// Script which will be inserted in the onClick of the "Add plusone" link
$reset_scripts = "";
@ -7569,7 +7598,7 @@ class Form
}
// Add a link to set data
if ($conf->use_javascript_ajax && !empty($adddateof)) {
if ($conf->use_javascript_ajax && !empty($adddateof) && !$disabled) {
if (!is_array($adddateof)) {
$arrayofdateof = array(array('adddateof' => $adddateof, 'labeladddateof' => $labeladddateof));
} else {

View File

@ -1557,11 +1557,13 @@ if (!getDolGlobalString('MAIN_DISABLE_SELECT2_FOCUS_PROTECTION') && !defined('DI
* TODO: Recheck with the select2 GH issue and remove once this is fixed on their side
*/
$(document).on('select2:open', (e) => {
console.log("Execute the focus (click on combo or use space when on component");
console.log("Execute the focus (click on combo or use space when on component)");
const target = $(e.target);
if (target && target.length) {
let id = target[0].id || target[0].name;
if (id.substr(-2) == "[]") id = id.substr(0,id.length-2);
if (id.substr(-2) == "[]") {
id = id.substr(0,id.length-2);
}
document.querySelector('input[aria-controls*='+id+']').focus();
}
});

View File

@ -10586,6 +10586,7 @@ function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1'
while ($scheck && $savescheck != $scheck) {
$savescheck = $scheck;
$scheck = preg_replace('/->[a-zA-Z0-9_]+\(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
$scheck = preg_replace('/::[a-zA-Z0-9_]+\(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
$scheck = preg_replace('/^\(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
$scheck = preg_replace('/\s\(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in 'if ($a == 1)'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
$scheck = preg_replace('/^!?[a-zA-Z0-9_]+\(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
@ -10646,22 +10647,26 @@ function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1'
$forbiddenphpstrings = array('$$', '$_', '}[');
$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
// We list all forbidden function as keywords we don't want to see (we don't mind it if is "kewyord(" or just "keyword", we don't want "keyword" at all)
$forbiddenphpfunctions = array();
// @phpcs:ignore
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64"."_"."decode", "rawurl"."decode", "url"."decode", "str"."_rot13", "hex"."2bin")); // name of forbidden functions are split to avoid false positive
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_compress_dir", "dol_decode", "dol_delete_file", "dol_delete_dir", "dol_delete_dir_recursive", "dol_copy", "archiveOrBackupFile")); // more dolibarr functions
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
$forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
$forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
$forbiddenphpregex = 'global\s*\$';
$forbiddenphpregex .= '|';
$forbiddenphpregex .= '\b('.implode('|', $forbiddenphpfunctions).')\b';
$forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';

View File

@ -694,8 +694,8 @@ function showWebsiteTemplates(Website $website)
/**
* Check a new string containing only php code (including <php tag)
* - Block if bad code in the new string.
* - Block also if user has no permission to change PHP code.
* - Block if user has no permission to change PHP code.
* - Block also if bad code found in the new string.
*
* @param string $phpfullcodestringold PHP old string (before the change). For example "<?php echo 'a' ?><php echo 'b' ?>"
* @param string $phpfullcodestring PHP new string. For example "<?php echo 'a' ?><php echo 'c' ?>"
@ -722,13 +722,15 @@ function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
// Then check forbidden commands
if (!$error) {
$forbiddenphpstrings = array('$$', '}[');
$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('ReflectionFunction'));
$forbiddenphpstrings = array('$$', '$_', '}[');
//$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', 'ReflectionFunction'));
$forbiddenphpfunctions = array();
//$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64"."_"."decode", "rawurl"."decode", "url"."decode", "str"."_rot13", "hex"."2bin")); // name of forbidden functions are split to avoid false positive
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("call_user_func"));
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("call_user_func", "call_user_func_array"));
//$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
if (!getDolGlobalString('WEBSITE_PHP_ALLOW_EXEC')) { // If option is not on, we disallow functions to execute commands
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
@ -736,27 +738,38 @@ function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
}
if (!getDolGlobalString('WEBSITE_PHP_ALLOW_WRITE')) { // If option is not on, we disallow functions to write files
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_compress_dir", "dol_decode", "dol_delete_file", "dol_delete_dir", "dol_delete_dir_recursive", "dol_copy", "archiveOrBackupFile")); // more dolibarr functions
$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
}
//$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
$forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
foreach ($forbiddenphpstrings as $forbiddenphpstring) {
if (preg_match('/'.preg_quote($forbiddenphpstring, '/').'/ms', $phpfullcodestring)) {
if (preg_match('/'.preg_quote($forbiddenphpstring, '/').'/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpstring), null, 'errors');
break;
}
}
foreach ($forbiddenphpfunctions as $forbiddenphpcommand) {
if (preg_match('/'.$forbiddenphpcommand.'\s*\(/ms', $phpfullcodestring)) {
/* replaced with next block
foreach ($forbiddenphpfunctions as $forbiddenphpfunction) { // Check "function(" but also "'function'(" and "function ("
if (preg_match('/'.$forbiddenphpfunction.'[\'\s]*\(/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpcommand), null, 'errors');
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction), null, 'errors');
break;
}
}*/
foreach ($forbiddenphpfunctions as $forbiddenphpfunction) { // Check "function" whatever is "function(" or "function'(" or "function (" or "function"
if (preg_match('/\b'.$forbiddenphpfunction.'\b/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction), null, 'errors');
break;
}
}
foreach ($forbiddenphpmethods as $forbiddenphpmethod) {
if (preg_match('/->'.$forbiddenphpmethod.'/ms', $phpfullcodestring)) {
if (preg_match('/->'.$forbiddenphpmethod.'/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpmethod), null, 'errors');
break;
@ -764,14 +777,14 @@ function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
}
}
// This char can be used to execute RCE for example using with echo `ls`
// This char can be used to execute RCE for example by using echo `ls`
if (!$error) {
$forbiddenphpchars = array();
if (!getDolGlobalString('WEBSITE_PHP_ALLOW_DANGEROUS_CHARS')) { // If option is not on, we disallow functions to execute commands
$forbiddenphpchars = array("`");
}
foreach ($forbiddenphpchars as $forbiddenphpchar) {
if (preg_match('/'.$forbiddenphpchar.'/ms', $phpfullcodestring)) {
if (preg_match('/'.$forbiddenphpchar.'/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpchar), null, 'errors');
break;
@ -779,23 +792,31 @@ function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
}
}
// Deny dynamic functions '${a}(' or '$a[b](' => So we refuse '}(' and ']('
// Deny code to call a function obfuscated with comment, like "exec/*...*/ ('ls')";
if (!$error) {
if (preg_match('/[}\]]\(/ims', $phpfullcodestring)) {
if (preg_match('/\*\/\s*\(/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", "exec/*...*/ ('ls')"), null, 'errors');
}
}
// Deny dynamic functions '${a}(' or '$a[b](' => So we refuse '}(' and ']('
if (!$error) {
if (preg_match('/[}\]]\s*\(/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", ']('), null, 'errors');
}
}
// Deny dynamic functions '$xxx('
// Deny dynamic functions '$xxx(' or '$xxx (' or '$xxx" ('
if (!$error) {
if (preg_match('/\$[a-z0-9_\-\/\*]+\(/ims', $phpfullcodestring)) {
if (preg_match('/\$[a-z0-9_\-\/\*\"]+\s*\(/ims', $phpfullcodestring)) {
$error++;
setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", '$...('), null, 'errors');
}
}
// No need to block $conf->global->aaa() because PHP try to run method aaa an not function into $conf->global->aaa.
// No need to block $conf->global->aaa() because PHP try to run the method aaa of $conf->global and not the function into $conf->global->aaa.
// Then check if installmodules does not block dynamic PHP code change.
if ($phpfullcodestringold != $phpfullcodestring) {

File diff suppressed because it is too large Load Diff

View File

@ -375,7 +375,7 @@ if ($step == 1 || !$datatoimport) {
$label = $objimport->array_import_label[$key];
print '<div class="twolinesmax-normallineheight minwidth200onall">';
print img_object($objimport->array_import_module[$key]['module']->getName(), $entityicon, 'class="pictofixedwidth"');
print dolPrintHtml($label);
print dolPrintHTML($label);
print '</div>';
print '</td><td style="text-align: right">';
if ($objimport->array_import_perms[$key]) {

View File

@ -798,7 +798,7 @@ Notes=Notes
AddNewLine=Add new line
AddFile=Add file
FreeZone=Free-text product
FreeLineOfType=Free-text item, type:
FreeLineOfType=Free-text item, type
CloneMainAttributes=Clone object with its main attributes
ReGeneratePDF=Re-generate PDF
PDFMerge=PDF Merge

View File

@ -1960,7 +1960,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($canvasdisplayactio
print '<td>'.$form->editfieldkey('AllocateCommercial', 'commercial_id', '', $object, 0).'</td>';
print '<td colspan="3" class="maxwidthonsmartphone">';
// TODO Use select_doluser in multiselect mode
$userlist = $form->select_dolusers($selected, '', 0, null, 0, '', '', '0', 0, 0, 'AND u.statut = 1', 0, '', '', 0, 2);
$userlist = $form->select_dolusers($selected, '', 0, null, 0, '', '', '0', 0, 0, 'u.statut:=:1', 0, '', '', 0, 2);
// Note: If user has no right to "see all thirdparties", we force selection of sale representative to him, so after creation he can see the record.
$selected = (GETPOSTISARRAY('commercial') ? GETPOST('commercial', 'array:int') : (GETPOSTINT('commercial') > 0 ? array(GETPOSTINT('commercial')) : array($user->id)));
print img_picto('', 'user').$form->multiselectarray('commercial', $userlist, $selected, 0, 0, 'quatrevingtpercent widthcentpercentminusx', 0, 0);
@ -2812,7 +2812,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($canvasdisplayactio
print '<tr>';
print '<td>'.$form->editfieldkey('AllocateCommercial', 'commercial_id', '', $object, 0).'</td>';
print '<td colspan="3" class="maxwidthonsmartphone">';
$userlist = $form->select_dolusers('', '', 0, null, 0, '', '', 0, 0, 0, 'AND u.statut = 1', 0, '', '', 0, 1);
$userlist = $form->select_dolusers('', '', 0, null, 0, '', '', 0, 0, 0, 'u.statut:=:1', 0, '', '', 0, 1);
$arrayselected = GETPOST('commercial', 'array');
if (empty($arrayselected)) {
$arrayselected = $object->getSalesRepresentatives($user, 1);

View File

@ -1290,7 +1290,7 @@ if (empty($type) || $type == 'f') {
}
// If the user can view prospects other than his'
$userlist = $form->select_dolusers('', '', 0, null, 0, '', '', 0, 0, 0, 'AND u.statut = 1', 0, '', '', 0, 1);
$userlist = $form->select_dolusers('', '', 0, null, 0, '', '', 0, 0, 0, 'u.statut:=:1', 0, '', '', 0, 1);
$userlist[-2] = $langs->trans("NoSalesRepresentativeAffected");
if ($user->hasRight("societe", "client", "voir") || $socid) {
$moreforfilter .= '<div class="divsearchfield">';

View File

@ -748,8 +748,12 @@ input:-webkit-autofill {
/* CSS for placeholder */
.placeholder { color: #ccc; }
select.placeholder { color: #ccc; }
::-webkit-input-placeholder { color: #ccc; }
input:-moz-placeholder { color: #ccc; }
select.placeholder option:not(.opacitymediumbycolor):not(.opacitymedium) {
color: var(--colortext);
}
input[name=price], input[name=weight], input[name=volume], input[name=surface], input[name=sizeheight], input[name=net_measure], select[name=incoterm_id] { margin-right: 6px; }
fieldset {
@ -766,6 +770,7 @@ input#onlinepaymenturl, input#directdownloadlink {
opacity: 0.7;
}
.formconsumeproduce {
background: #f3f3f3;
padding: 20px 0px 0px 0px;

View File

@ -933,11 +933,15 @@ input[type=checkbox], input[type=radio] {
/* CSS for placeholder */
.placeholder { color: #ccc; }
select.placeholder { color: #ccc; }
::-webkit-input-placeholder { color:#ccc; }
:-moz-placeholder { color:#bbb; } /* firefox 18- */
::-moz-placeholder { color:#bbb; } /* firefox 19+ */
:-ms-input-placeholder { color:#ccc; } /* ie */
input:-moz-placeholder { color:#ccc; }
select.placeholder option:not(.opacitymediumbycolor):not(.opacitymedium) {
color: var(--colortext);
}
input[name=price], input[name=weight], input[name=volume], input[name=surface], input[name=sizeheight], input[name=net_measure], select[name=incoterm_id] { margin-right: 6px; }
fieldset {
@ -4490,7 +4494,7 @@ tr.liste_titre_filter td.liste_titre {
padding-top: 4px;
padding-bottom: 3px;
}
.liste_titre_create td, .liste_titre_create th, .liste_titre_create .tagtd
.liste_titre_create td:not(.linecoldescription), .liste_titre_create th, .liste_titre_create .tagtd
{
border-top-width: 1px;
border-top-color: var(--colortopbordertitle1);
@ -4512,6 +4516,10 @@ tr#trlinefordates td {
border-top-style: solid;
}
td.linecoldescription {
padding: 6px 10px 6px 12px !important; /* t r b l */
}
table.liste th, table.noborder th, table.noborder tr.liste_titre td, table.noborder tr.box_titre td {
padding: 8px 8px 8px 10px; /* t r b l */
}

View File

@ -134,6 +134,8 @@ class AllTests
$suite->addTestSuite('SecurityTest');
require_once dirname(__FILE__).'/SecurityGETPOSTTest.php';
$suite->addTestSuite('SecurityGETPOSTTest');
require_once dirname(__FILE__).'/SecurityLoginTest.php';
$suite->addTestSuite('SecurityLoginTest');
require_once dirname(__FILE__).'/UserTest.php';
$suite->addTestSuite('UserTest');

View File

@ -0,0 +1,106 @@
<?php
/* Copyright (C) 2010 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2023 Alexandre Janniaux <alexandre.janniaux@gmail.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
*
* 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 <https://www.gnu.org/licenses/>.
* or see https://www.gnu.org/
*/
/**
* \file test/phpunit/SecurityTest.php
* \ingroup test
* \brief PHPUnit test
* \remarks To run this script as CLI: phpunit filename.php
*/
global $conf,$user,$langs,$db;
//define('TEST_DB_FORCE_TYPE','mysql'); // This is to force using mysql driver
//require_once 'PHPUnit/Autoload.php';
if (! defined('NOREQUIRESOC')) {
define('NOREQUIRESOC', '1');
}
if (! defined('NOCSRFCHECK')) {
define('NOCSRFCHECK', '1');
}
if (! defined('NOTOKENRENEWAL')) {
define('NOTOKENRENEWAL', '1');
}
if (! defined('NOREQUIREMENU')) {
define('NOREQUIREMENU', '1'); // If there is no menu to show
}
if (! defined('NOREQUIREHTML')) {
define('NOREQUIREHTML', '1'); // If we don't need to load the html.form.class.php
}
if (! defined('NOREQUIREAJAX')) {
define('NOREQUIREAJAX', '1');
}
if (! defined("NOLOGIN")) {
define("NOLOGIN", '1'); // If this page is public (can be called outside logged session)
}
if (! defined("NOSESSION")) {
define("NOSESSION", '1');
}
require_once dirname(__FILE__).'/../../htdocs/main.inc.php'; // We force include of main.inc.php instead of master.inc.php even if we are in CLI mode because it contains a lot of security components we want to test.
require_once dirname(__FILE__).'/../../htdocs/core/lib/security.lib.php';
require_once dirname(__FILE__).'/../../htdocs/core/lib/security2.lib.php';
require_once dirname(__FILE__).'/CommonClassTest.class.php';
if (empty($user->id)) {
print "Load permissions for admin user nb 1\n";
$user->fetch(1);
$user->loadRights();
}
$conf->global->MAIN_DISABLE_ALL_MAILS = 1;
/**
* Class for PHPUnit tests
*
* @backupGlobals disabled
* @backupStaticAttributes enabled
* @remarks backupGlobals must be disabled to have db,conf,user and lang not erased.
*/
class SecurityLoginTest extends CommonClassTest
{
/**
* testCheckLoginPassEntity
*
* @return void
*/
public function testCheckLoginPassEntity()
{
$login = checkLoginPassEntity('loginbidon', 'passwordbidon', 1, array('dolibarr'));
print __METHOD__." login=".$login."\n";
$this->assertEquals($login, '');
$login = checkLoginPassEntity('admin', 'passwordbidon', 1, array('dolibarr'));
print __METHOD__." login=".$login."\n";
$this->assertEquals($login, '');
$login = checkLoginPassEntity('admin', 'admin', 1, array('dolibarr')); // Should works because admin/admin exists
print __METHOD__." login=".$login."\n";
$this->assertEquals($login, 'admin', 'The test to check if pass of user "admin" is "admin" has failed');
$login = checkLoginPassEntity('admin', 'admin', 1, array('http','dolibarr')); // Should work because of second authentication method
print __METHOD__." login=".$login."\n";
$this->assertEquals($login, 'admin');
$login = checkLoginPassEntity('admin', 'admin', 1, array('forceuser'));
print __METHOD__." login=".$login."\n";
$this->assertEquals('', $login, 'Error'); // Expected '' because should failed because login 'auto' does not exists
}
}

View File

@ -636,13 +636,13 @@ class SecurityTest extends CommonClassTest
$this->assertEquals('Bad string syntax to evaluate: new __forbiddenstring__(\'abc\')', $result);
$result = (string) dol_eval('$a=function() { }; $a;', 1, 1, '0');
print "result5 = ".$result."\n";
$this->assertStringContainsString('Bad string syntax to evaluate', $result);
$result = dol_eval('$a=function() { }; $a', 1, 1, '0'); // result of dol_eval may be an object Closure
print "result5 = ".json_encode($result)."\n";
$this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result));
$result = (string) dol_eval('$a=function() { }; $a;', 1, 1, '1');
print "result6 = ".$result."\n";
$this->assertStringContainsString('Bad string syntax to evaluate', $result);
$result = dol_eval('$a=function() { }; $a();', 1, 1, '1');
print "result6 = ".json_encode($result)."\n";
$this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result));
$result = (string) dol_eval('$a=exec("ls");', 1, 1);
print "result7 = ".$result."\n";
@ -723,6 +723,11 @@ class SecurityTest extends CommonClassTest
$result = (string) dol_eval('($a = "ex") && ($b = "ec") && ($cmd = "$a$b") && $cmd ("curl localhost:5555")', 1, 0);
print "result22 = ".$result."\n";
$this->assertStringContainsString('Bad string syntax to evaluate', $result, 'Test 22');
$result = (string) dol_eval('\'exec\'("aaa")', 1, 0);
print "result1 = ".$result."\n";
$this->assertStringContainsString('Bad string syntax to evaluate', json_encode($result), 'Cant find the string Bad string syntaxwhen i should');
}
/**
@ -966,33 +971,4 @@ class SecurityTest extends CommonClassTest
return 0;
}
/**
* testCheckLoginPassEntity
*
* @return void
*/
public function testCheckLoginPassEntity()
{
$login = checkLoginPassEntity('loginbidon', 'passwordbidon', 1, array('dolibarr'));
print __METHOD__." login=".$login."\n";
$this->assertEquals($login, '');
$login = checkLoginPassEntity('admin', 'passwordbidon', 1, array('dolibarr'));
print __METHOD__." login=".$login."\n";
$this->assertEquals($login, '');
$login = checkLoginPassEntity('admin', 'admin', 1, array('dolibarr')); // Should works because admin/admin exists
print __METHOD__." login=".$login."\n";
$this->assertEquals($login, 'admin', 'The test to check if pass of user "admin" is "admin" has failed');
$login = checkLoginPassEntity('admin', 'admin', 1, array('http','dolibarr')); // Should work because of second authentication method
print __METHOD__." login=".$login."\n";
$this->assertEquals($login, 'admin');
$login = checkLoginPassEntity('admin', 'admin', 1, array('forceuser'));
print __METHOD__." login=".$login."\n";
$this->assertEquals('', $login, 'Error'); // Expected '' because should failed because login 'auto' does not exists
}
}

View File

@ -65,11 +65,11 @@ if (empty($user->id)) {
print "Load permissions for admin user nb 1\n";
$user->fetch(1);
$user->loadRights();
if (empty($user->rights->website)) {
$user->rights->website = new stdClass();
}
}
if (empty($user->rights->website)) {
$user->rights->website = new stdClass();
}
$conf->global->MAIN_DISABLE_ALL_MAILS = 1;
@ -132,22 +132,61 @@ class WebsiteTest extends CommonClassTest
*/
public function testCheckPHPCode()
{
global $user;
global $conf, $user;
// Force permission so this is not the permission that will affect result of checkPHPCode
$user->rights->website->writephp = 1;
// Legitimate
$t = '';
$s = '<?php execu ?>';
$result = checkPHPCode($t, $s);
print __METHOD__." result checkPHPCode=".$result."\n";
$this->assertEquals($result, 0, 'checkPHPCode detect string as dangerous when it is legitimate');
// Dangerous
$t = '';
$s = '<?php exec("eee"); ?>';
$result = checkPHPCode($t, $s);
print __METHOD__." result checkPHPCode=".$result."\n";
$this->assertEquals($result, 1, 'checkPHPCode did not detect the string was dangerous');
$t = '';
$s = '<?php eXec ("eee"); ?>';
$result = checkPHPCode($t, $s);
print __METHOD__." result checkPHPCode=".$result."\n";
$this->assertEquals($result, 1, 'checkPHPCode did not detect the string was dangerous');
$t = '';
$s = '<?php $a="xec"; "e$a" ("ee"); ?>';
$result = checkPHPCode($t, $s);
print __METHOD__." result checkPHPCode=".$result."\n";
$this->assertEquals($result, 1, 'checkPHPCode did not detect the string was dangerous');
$t = '';
$s = '<?php $a=\'exec\'("ee"); ?>';
$result = checkPHPCode($t, $s);
print __METHOD__." result checkPHPCode=".$result."\n";
$this->assertEquals($result, 1, 'checkPHPCode did not detect the string was dangerous');
$t = '';
$s = '<?php $_="{"; $_=($_^"<").($_^">;").($_^"/"); ?><?=${\'_\'.$_}["_"](${\'_\'.$_}["__"]);?>';
$result = checkPHPCode($t, $s);
print __METHOD__." result checkPHPCode=".$result."\n";
$this->assertEquals($result, 1, 'checkPHPCode did not detect the string was dangerous');
// Dangerous but legitimate due to option WEBSITE_PHP_ALLOW_EXEC
$conf->global->WEBSITE_PHP_ALLOW_EXEC = 1;
$t = '';
$s = '<?php exec("eee"); ?>';
$result = checkPHPCode($t, $s);
print __METHOD__." result checkPHPCode=".$result."\n";
$this->assertEquals($result, 0, 'checkPHPCode did not accept the exec. it should when WEBSITE_PHP_ALLOW_EXEC is set.');
}
/**