';
+ print '
';
print '';
print ''.img_picto('', 'technic', 'class="pictofixedwidth"').$form->textwithpicto($langs->trans("EmailcollectorOperations"), $langs->trans("EmailcollectorOperationsDesc")).' ';
print ' ';
$arrayoftypes = array(
- 'loadthirdparty' => $langs->trans('LoadThirdPartyFromName', $langs->transnoentities("ThirdPartyName")),
- 'loadandcreatethirdparty' => $langs->trans('LoadThirdPartyFromNameOrCreate', $langs->transnoentities("ThirdPartyName")),
+ 'loadthirdparty' => $langs->trans('LoadThirdPartyFromName', $langs->transnoentities("ThirdPartyName").'/'.$langs->transnoentities("AliasNameShort").'/'.$langs->transnoentities("Email").'/'.$langs->transnoentities("ID")),
+ 'loadandcreatethirdparty' => $langs->trans('LoadThirdPartyFromNameOrCreate', $langs->transnoentities("ThirdPartyName").'/'.$langs->transnoentities("AliasNameShort").'/'.$langs->transnoentities("Email").'/'.$langs->transnoentities("ID")),
'recordjoinpiece' => 'AttachJoinedDocumentsToObject',
'recordevent' => 'RecordEvent'
);
diff --git a/htdocs/admin/emailcollector_list.php b/htdocs/admin/emailcollector_list.php
index b58bb5bb15d..ccee1f604b2 100644
--- a/htdocs/admin/emailcollector_list.php
+++ b/htdocs/admin/emailcollector_list.php
@@ -597,7 +597,7 @@ while ($i < $imaxinloop) {
if (!empty($arrayfields['t.'.$key]['checked'])) {
print '$key)) {
print ' title="'.dol_escape_htmltag($object->$key).'"';
}
print '>';
diff --git a/htdocs/admin/mails.php b/htdocs/admin/mails.php
index 06f4dfdea85..504197fac7a 100644
--- a/htdocs/admin/mails.php
+++ b/htdocs/admin/mails.php
@@ -924,7 +924,7 @@ if ($action == 'edit') {
print ''.$langs->trans("DoTestServerAvailability").' ';
}
} else {
- print ''.$langs->trans("DoTestServerAvailability").' ';
+ //print ''.$langs->trans("DoTestServerAvailability").' ';
}
print ''.$langs->trans("DoTestSend").' ';
@@ -1028,7 +1028,7 @@ if ($action == 'edit') {
print '
';
print load_fiche_titre($action == 'testhtml' ? $langs->trans("DoTestSendHTML") : $langs->trans("DoTestSend"));
- print dol_get_fiche_head('');
+ print dol_get_fiche_head(array(), '', '', -1);
// Cree l'objet formulaire mail
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
@@ -1073,6 +1073,7 @@ if ($action == 'edit') {
print dol_get_fiche_end();
// References
+ print ' ';
print ''.$langs->trans("EMailsWillHaveMessageID").': ';
print dol_escape_htmltag('');
print ' ';
diff --git a/htdocs/admin/mails_senderprofile_list.php b/htdocs/admin/mails_senderprofile_list.php
index 38fb81c6430..dbf47365440 100644
--- a/htdocs/admin/mails_senderprofile_list.php
+++ b/htdocs/admin/mails_senderprofile_list.php
@@ -670,7 +670,7 @@ if ($num == 0) {
$colspan++;
}
}
- print ' '.$langs->trans("NoRecordFound").' ';
+ print ''.$langs->trans("NoRecordFound").' ';
}
diff --git a/htdocs/admin/modules.php b/htdocs/admin/modules.php
index 5dbc6625ba2..68bec8431a7 100644
--- a/htdocs/admin/modules.php
+++ b/htdocs/admin/modules.php
@@ -4,7 +4,7 @@
* Copyright (C) 2004-2017 Laurent Destailleur
* Copyright (C) 2004 Eric Seigne
* Copyright (C) 2005-2017 Regis Houssin
- * Copyright (C) 2011 Juanjo Menent
+ * Copyright (C) 2011-2023 Juanjo Menent
* Copyright (C) 2015 Jean-François Ferry
* Copyright (C) 2015 Raphaël Doursenaud
* Copyright (C) 2018 Nicolas ZABOURI
@@ -230,7 +230,10 @@ if ($action == 'install') {
// Now we install the module
if (!$error) {
@dol_delete_dir_recursive($dirins.'/'.$modulenameval); // delete the target directory
- $submodulenamedir = $conf->admin->dir_temp.'/'.$tmpdir.'/htdocs/'.$modulenameval;
+ $submodulenamedir = $conf->admin->dir_temp.'/'.$tmpdir.'/'.$modulenameval;
+ if (!dol_is_dir($modulenamedir)) {
+ $submodulenamedir = $conf->admin->dir_temp.'/'.$tmpdir.'/htdocs/'.$modulenameval;
+ }
dol_syslog("We copy now directory ".$submodulenamedir." into target dir ".$dirins.'/'.$modulenameval);
$result = dolCopyDir($submodulenamedir, $dirins.'/'.$modulenameval, '0444', 1);
if ($result <= 0) {
diff --git a/htdocs/admin/system/browser.php b/htdocs/admin/system/browser.php
index 7e2c772183d..f86b6404019 100644
--- a/htdocs/admin/system/browser.php
+++ b/htdocs/admin/system/browser.php
@@ -1,5 +1,5 @@
+/* Copyright (C) 2005-2023 Laurent Destailleur
* Copyright (C) 2007 Rodolphe Quiedeville
* Copyright (C) 2007-2012 Regis Houssin
*
@@ -65,7 +65,7 @@ if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
}
print ''."\n";
print ''.$langs->trans("SessionName").' '.session_name().' '."\n";
-print ''.$langs->trans("SessionId").' '.session_id().' '."\n";
+print ''.$langs->trans("SessionId").' ******** '."\n";
print ''.$langs->trans("Screen").' ';
print $_SESSION['dol_screenwidth'].' x '.$_SESSION['dol_screenheight'];
diff --git a/htdocs/admin/system/security.php b/htdocs/admin/system/security.php
index 90bb35320c0..15db7d2ba0d 100644
--- a/htdocs/admin/system/security.php
+++ b/htdocs/admin/system/security.php
@@ -241,14 +241,41 @@ print ' ';
print ' ';
$installlock = DOL_DATA_ROOT.'/install.lock';
+$upgradeunlock = DOL_DATA_ROOT.'/upgrade.unlock';
+$installmoduleslock = DOL_DATA_ROOT.'/installmodules.lock';
+
+// Is install (upgrade) locked
print ''.$langs->trans("DolibarrSetup").' : ';
if (file_exists($installlock)) {
- print img_picto('', 'tick').' '.$langs->trans("InstallAndUpgradeLockedBy", $installlock);
+ if (file_exists($upgradeunlock)) {
+ print img_picto('', 'tick').' '.$langs->trans("InstallLockedBy", $installlock);
+ } else {
+ print img_picto('', 'tick').' '.$langs->trans("InstallAndUpgradeLockedBy", $installlock);
+ }
} else {
print img_warning().' '.$langs->trans("WarningLockFileDoesNotExists", DOL_DATA_ROOT);
}
print ' ';
+// Is upgrade unlocked
+if (file_exists($installlock)) { // If install not locked, no need to show this.
+ if (file_exists($upgradeunlock)) {
+ print ''.$langs->trans("DolibarrUpgrade").' : ';
+ print img_warning().' '.$langs->trans("UpgradeHasBeenUnlocked", $upgradeunlock);
+ print ' ';
+ }
+}
+
+// Is addon install locked ?
+print ''.$langs->trans("DolibarrAddonInstall").' : ';
+if (file_exists($installmoduleslock)) {
+ print img_picto('', 'tick').' '.$langs->trans("InstallAndUpgradeLockedBy", $installmoduleslock);
+} else {
+ print $langs->trans("InstallOfAddonIsNotBlocked", DOL_DATA_ROOT);
+}
+print ' ';
+
+
// File conf.php
@@ -286,7 +313,7 @@ if (empty($dolibarr_main_restrict_os_commands)) {
} else {
print $dolibarr_main_restrict_os_commands;
}
-print ' ('.$langs->trans("RecommendedValueIs", 'mysqldump, mysql, pg_dump, pgrestore').') ';
+print ' ('.$langs->trans("RecommendedValueIs", 'mysqldump, mysql, pg_dump, pgrestore, clamdscan').') ';
print ' ';
if (empty($conf->global->SECURITY_DISABLE_TEST_ON_OBFUSCATED_CONF)) {
diff --git a/htdocs/admin/ticket_public.php b/htdocs/admin/ticket_public.php
index 1f32fec97aa..2bd16271650 100644
--- a/htdocs/admin/ticket_public.php
+++ b/htdocs/admin/ticket_public.php
@@ -379,6 +379,20 @@ if (!empty($conf->global->TICKET_ENABLE_PUBLIC_INTERFACE)) {
print ' ';
print ' ';
+ // Show progression
+ print ''.$langs->trans("TicketsShowProgression").' ';
+ print '';
+ if (empty(getDolGlobalInt('TICKET_SHOW_PROGRESSION'))) {
+ print '' . img_picto($langs->trans('Disabled'), 'switch_off') . ' ';
+ } else {
+ print '' . img_picto($langs->trans('Enabled'), 'switch_on') . ' ';
+ }
+ print ' ';
+ print '';
+ print $form->textwithpicto('', $langs->trans("TicketsShowProgressionHelp"), 1, 'help');
+ print ' ';
+ print ' ';
+
// Also send to main email address
if ($conf->global->MAIN_FEATURES_LEVEL >= 2) {
print ''.$langs->trans("TicketsEmailAlsoSendToMainAddress").' ';
diff --git a/htdocs/admin/tools/dolibarr_export.php b/htdocs/admin/tools/dolibarr_export.php
index 3ce6dbd48a4..856113eef4d 100644
--- a/htdocs/admin/tools/dolibarr_export.php
+++ b/htdocs/admin/tools/dolibarr_export.php
@@ -163,13 +163,13 @@ $title = $langs->trans("BackupDumpWizard");
print load_fiche_titre($title);
-print '';
-print '';
-print '';
-print $langs->trans("DatabaseName").' : '.$dolibarr_main_db_name.' ';
-print ' ';
-print ' ';
-print '';
+//print '';
+//print '';
+//print '';
+print ''.$langs->trans("DatabaseName").' : '.$dolibarr_main_db_name.' ';
+//print ' ';
+//print ' ';
+//print '';
print '';
@@ -589,8 +589,8 @@ if (!empty($_SESSION["commandbackuptorun"])) {
print " \n";
-print '';
-print '
';
+//print ' ';
+//print '
';
print " \n";
@@ -598,7 +598,7 @@ print " \n";
print '';
$filearray = dol_dir_list($conf->admin->dir_output.'/backup', 'files', 0, '', '', $sortfield, (strtolower($sortorder) == 'asc' ?SORT_ASC:SORT_DESC), 1);
-$result = $formfile->list_of_documents($filearray, null, 'systemtools', '', 1, 'backup/', 1, 0, $langs->trans("NoBackupFileAvailable"), 0, $langs->trans("PreviousDumpFiles"), '', 0, -1, '', '', 'ASC', 1, 0, -1, 'style="height:480px; overflow: auto;"');
+$result = $formfile->list_of_documents($filearray, null, 'systemtools', '', 1, 'backup/', 1, 0, $langs->trans("NoBackupFileAvailable"), 0, $langs->trans("PreviousDumpFiles"), '', 0, -1, '', '', 'ASC', 1, 0, -1, 'style="height:250px; overflow: auto;"');
print ' ';
print '
';
@@ -652,12 +652,12 @@ foreach ($filecompression as $key => $val) {
if ($key == 'gz') {
$checked = ' checked';
}
- print ' ';
- print ' '.$val['label'].' ';
+ print ' ';
+ print ' '.$val['label'].' ';
} else // Disabled export format
{
- print ' ';
- print ' '.$val['label'].' ';
+ print ' ';
+ print ' '.$val['label'].' ';
print ' ('.$langs->trans("NotAvailable").') ';
}
print ' ';
@@ -686,6 +686,8 @@ print '';
print '';
print '';
+print ' ';
+
// End of page
llxFooter();
$db->close();
diff --git a/htdocs/asset/class/asset.class.php b/htdocs/asset/class/asset.class.php
index 8e69cb19a1a..e2d93806ca3 100644
--- a/htdocs/asset/class/asset.class.php
+++ b/htdocs/asset/class/asset.class.php
@@ -162,20 +162,6 @@ class Asset extends CommonObject
*/
public $oldcopy;
- // /**
- // * @var string Field with ID of parent key if this object has a parent
- // */
- // public $fk_element = 'fk_asset';
- // /**
- // * @var array List of child tables. To test if we can delete object.
- // */
- // protected $childtables = array();
- // /**
- // * @var array List of child tables. To know object to delete on cascade.
- // * If name matches '@ClassNAme:FilePathClass;ParentFkFieldName' it will
- // * call method deleteByParentField(parentId, ParentFkFieldName) to fetch and delete child object
- // */
- // protected $childtablesoncascade = array('asset_assetdet');
/**
* @var AssetDepreciationOptions Used for computed fields of depreciation options class.
diff --git a/htdocs/asset/list.php b/htdocs/asset/list.php
index d98165d28a2..4b3b530148d 100644
--- a/htdocs/asset/list.php
+++ b/htdocs/asset/list.php
@@ -639,7 +639,7 @@ if ($num == 0) {
$colspan++;
}
}
- print ' '.$langs->trans("NoRecordFound").' ';
+ print ''.$langs->trans("NoRecordFound").' ';
}
diff --git a/htdocs/blockedlog/class/authority.class.php b/htdocs/blockedlog/class/authority.class.php
index b3dd9b45f47..e274ff4175d 100644
--- a/htdocs/blockedlog/class/authority.class.php
+++ b/htdocs/blockedlog/class/authority.class.php
@@ -20,6 +20,11 @@
*/
class BlockedLogAuthority
{
+ /**
+ * DoliDB
+ * @var DoliDB
+ */
+ public $db;
/**
* Id of the log
@@ -45,6 +50,12 @@ class BlockedLogAuthority
*/
public $tms = 0;
+ /**
+ * Error message
+ * @var string
+ */
+ public $error;
+
/**
* Constructor
*
diff --git a/htdocs/bom/bom_agenda.php b/htdocs/bom/bom_agenda.php
index d22f457df85..ddd57d34e76 100644
--- a/htdocs/bom/bom_agenda.php
+++ b/htdocs/bom/bom_agenda.php
@@ -92,7 +92,7 @@ if ($id > 0 || !empty($ref)) {
//if ($user->socid > 0) accessforbidden();
//if ($user->socid > 0) $socid = $user->socid;
$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
-restrictedArea($user, 'bom', $object->id, 'bom_bom', '', '', 'rowid', $isdraft);
+restrictedArea($user, 'bom', $object->id, $object->table_element, '', '', 'rowid', $isdraft);
/*
diff --git a/htdocs/bom/bom_card.php b/htdocs/bom/bom_card.php
index 33e562a8a20..e27cd0a2e1c 100644
--- a/htdocs/bom/bom_card.php
+++ b/htdocs/bom/bom_card.php
@@ -84,7 +84,7 @@ if ($object->id > 0) {
//if ($user->socid > 0) accessforbidden();
//if ($user->socid > 0) $socid = $user->socid;
$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
-$result = restrictedArea($user, 'bom', $object->id, 'bom_bom', '', '', 'rowid', $isdraft);
+$result = restrictedArea($user, 'bom', $object->id, $object->table_element, '', '', 'rowid', $isdraft);
// Permissions
$permissionnote = $user->hasRight('bom', 'write'); // Used by the include of actions_setnotes.inc.php
diff --git a/htdocs/bom/bom_document.php b/htdocs/bom/bom_document.php
index 03b9f416d43..88fbdde11c7 100644
--- a/htdocs/bom/bom_document.php
+++ b/htdocs/bom/bom_document.php
@@ -83,7 +83,7 @@ if ($id > 0 || !empty($ref)) {
//if ($user->socid > 0) accessforbidden();
//if ($user->socid > 0) $socid = $user->socid;
$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
-restrictedArea($user, 'bom', $object->id, 'bom_bom', '', '', 'rowid', $isdraft);
+restrictedArea($user, 'bom', $object->id, $object->table_element, '', '', 'rowid', $isdraft);
$permissiontoadd = $user->hasRight('bom', 'write'); // Used by the include of actions_addupdatedelete.inc.php and actions_linkedfiles.inc.php
diff --git a/htdocs/bom/bom_list.php b/htdocs/bom/bom_list.php
index a12bd7fdb2d..065a3a578b7 100644
--- a/htdocs/bom/bom_list.php
+++ b/htdocs/bom/bom_list.php
@@ -728,8 +728,8 @@ while ($i < $imaxinloop) {
}
if (!empty($arrayfields['t.'.$key]['checked'])) {
- print '$key)) {
print ' title="'.dol_escape_htmltag($object->$key).'"';
}
print '>';
diff --git a/htdocs/bom/bom_net_needs.php b/htdocs/bom/bom_net_needs.php
index 56683e82338..f0e993d6eea 100644
--- a/htdocs/bom/bom_net_needs.php
+++ b/htdocs/bom/bom_net_needs.php
@@ -81,7 +81,7 @@ if ($object->id > 0) {
//if ($user->socid > 0) accessforbidden();
//if ($user->socid > 0) $socid = $user->socid;
$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
-$result = restrictedArea($user, 'bom', $object->id, 'bom_bom', '', '', 'rowid', $isdraft);
+$result = restrictedArea($user, 'bom', $object->id, $object->table_element, '', '', 'rowid', $isdraft);
// Permissions
$permissionnote = $user->hasRight('bom', 'write'); // Used by the include of actions_setnotes.inc.php
diff --git a/htdocs/bom/bom_note.php b/htdocs/bom/bom_note.php
index 10c64a1b104..188c34c8c05 100644
--- a/htdocs/bom/bom_note.php
+++ b/htdocs/bom/bom_note.php
@@ -67,7 +67,7 @@ $permissionnote = $user->hasRight('bom', 'write'); // Used by the include of act
//if ($user->socid > 0) accessforbidden();
//if ($user->socid > 0) $socid = $user->socid;
$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
-restrictedArea($user, 'bom', $object->id, 'bom_bom', '', '', 'rowid', $isdraft);
+restrictedArea($user, 'bom', $object->id, $object->table_element, '', '', 'rowid', $isdraft);
/*
diff --git a/htdocs/bom/class/bom.class.php b/htdocs/bom/class/bom.class.php
index c00b9781756..45cbfdef383 100644
--- a/htdocs/bom/class/bom.class.php
+++ b/htdocs/bom/class/bom.class.php
@@ -1131,13 +1131,12 @@ class BOM extends CommonObject
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
-
-
$url = DOL_URL_ROOT.'/bom/bom_card.php?id='.$this->id;
if ($option != 'nolink') {
@@ -1157,7 +1156,7 @@ class BOM extends CommonObject
$label = $langs->trans("ShowBillOfMaterials");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
$linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
} else {
$linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
diff --git a/htdocs/bom/tpl/objectline_create.tpl.php b/htdocs/bom/tpl/objectline_create.tpl.php
index e87d526302b..0d49937e6d2 100644
--- a/htdocs/bom/tpl/objectline_create.tpl.php
+++ b/htdocs/bom/tpl/objectline_create.tpl.php
@@ -114,17 +114,18 @@ if (isModEnabled("product") || isModEnabled("service")) {
$statustoshow = -1;
if (!empty($conf->global->ENTREPOT_EXTRA_STATUS)) {
// hide products in closed warehouse, but show products for internal transfer
- $form->select_produits(GETPOST('idprod', 'int'), (($filtertype == 1) ? 'idprodservice' : 'idprod'), $filtertype, $conf->product->limit_size, $buyer->price_level, $statustoshow, 2, '', 1, array(), $buyer->id, '1', 0, 'maxwidth500', 0, 'warehouseopen,warehouseinternal', GETPOST('combinations', 'array'));
+ print $form->select_produits(GETPOST('idprod', 'int'), (($filtertype == 1) ? 'idprodservice' : 'idprod'), $filtertype, $conf->product->limit_size, $buyer->price_level, $statustoshow, 2, '', 1, array(), $buyer->id, '1', 0, 'maxwidth500', 0, 'warehouseopen,warehouseinternal', GETPOST('combinations', 'array'), 1);
} else {
- $form->select_produits(GETPOST('idprod', 'int'), (($filtertype == 1) ? 'idprodservice' : 'idprod'), $filtertype, $conf->product->limit_size, $buyer->price_level, $statustoshow, 2, '', 1, array(), $buyer->id, '1', 0, 'maxwidth500', 0, '', GETPOST('combinations', 'array'));
+ print $form->select_produits(GETPOST('idprod', 'int'), (($filtertype == 1) ? 'idprodservice' : 'idprod'), $filtertype, $conf->product->limit_size, $buyer->price_level, $statustoshow, 2, '', 1, array(), $buyer->id, '1', 0, 'maxwidth500', 0, '', GETPOST('combinations', 'array'), 1);
}
+ $urltocreateproduct = DOL_URL_ROOT.'/product/card.php?action=create&backtopage='.urlencode($_SERVER["PHP_SELF"].'?id='.$object->id);
+ print ' ';
echo '';
}
if (!empty($conf->global->BOM_SUB_BOM) && $filtertype!=1) {
print ''.$langs->trans("or").' '.$langs->trans("BOM");
- // TODO Add component to select a BOM
- $form->select_bom();
+ print $form->select_bom('', 'bom_id', 0, 1, 0, '1', '', 1);
}
if (is_object($objectline)) {
diff --git a/htdocs/bookcal/availabilities_list.php b/htdocs/bookcal/availabilities_list.php
index 208190aaad5..fd5866eaff8 100644
--- a/htdocs/bookcal/availabilities_list.php
+++ b/htdocs/bookcal/availabilities_list.php
@@ -682,8 +682,8 @@ while ($i < $imaxinloop) {
//if (in_array($key, array('fk_soc', 'fk_user', 'fk_warehouse'))) $cssforfield = 'tdoverflowmax100';
if (!empty($arrayfields['t.'.$key]['checked'])) {
- print ' $key)) {
print ' title="'.dol_escape_htmltag($object->$key).'"';
}
print '>';
diff --git a/htdocs/bookcal/booking_list.php b/htdocs/bookcal/booking_list.php
index 033c8ec121f..e030a52b7e3 100644
--- a/htdocs/bookcal/booking_list.php
+++ b/htdocs/bookcal/booking_list.php
@@ -682,8 +682,8 @@ while ($i < $imaxinloop) {
//if (in_array($key, array('fk_soc', 'fk_user', 'fk_warehouse'))) $cssforfield = 'tdoverflowmax100';
if (!empty($arrayfields['t.'.$key]['checked'])) {
- print ' $key)) {
print ' title="'.dol_escape_htmltag($object->$key).'"';
}
print '>';
diff --git a/htdocs/categories/class/categorie.class.php b/htdocs/categories/class/categorie.class.php
index 6762770d564..fc0246e1a02 100644
--- a/htdocs/categories/class/categorie.class.php
+++ b/htdocs/categories/class/categorie.class.php
@@ -1624,6 +1624,8 @@ class Categorie extends CommonObject
{
global $langs;
+ $langs->load('categories');
+
$datas = [];
$datas['label'] = $langs->trans("ShowCategory").': '.($this->ref ? $this->ref : $this->label);
@@ -1655,10 +1657,11 @@ class Categorie extends CommonObject
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
// Check contrast with background and correct text color
$forced_color = 'categtextwhite';
@@ -1668,7 +1671,9 @@ class Categorie extends CommonObject
}
}
$link = '';
+ $link .= '"'.$dataparams;
+ $link .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
+ $link .= ' class="'.$classfortooltip.' '.$forced_color.'">';
$linkend = ' ';
$picto = 'category';
diff --git a/htdocs/comm/action/card.php b/htdocs/comm/action/card.php
index 4dc5221cea3..942d4d2e75e 100644
--- a/htdocs/comm/action/card.php
+++ b/htdocs/comm/action/card.php
@@ -32,7 +32,6 @@
// Load Dolibarr environment
require '../../main.inc.php';
-
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
@@ -102,8 +101,8 @@ if (GETPOST('datep')) {
// Security check
$socid = GETPOST('socid', 'int');
$id = GETPOST('id', 'int');
-if ($user->socid) {
- $socid = $user->socid;
+if ($user->socid && ($socid != $user->socid)) {
+ accessforbidden();
}
$error = GETPOST("error");
@@ -154,7 +153,7 @@ if (!empty($conf->global->AGENDA_REMINDER_EMAIL)) {
$TDurationTypes = array('y'=>$langs->trans('Years'), 'm'=>$langs->trans('Month'), 'w'=>$langs->trans('Weeks'), 'd'=>$langs->trans('Days'), 'h'=>$langs->trans('Hours'), 'i'=>$langs->trans('Minutes'));
-$result = restrictedArea($user, 'agenda', $object->id, 'actioncomm&societe', 'myactions|allactions', 'fk_soc', 'id');
+$result = restrictedArea($user, 'agenda', $object, 'actioncomm&societe', 'myactions|allactions', 'fk_soc', 'id');
$usercancreate = $user->hasRight('agenda', 'allactions', 'create') || (($object->authorid == $user->id || $object->userownerid == $user->id) && $user->rights->agenda->myactions->create);
diff --git a/htdocs/comm/action/class/actioncomm.class.php b/htdocs/comm/action/class/actioncomm.class.php
index 48c51840e83..5822e6dd9d2 100644
--- a/htdocs/comm/action/class/actioncomm.class.php
+++ b/htdocs/comm/action/class/actioncomm.class.php
@@ -1319,15 +1319,17 @@ class ActionComm extends CommonObject
*/
public function getActions($socid = 0, $fk_element = 0, $elementtype = '', $filter = '', $sortfield = 'a.datep', $sortorder = 'DESC', $limit = 0)
{
- global $conf, $langs;
+ global $conf, $langs, $hookmanager;
$resarray = array();
dol_syslog(get_class()."::getActions", LOG_DEBUG);
- require_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
- $hookmanager = new HookManager($this->db);
// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
+ if (!is_object($hookmanager)) {
+ include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
+ $hookmanager = new HookManager($db);
+ }
$hookmanager->initHooks(array('agendadao'));
$sql = "SELECT a.id";
@@ -1744,8 +1746,8 @@ class ActionComm extends CommonObject
'nofetch' => 1,
];
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $tooltip = '';
}
//if (!empty($conf->global->AGENDA_USE_EVENT_TYPE) && $this->type_color)
// $linkclose = ' style="background-color:#'.$this->type_color.'"';
@@ -1755,7 +1757,7 @@ class ActionComm extends CommonObject
$label = $langs->trans("ShowAction");
$linkclose .= ' alt="'.dol_escape_htmltag($tooltip, 1).'"';
}
- $linkclose .= ' title="'.dol_escape_htmltag($tooltip, 1, 0, '', 1).'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
$linkclose .= $dataparams.' class="'.$classname.' '.$classfortooltip.'"';
} else {
$linkclose .= ' class="'.$classname.'"';
diff --git a/htdocs/comm/action/index.php b/htdocs/comm/action/index.php
index 91f749a1cd0..ed96199e4c3 100644
--- a/htdocs/comm/action/index.php
+++ b/htdocs/comm/action/index.php
@@ -927,6 +927,7 @@ if ($resql) {
}
//var_dump($eventarray);
+
// BIRTHDATES CALENDAR
// Complete $eventarray with birthdates
if ($showbirthday) {
@@ -972,6 +973,8 @@ if ($showbirthday) {
$event->percentage = 100;
$event->fulldayevent = 1;
+ $event->contact_id = $obj->rowid;
+
$event->date_start_in_calendar = $db->jdate($event->datep);
$event->date_end_in_calendar = $db->jdate($event->datef);
@@ -1000,7 +1003,7 @@ if ($showbirthday) {
}
}
-// LEAVE CALENDAR
+// LEAVE-HOLIDAY CALENDAR
$sql = "SELECT u.rowid as uid, u.lastname, u.firstname, u.statut, x.rowid, x.date_debut as date_start, x.date_fin as date_end, x.halfday, x.statut as status";
$sql .= " FROM ".MAIN_DB_PREFIX."holiday as x, ".MAIN_DB_PREFIX."user as u";
$sql .= " WHERE u.rowid = x.fk_user";
@@ -1012,12 +1015,12 @@ if ($mode == 'show_day') {
$sql .= " AND '".$db->escape($year)."-".$db->escape($month)."-".$db->escape($day)."' BETWEEN x.date_debut AND x.date_fin"; // date_debut and date_fin are date without time
} elseif ($mode == 'show_week') {
// Restrict on current month (we get more, but we will filter later)
- $sql .= " AND date_debut < '".dol_get_last_day($year, $month)."'";
- $sql .= " AND date_fin >= '".dol_get_first_day($year, $month)."'";
+ $sql .= " AND date_debut < '".$db->idate(dol_get_last_day($year, $month))."'";
+ $sql .= " AND date_fin >= '".$db->idate(dol_get_first_day($year, $month))."'";
} elseif ($mode == 'show_month') {
// Restrict on current month
- $sql .= " AND date_debut <= '".dol_get_last_day($year, $month)."'";
- $sql .= " AND date_fin >= '".dol_get_first_day($year, $month)."'";
+ $sql .= " AND date_debut <= '".$db->idate(dol_get_last_day($year, $month))."'";
+ $sql .= " AND date_fin >= '".$db->idate(dol_get_first_day($year, $month))."'";
}
$resql = $db->query($sql);
@@ -1800,7 +1803,10 @@ function show_day_events($db, $day, $month, $year, $monthshown, $style, &$eventa
$color = ($event->icalcolor ? $event->icalcolor : -1);
$cssclass = (!empty($event->icalname) ? 'family_ext'.md5($event->icalname) : 'family_other');
} elseif ($event->type_code == 'BIRTHDAY') {
- $numbirthday++; $colorindex = 2; $cssclass = 'family_birthday '; $color = sprintf("%02x%02x%02x", $theme_datacolor[$colorindex][0], $theme_datacolor[$colorindex][1], $theme_datacolor[$colorindex][2]);
+ $numbirthday++;
+ $colorindex = 2;
+ $cssclass = 'family_birthday ';
+ $color = sprintf("%02x%02x%02x", $theme_datacolor[$colorindex][0], $theme_datacolor[$colorindex][1], $theme_datacolor[$colorindex][2]);
} else {
$numother++;
$color = ($event->icalcolor ? $event->icalcolor : -1);
@@ -1930,9 +1936,31 @@ function show_day_events($db, $day, $month, $year, $monthshown, $style, &$eventa
$daterange = '';
- if ($event->type_code == 'BIRTHDAY') { // It's birthday calendar
- print $event->getNomUrl(1, $maxnbofchar, 'cal_event', 'birthday', 'contact');
- } elseif ($event->type_code == 'HOLIDAY') { // It's holiday calendar
+ if ($event->type_code == 'BIRTHDAY') {
+ // It's birthday calendar
+ $picb = ' ';
+ //$pice = ' ';
+ //$typea = ($objp->typea == 'birth') ? $picb : $pice;
+ //var_dump($event);
+ print $picb.' '.$langs->trans("Birthday").' ';
+ //print img_picto($langs->trans("Birthday"), 'birthday-cake').' ';
+
+ $tmpid = $event->id;
+ if (empty($cachecontacts[$tmpid])) {
+ $newcontact = new Contact($db);
+ $newcontact->fetch($tmpid);
+ $cachecontact[$tmpid] = $newcontact;
+ }
+ print $cachecontact[$tmpid]->getNomUrl(1);
+
+ //$event->picto = 'birthday-cake';
+ //print $event->getNomUrl(1, $maxnbofchar, 'cal_event', 'birthday', 'contact');
+ /*$listofcontacttoshow = '';
+ $listofcontacttoshow .= ' '.$cacheusers[$tmpid]->getNomUrl(-1, '', 0, 0, 0, 0, '', 'paddingright valignmiddle');
+ print $listofcontacttoshow;
+ */
+ } elseif ($event->type_code == 'HOLIDAY') {
+ // It's holiday calendar
$tmpholiday->fetch($event->id);
print $tmpholiday->getNomUrl(1);
@@ -1947,8 +1975,8 @@ function show_day_events($db, $day, $month, $year, $monthshown, $style, &$eventa
$listofusertoshow = '';
$listofusertoshow .= ' '.$cacheusers[$tmpid]->getNomUrl(-1, '', 0, 0, 0, 0, '', 'paddingright valignmiddle');
print $listofusertoshow;
- } else { // Other calendar
- // Picto
+ } else {
+ // Other calendar
if (empty($event->fulldayevent)) {
//print $event->getNomUrl(2).' ';
}
diff --git a/htdocs/comm/action/info.php b/htdocs/comm/action/info.php
index aa14224014a..ee34d07ea4b 100644
--- a/htdocs/comm/action/info.php
+++ b/htdocs/comm/action/info.php
@@ -1,6 +1,6 @@
- * Copyright (C) 2004-2010 Laurent Destailleur
+ * Copyright (C) 2004-2023 Laurent Destailleur
*
* 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
@@ -98,8 +98,6 @@ if (empty($reshook)) {
$linkback .= $out;
$morehtmlref = '';
-// Thirdparty
-//$morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1);
// Project
if (isModEnabled('project')) {
$langs->load("projects");
diff --git a/htdocs/comm/mailing/class/mailing.class.php b/htdocs/comm/mailing/class/mailing.class.php
index 75bfce98496..ef83c5d1470 100644
--- a/htdocs/comm/mailing/class/mailing.class.php
+++ b/htdocs/comm/mailing/class/mailing.class.php
@@ -792,10 +792,11 @@ class Mailing extends CommonObject
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
$url = DOL_URL_ROOT.'/comm/mailing/card.php?id='.$this->id;
@@ -816,8 +817,8 @@ class Mailing extends CommonObject
$label = $langs->trans("ShowEMailing");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= $dataparams.' title="'.dol_escape_htmltag($label, 1).'"';
- $linkclose .= ' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
+ $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
} else {
$linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
}
diff --git a/htdocs/comm/propal/card.php b/htdocs/comm/propal/card.php
index 5c4c4aae9f1..abf68f1d30e 100644
--- a/htdocs/comm/propal/card.php
+++ b/htdocs/comm/propal/card.php
@@ -221,7 +221,7 @@ if (empty($reshook)) {
}
}
- $result = $object->createFromClone($user, $socid, (GETPOSTISSET('entity') ? GETPOST('entity', 'int') : null), (GETPOST('update_prices', 'aZ') ? true : false));
+ $result = $object->createFromClone($user, $socid, (GETPOSTISSET('entity') ? GETPOST('entity', 'int') : null), (GETPOST('update_prices', 'aZ') ? true : false), (GETPOST('update_desc', 'aZ') ? true : false));
if ($result > 0) {
header("Location: ".$_SERVER['PHP_SELF'].'?id='.$result);
exit();
@@ -1599,7 +1599,7 @@ if (empty($reshook)) {
$error++;
}
if (!$error) {
- $result = $object->updateExtraField(GETPOST('attribute', 'restricthtml'), 'PROPAL_MODIFY', $user);
+ $result = $object->insertExtraFields('PROPAL_MODIFY');
if ($result < 0) {
setEventMessages($object->error, $object->errors, 'errors');
$error++;
@@ -2137,7 +2137,8 @@ if ($action == 'create') {
// 'text' => $langs->trans("ConfirmClone"),
// array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
array('type' => 'other', 'name' => 'socid', 'label' => $langs->trans("SelectThirdParty"), 'value' => $form->select_company(GETPOST('socid', 'int'), 'socid', '(s.client=1 OR s.client=2 OR s.client=3)', '', 0, 0, null, 0, 'maxwidth300')),
- array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans('PuttingPricesUpToDate'), 'value' => (!empty($conf->global->PROPOSAL_CLONE_UPDATE_PRICES) ? 1 : 0)),
+ array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans('PuttingPricesUpToDate'), 'value' => 0),
+ array('type' => 'checkbox', 'name' => 'update_desc', 'label' => $langs->trans('PuttingDescUpToDate'), 'value' => 0),
);
if (!empty($conf->global->PROPAL_CLONE_DATE_DELIVERY) && !empty($object->delivery_date)) {
$formquestion[] = array('type' => 'date', 'name' => 'date_delivery', 'label' => $langs->trans("DeliveryDate"), 'value' => $object->delivery_date);
diff --git a/htdocs/comm/propal/class/propal.class.php b/htdocs/comm/propal/class/propal.class.php
index 40e8fe385d7..3a247a11ec2 100644
--- a/htdocs/comm/propal/class/propal.class.php
+++ b/htdocs/comm/propal/class/propal.class.php
@@ -1360,9 +1360,10 @@ class Propal extends CommonObject
* @param int $socid Id of thirdparty
* @param int $forceentity Entity id to force
* @param bool $update_prices [=false] Update prices if true
+ * @param bool $update_desc [=false] Update description if true
* @return int New id of clone
*/
- public function createFromClone(User $user, $socid = 0, $forceentity = null, $update_prices = false)
+ public function createFromClone(User $user, $socid = 0, $forceentity = null, $update_prices = false, $update_desc = false)
{
global $conf, $hookmanager, $mysoc;
@@ -1413,9 +1414,9 @@ class Propal extends CommonObject
}
// update prices
- if ($update_prices === true) {
+ if ($update_prices === true || $update_desc === true) {
if ($objsoc->id > 0 && !empty($object->lines)) {
- if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
+ if ($update_prices === true && !empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
// If price per customer
require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
}
@@ -1425,36 +1426,41 @@ class Propal extends CommonObject
$prod = new Product($this->db);
$res = $prod->fetch($line->fk_product);
if ($res > 0) {
- $pu_ht = $prod->price;
- $tva_tx = get_default_tva($mysoc, $objsoc, $prod->id);
- $remise_percent = $objsoc->remise_percent;
+ if ($update_prices === true) {
+ $pu_ht = $prod->price;
+ $tva_tx = get_default_tva($mysoc, $objsoc, $prod->id);
+ $remise_percent = $objsoc->remise_percent;
- if (!empty($conf->global->PRODUIT_MULTIPRICES) && $objsoc->price_level > 0) {
- $pu_ht = $prod->multiprices[$objsoc->price_level];
- if (!empty($conf->global->PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL)) { // using this option is a bug. kept for backward compatibility
- if (isset($prod->multiprices_tva_tx[$objsoc->price_level])) {
- $tva_tx = $prod->multiprices_tva_tx[$objsoc->price_level];
+ if (!empty($conf->global->PRODUIT_MULTIPRICES) && $objsoc->price_level > 0) {
+ $pu_ht = $prod->multiprices[$objsoc->price_level];
+ if (!empty($conf->global->PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL)) { // using this option is a bug. kept for backward compatibility
+ if (isset($prod->multiprices_tva_tx[$objsoc->price_level])) {
+ $tva_tx = $prod->multiprices_tva_tx[$objsoc->price_level];
+ }
}
- }
- } elseif (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
- $prodcustprice = new Productcustomerprice($this->db);
- $filter = array('t.fk_product' => $prod->id, 't.fk_soc' => $objsoc->id);
- $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
- if ($result) {
- // If there is some prices specific to the customer
- if (count($prodcustprice->lines) > 0) {
- $pu_ht = price($prodcustprice->lines[0]->price);
- $tva_tx = ($prodcustprice->lines[0]->default_vat_code ? $prodcustprice->lines[0]->tva_tx.' ('.$prodcustprice->lines[0]->default_vat_code.' )' : $prodcustprice->lines[0]->tva_tx);
- if ($prodcustprice->lines[0]->default_vat_code && !preg_match('/\(.*\)/', $tva_tx)) {
- $tva_tx .= ' ('.$prodcustprice->lines[0]->default_vat_code.')';
+ } elseif (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
+ $prodcustprice = new Productcustomerprice($this->db);
+ $filter = array('t.fk_product' => $prod->id, 't.fk_soc' => $objsoc->id);
+ $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
+ if ($result) {
+ // If there is some prices specific to the customer
+ if (count($prodcustprice->lines) > 0) {
+ $pu_ht = price($prodcustprice->lines[0]->price);
+ $tva_tx = ($prodcustprice->lines[0]->default_vat_code ? $prodcustprice->lines[0]->tva_tx.' ('.$prodcustprice->lines[0]->default_vat_code.' )' : $prodcustprice->lines[0]->tva_tx);
+ if ($prodcustprice->lines[0]->default_vat_code && !preg_match('/\(.*\)/', $tva_tx)) {
+ $tva_tx .= ' ('.$prodcustprice->lines[0]->default_vat_code.')';
+ }
}
}
}
- }
- $line->subprice = $pu_ht;
- $line->tva_tx = $tva_tx;
- $line->remise_percent = $remise_percent;
+ $line->subprice = $pu_ht;
+ $line->tva_tx = $tva_tx;
+ $line->remise_percent = $remise_percent;
+ }
+ if ($update_desc === true) {
+ $line->desc = $prod->description;
+ }
}
}
}
@@ -3790,10 +3796,11 @@ class Propal extends CommonObject
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
$url = '';
if ($user->rights->propal->lire) {
@@ -3825,7 +3832,7 @@ class Propal extends CommonObject
$label = $langs->trans("Proposal");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
$linkclose .= $dataparams.' class="'.$classfortooltip.'"';
}
diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php
index 2b11588b854..f707714d772 100644
--- a/htdocs/commande/class/commande.class.php
+++ b/htdocs/commande/class/commande.class.php
@@ -3834,19 +3834,19 @@ class Commande extends CommonOrder
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
-
$linkclose = '';
if (empty($notooltip) && $user->hasRight('commande', 'lire')) {
if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
$label = $langs->trans("Order");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
$linkclose .= $dataparams.' class="'.$classfortooltip.'"';
$target_value = array('_self', '_blank', '_parent', '_top');
diff --git a/htdocs/commande/list.php b/htdocs/commande/list.php
index c665166b0af..27b9a05181b 100644
--- a/htdocs/commande/list.php
+++ b/htdocs/commande/list.php
@@ -893,19 +893,19 @@ if ($search_status <> '') {
if ($search_status == 1 && empty($conf->expedition->enabled)) {
$sql .= ' AND c.fk_statut IN (1,2)'; // If module expedition disabled, we include order with status 'sending in process' into 'validated'
} else {
- $sql .= ' AND c.fk_statut = '.((int) $search_status); // brouillon, validee, en cours, annulee
+ $sql .= ' AND c.fk_statut = '.((int) $search_status); // draft, validated, in process or canceled
}
}
- if ($search_status == -2) { // To process
+ if ($search_status == -2) { // "validated + in process"
//$sql.= ' AND c.fk_statut IN (1,2,3) AND c.facture = 0';
- $sql .= " AND ((c.fk_statut IN (1,2)) OR (c.fk_statut = 3 AND c.facture = 0))"; // If status is 2 and facture=1, it must be selected
+ $sql .= " AND (c.fk_statut IN (1,2))";
}
- if ($search_status == -3) { // To bill
+ if ($search_status == -3) { // "validated + in process + delivered"
//$sql.= ' AND c.fk_statut in (1,2,3)';
//$sql.= ' AND c.facture = 0'; // invoice not created
- $sql .= ' AND ((c.fk_statut IN (1,2)) OR (c.fk_statut = 3 AND c.facture = 0))'; // validated, in process or closed but not billed
+ $sql .= ' AND (c.fk_statut IN (1,2,3))'; // validated, in process or closed
}
- if ($search_status == -4) { // "validate and in progress"
+ if ($search_status == -4) { // "validate + in progress"
$sql .= ' AND (c.fk_statut IN (1,2))'; // validated, in process
}
}
diff --git a/htdocs/compta/bank/class/account.class.php b/htdocs/compta/bank/class/account.class.php
index 35b9dba1e54..f9a665640cd 100644
--- a/htdocs/compta/bank/class/account.class.php
+++ b/htdocs/compta/bank/class/account.class.php
@@ -1409,13 +1409,15 @@ class Account extends CommonObject
/**
* getTooltipContentArray
- * @param array $params params to construct tooltip data
- * @since v18
- * @return array
+ *
+ * @param array $params Params to construct tooltip data
+ * @since v18
+ * @return array
*/
public function getTooltipContentArray($params)
{
global $langs;
+ $langs->loadLangs(['banks', 'compta']);
include_once DOL_DOCUMENT_ROOT.'/core/lib/bank.lib.php';
$datas = array();
@@ -1461,6 +1463,7 @@ class Account extends CommonObject
public function getNomUrl($withpicto = 0, $mode = '', $option = '', $save_lastsearch_value = -1, $notooltip = 0)
{
global $conf, $langs, $user;
+
include_once DOL_DOCUMENT_ROOT.'/core/lib/bank.lib.php';
$result = '';
@@ -1474,11 +1477,15 @@ class Account extends CommonObject
];
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
- $linkclose = '"'.$dataparams.' title="'.dol_escape_htmltag($label, 1).'" class="'.$classfortooltip.'">';
+ $linkclose = '';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
+ $linkclose .= $dataparams.' class="'.$classfortooltip.'">';
$url = DOL_URL_ROOT.'/compta/bank/card.php?id='.$this->id;
if ($mode == 'transactions') {
@@ -1498,7 +1505,7 @@ class Account extends CommonObject
}
}
- $linkstart = '
';
if ($option == 'nolink') {
diff --git a/htdocs/compta/bank/class/paymentvarious.class.php b/htdocs/compta/bank/class/paymentvarious.class.php
index 705ff2d20ca..912f46e074a 100644
--- a/htdocs/compta/bank/class/paymentvarious.class.php
+++ b/htdocs/compta/bank/class/paymentvarious.class.php
@@ -828,4 +828,36 @@ class PaymentVarious extends CommonObject
$return .= ' ';
return $return;
}
+
+ /**
+ * Return General accounting account with defined length (used for product and miscellaneous)
+ *
+ * @param string $account General accounting account
+ * @return string String with defined length
+ */
+ public function lengthAccountg($account)
+ {
+ include_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
+
+ /*
+ if (isModEnabled('accounting')) {
+ $accountingaccount = new AccountingAccount($db);
+ $accountingaccount->fetch('', $valuetoshow, 1);
+ }*/
+
+ return length_accountg($account);
+ }
+
+ /**
+ * Return Auxiliary accounting account of thirdparties with defined length
+ *
+ * @param string $account Auxiliary accounting account
+ * @return string String with defined length
+ */
+ public function lengthAccounta($account)
+ {
+ include_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
+
+ return length_accounta($account);
+ }
}
diff --git a/htdocs/compta/bank/transfer.php b/htdocs/compta/bank/transfer.php
index 5f33afbae7f..724e09554db 100644
--- a/htdocs/compta/bank/transfer.php
+++ b/htdocs/compta/bank/transfer.php
@@ -346,7 +346,7 @@ for ($i = 1 ; $i < $MAXLINES; $i++) {
print ' ';
print '';
-};
+}
print '
';
print '';
diff --git a/htdocs/compta/bank/various_payment/card.php b/htdocs/compta/bank/various_payment/card.php
index 1eb552fd711..7fbf2774e85 100644
--- a/htdocs/compta/bank/various_payment/card.php
+++ b/htdocs/compta/bank/various_payment/card.php
@@ -1,6 +1,7 @@
* Copyright (C) 2018-2020 Frédéric France
+ * Copyright (C) 2023 Laurent Destailleur
*
* 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
@@ -71,6 +72,8 @@ $object = new PaymentVarious($db);
// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
$hookmanager->initHooks(array('variouscard', 'globalcard'));
+$permissiontoadd = $user->hasRight('banque', 'modifier');
+
/**
* Actions
@@ -83,14 +86,8 @@ if ($reshook < 0) {
}
if (empty($reshook)) {
- // Link to a project
- if ($action == 'classin' && $user->rights->banque->modifier) {
- $object->fetch($id);
- $object->setProject(GETPOST('projectid'));
- }
-
if ($cancel) {
- if ($action != 'addlink') {
+ if ($action != 'addlink' && $action != 'setaccountancy_code' && $action != 'setsubledger_account') {
$urltogo = $backtopage ? $backtopage : dol_buildpath('/compta/bank/various_payment/list.php', 1);
header("Location: ".$urltogo);
exit;
@@ -101,6 +98,12 @@ if (empty($reshook)) {
$action = '';
}
+ // Link to a project
+ if ($action == 'classin' && $permissiontoadd) {
+ $object->fetch($id);
+ $object->setProject(GETPOST('projectid', 'int'));
+ }
+
if ($action == 'add') {
$error = 0;
@@ -214,6 +217,22 @@ if (empty($reshook)) {
}
}
+ if ($action == 'setaccountancy_code') {
+ $db->begin();
+
+ $result = $object->fetch($id);
+
+ $object->accountancy_code = GETPOST('accountancy_code', 'alpha');
+
+ $res = $object->update($user);
+ if ($res > 0) {
+ $db->commit();
+ } else {
+ $db->rollback();
+ setEventMessages($object->error, $object->errors, 'errors');
+ }
+ }
+
if ($action == 'setsubledger_account') {
$db->begin();
@@ -236,7 +255,7 @@ if ($action == 'confirm_clone' && $confirm != 'yes') {
$action = '';
}
-if ($action == 'confirm_clone' && $confirm == 'yes' && ($user->rights->banque->modifier)) {
+if ($action == 'confirm_clone' && $confirm == 'yes' && $permissiontoadd) {
$db->begin();
$originalId = $id;
@@ -560,32 +579,25 @@ if ($id) {
// Project
if (isModEnabled('project')) {
$langs->load("projects");
- $morehtmlref .= $langs->trans('Project').' ';
- if ($user->rights->banque->modifier) {
+ //$morehtmlref .= ' ';
+ if ($permissiontoadd) {
+ $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
if ($action != 'classify') {
- $morehtmlref .= ''.img_edit($langs->transnoentitiesnoconv('SetProject')).' : ';
- }
- if ($action == 'classify') {
- //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1);
- $morehtmlref .= '';
- } else {
- $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1, '', 'maxwidth300');
+ $morehtmlref .= ''.img_edit($langs->transnoentitiesnoconv('SetProject')).' ';
}
+ $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, $object->socid, $object->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300');
} else {
if (!empty($object->fk_project)) {
$proj = new Project($db);
$proj->fetch($object->fk_project);
$morehtmlref .= $proj->getNomUrl(1);
- } else {
- $morehtmlref .= '';
+ if ($proj->title) {
+ $morehtmlref .= ' - '.dol_escape_htmltag($proj->title).' ';
+ }
}
}
}
+
$morehtmlref .= '';
$linkback = ''.$langs->trans("BackToList").' ';
@@ -622,25 +634,24 @@ if ($id) {
print ''.$langs->trans("Amount").' '.price($object->amount, 0, $langs, 1, -1, -1, $conf->currency).' ';
- // Accountancy code
- print '';
- print $langs->trans("AccountAccounting");
- print ' ';
+ // Account of Chart of account
+ $editvalue = '';
if (isModEnabled('accounting')) {
- $accountingaccount = new AccountingAccount($db);
- $accountingaccount->fetch('', $object->accountancy_code, 1);
-
- print $accountingaccount->getNomUrl(0, 1, 1, '', 1);
- } else {
- print $object->accountancy_code;
+ $editvalue = $formaccounting->select_account($object->accountancy_code, 'accountancy_code', 1, null, 1, 1);
}
+
+ print ' ';
+ print '';
+ print $form->editfieldkey('AccountAccounting', 'accountancy_code', $object->accountancy_code, $object, (!$alreadyaccounted && $permissiontoadd), 'string', '', 0);
+ print ' ';
+ print $form->editfieldval('AccountAccounting', 'accountancy_code', $object->accountancy_code, $object, (!$alreadyaccounted && $permissiontoadd), 'asis', $editvalue, 0, null, '', 1, 'lengthAccountg');
print ' ';
// Subledger account
print '';
- print $form->editfieldkey('SubledgerAccount', 'subledger_account', $object->subledger_account, $object, (!$alreadyaccounted && $user->rights->banque->modifier), 'string', '', 0);
+ print $form->editfieldkey('SubledgerAccount', 'subledger_account', $object->subledger_account, $object, (!$alreadyaccounted && $permissiontoadd), 'string', '', 0);
print ' ';
- print $form->editfieldval('SubledgerAccount', 'subledger_account', $object->subledger_account, $object, (!$alreadyaccounted && $user->rights->banque->modifier), 'string', '', 0);
+ print $form->editfieldval('SubledgerAccount', 'subledger_account', $object->subledger_account, $object, (!$alreadyaccounted && $permissiontoadd), 'string', '', 0, null, '', 1, 'lengthAccounta');
print ' ';
$bankaccountnotfound = 0;
@@ -689,13 +700,13 @@ if ($id) {
// Add button modify
// Clone
- if ($user->rights->banque->modifier) {
+ if ($permissiontoadd) {
print '";
}
// Delete
if (empty($object->rappro) || $bankaccountnotfound) {
- if (!empty($user->rights->banque->modifier)) {
+ if ($permissiontoadd) {
if ($alreadyaccounted) {
print '';
} else {
diff --git a/htdocs/compta/bank/various_payment/document.php b/htdocs/compta/bank/various_payment/document.php
index 43259727917..58e4d3680b9 100644
--- a/htdocs/compta/bank/various_payment/document.php
+++ b/htdocs/compta/bank/various_payment/document.php
@@ -100,7 +100,6 @@ if ($object->id) {
// Project
if (isModEnabled('project')) {
$langs->load("projects");
- $morehtmlref .= $langs->trans('Project').' : ';
if ($user->rights->banque->modifier && 0) {
if ($action != 'classify') {
$morehtmlref .= ''.img_edit($langs->transnoentitiesnoconv('SetProject')).' : ';
diff --git a/htdocs/compta/bank/various_payment/info.php b/htdocs/compta/bank/various_payment/info.php
index ca31915d2f4..06f243a9847 100644
--- a/htdocs/compta/bank/various_payment/info.php
+++ b/htdocs/compta/bank/various_payment/info.php
@@ -60,7 +60,6 @@ $morehtmlref = '';
// Project
if (isModEnabled('project')) {
$langs->load("projects");
- $morehtmlref .= $langs->trans('Project').' : ';
if ($user->rights->banque->modifier && 0) {
if ($action != 'classify') {
$morehtmlref .= '
'.img_edit($langs->transnoentitiesnoconv('SetProject')).' : ';
diff --git a/htdocs/compta/cashcontrol/cashcontrol_list.php b/htdocs/compta/cashcontrol/cashcontrol_list.php
index a050cf7ffec..1333a6b2346 100644
--- a/htdocs/compta/cashcontrol/cashcontrol_list.php
+++ b/htdocs/compta/cashcontrol/cashcontrol_list.php
@@ -622,8 +622,8 @@ while ($i < ($limit ? min($num, $limit) : $num)) {
//if (in_array($key, array('fk_soc', 'fk_user', 'fk_warehouse'))) $cssforfield = 'tdoverflowmax100';
if (!empty($arrayfields['t.'.$key]['checked'])) {
- print '
$key)) {
print ' title="'.dol_escape_htmltag($object->$key).'"';
}
print '>';
diff --git a/htdocs/compta/facture/agenda.php b/htdocs/compta/facture/agenda.php
index a7930660378..a452a8dd6aa 100644
--- a/htdocs/compta/facture/agenda.php
+++ b/htdocs/compta/facture/agenda.php
@@ -29,7 +29,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
// Load translation files required by the page
-$langs->loadLangs(array("facture", "other"));
+$langs->loadLangs(array("bills", "other"));
// Get parameters
$id = GETPOST('id', 'int');
diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php
index a4e7bd52430..5d69bb2b6dd 100644
--- a/htdocs/compta/facture/class/facture.class.php
+++ b/htdocs/compta/facture/class/facture.class.php
@@ -574,6 +574,7 @@ class Facture extends CommonInvoice
$this->type = self::TYPE_STANDARD;
}
$this->ref_client = trim($this->ref_client);
+ $this->ref_customer = trim($this->ref_customer);
$this->note_public = trim($this->note_public);
$this->note_private = trim($this->note_private);
$this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->ref));
@@ -591,8 +592,6 @@ class Facture extends CommonInvoice
// We do not add link to template invoice or next invoice will be linked to all generated invoices
//$this->linked_objects['facturerec'][0] = $this->fac_rec;
- $forceduedate = $this->calculate_date_lim_reglement();
-
// For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice
if ($_facrec->frequency > 0) {
dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when");
@@ -1897,10 +1896,11 @@ class Facture extends CommonInvoice
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
$linkclose = ($target ? ' target="'.$target.'"' : '');
if (empty($notooltip) && $user->hasRight("facture", "read")) {
@@ -1908,8 +1908,8 @@ class Facture extends CommonInvoice
$label = $langs->trans("Invoice");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= $dataparams.' title="'.dol_escape_htmltag($label, 1).'"';
- $linkclose .= ' class="'.$classfortooltip.'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
+ $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
}
$linkstart = 'contact_id = 0;
$actioncomm->code = 'AC_EMAIL';
- $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateOK';
+ $actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateOK (nbdays='.$nbdays.' paymentmode='.$paymentmode.' template='.$template.' forcerecipient='.$forcerecipient.')';
$actioncomm->note_private = $sendContent;
$actioncomm->fk_project = $tmpinvoice->fk_project;
$actioncomm->datep = dol_now();
@@ -5666,6 +5666,7 @@ class Facture extends CommonInvoice
$actioncomm->userownerid = $user->id; // Owner of action
// Fields when action is an email (content should be added into note)
$actioncomm->email_msgid = $cMailFile->msgid;
+ $actioncomm->email_subject = $sendTopic;
$actioncomm->email_from = $from;
$actioncomm->email_sender = '';
$actioncomm->email_to = $to;
diff --git a/htdocs/compta/facture/list.php b/htdocs/compta/facture/list.php
index 83b609f91f7..928ffcfc4bd 100644
--- a/htdocs/compta/facture/list.php
+++ b/htdocs/compta/facture/list.php
@@ -1952,7 +1952,7 @@ if ($resql) {
// Action column
if (!empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) {
- print ' ';
+ print ' ';
if (($massactionbutton || $massaction) && $contextpage != 'poslist') { // 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->id, $arrayofselected)) {
@@ -2430,7 +2430,7 @@ if ($resql) {
}
}
if (!empty($arrayfields['multicurrency_dynamount_payed']['checked'])) {
- print ' '.(!empty($multicurrency_totalpay) ?price($multicurrency_totalpay, 0, $langs) : ' ').' '; // TODO Use a denormalized field
+ print '
'.(!empty($multicurrency_totalpay) ? price($multicurrency_totalpay, 0, $langs) : ' ').' '; // TODO Use a denormalized field
if (!$i) {
$totalarray['nbfield']++;
}
@@ -2440,7 +2440,7 @@ if ($resql) {
if (!empty($arrayfields['multicurrency_rtp']['checked'])) {
print '
';
print (!empty($multicurrency_remaintopay) ? price($multicurrency_remaintopay, 0, $langs) : ' ');
- print ' '; // TODO Use a denormalized field
+ print ''; // TODO Use a denormalized field ?
if (!$i) {
$totalarray['nbfield']++;
}
@@ -2448,14 +2448,14 @@ if ($resql) {
// Total buying or cost price
if (!empty($arrayfields['total_pa']['checked'])) {
- print '
'.price($marginInfo['pa_total']).' ';
+ print '
'.price($marginInfo['pa_total'], 0, $langs, 1, -1, 'MT').' ';
if (!$i) {
$totalarray['nbfield']++;
}
}
// Total margin
if (!empty($arrayfields['total_margin']['checked'])) {
- print '
'.price($marginInfo['total_margin']).' ';
+ print '
'.price($marginInfo['total_margin'], 0, $langs, 1, -1, 'MT').' ';
if (!$i) {
$totalarray['nbfield']++;
}
@@ -2570,7 +2570,7 @@ if ($resql) {
// Action column (Show the massaction button only when this page is not opend from the Extended POS)
if (empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) {
- print '
';
+ print ' ';
if (($massactionbutton || $massaction) && $contextpage != 'poslist') { // 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->id, $arrayofselected)) {
diff --git a/htdocs/compta/facture/prelevement.php b/htdocs/compta/facture/prelevement.php
index 5f027960ee8..948bb42337a 100644
--- a/htdocs/compta/facture/prelevement.php
+++ b/htdocs/compta/facture/prelevement.php
@@ -693,6 +693,16 @@ if ($object->id > 0) {
$resteapayer = price2num($object->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
+ // Hook to change amount for other reasons, e.g. apply cash discount for payment before agreed date
+ $parameters = array('remaintopay' => $resteapayer);
+ $reshook = $hookmanager->executeHooks('finalizeAmountOfSupplierInvoice', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
+ if ($reshook > 0) {
+ print $hookmanager->resPrint;
+ if (!empty($remaintopay = $hookmanager->resArray['remaintopay'])) {
+ $resteapayer = $remaintopay;
+ }
+ }
+
// TODO Replace this by an include with same code to show already done payment visible in invoice card
print ' '.$langs->trans('RemainderToPay').' '.price($resteapayer, 1, '', 1, - 1, - 1, $conf->currency).' ';
diff --git a/htdocs/compta/paiement/card.php b/htdocs/compta/paiement/card.php
index 39dac0352e6..4fb3cfa41aa 100644
--- a/htdocs/compta/paiement/card.php
+++ b/htdocs/compta/paiement/card.php
@@ -36,6 +36,9 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
if (isModEnabled("banque")) {
require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
}
+if (!empty($conf->margin->enabled)) {
+ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php';
+}
// Load translation files required by the page
$langs->loadLangs(array('bills', 'banks', 'companies'));
@@ -432,6 +435,10 @@ if ($resql) {
if (isModEnabled('multicompany') && !empty($conf->global->MULTICOMPANY_INVOICE_SHARING_ENABLED)) {
print '
'.$langs->trans('Entity').' ';
}
+ //Add Margin
+ if (!empty($conf->margin->enabled) && getDolGlobalInt('MARGIN_SHOW_MARGIN_ON_PAYMENT')) {
+ print '
'.$langs->trans('Margin').' ';
+ }
print '
'.$langs->trans('ExpectedToPay').' ';
print '
'.$langs->trans('PayedByThisPayment').' ';
print '
'.$langs->trans('RemainderToPay').' ';
@@ -447,6 +454,14 @@ if ($resql) {
$invoice = new Facture($db);
$invoice->fetch($objp->facid);
+ // Add Margin
+ if (!empty($conf->margin->enabled) && getDolGlobalInt('MARGIN_SHOW_MARGIN_ON_PAYMENT')) {
+ $formmargin = new FormMargin($db);
+ $marginInfo = array();
+ $invoice->fetch_lines();
+ $marginInfo = $formmargin->getMarginInfosArray($invoice);
+ }
+
$paiement = $invoice->getSommePaiement();
$creditnotes = $invoice->getSumCreditNotesUsed();
$deposits = $invoice->getSumDepositsUsed();
@@ -472,6 +487,12 @@ if ($resql) {
print $mc->label;
print '';
}
+
+ // Add margin
+ if (!empty($conf->margin->enabled) && getDolGlobalInt('MARGIN_SHOW_MARGIN_ON_PAYMENT')) {
+ print '
'.price($marginInfo['total_margin']).' ';
+ }
+
// Expected to pay
print '
'.price($objp->total_ttc).' ';
diff --git a/htdocs/compta/paiement/class/paiement.class.php b/htdocs/compta/paiement/class/paiement.class.php
index e16834e5707..f573ec5be56 100644
--- a/htdocs/compta/paiement/class/paiement.class.php
+++ b/htdocs/compta/paiement/class/paiement.class.php
@@ -11,6 +11,7 @@
* Copyright (C) 2018-2022 Frédéric France
* Copyright (C) 2020 Andreu Bisquerra Gaya
* Copyright (C) 2021 OpenDsi
+ * Copyright (C) 2023 Joachim Kueter
*
* 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
@@ -382,7 +383,19 @@ class Paiement extends CommonObject
if (!in_array($invoice->type, $affected_types)) {
dol_syslog("Invoice ".$facid." is not a standard, nor replacement invoice, nor credit note, nor deposit invoice, nor situation invoice. We do nothing more.");
} elseif ($remaintopay) {
- dol_syslog("Remain to pay for invoice ".$facid." not null. We do nothing more.");
+ // hook to have an option to automatically close a closable invoice with less payment than the total amount (e.g. agreed cash discount terms)
+ global $hookmanager;
+ $hookmanager->initHooks(array('paymentdao'));
+ $parameters = array('facid' => $facid, 'invoice' => $invoice, 'remaintopay' => $remaintopay);
+ $action = 'CLOSEPAIDINVOICE';
+ $reshook = $hookmanager->executeHooks('createPayment', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
+ if ($reshook < 0) {
+ $this->errors[] = $hookmanager->error;
+ $this->error = $hookmanager->error;
+ $error++;
+ } elseif ($reshook == 0) {
+ dol_syslog("Remain to pay for invoice " . $facid . " not null. We do nothing more.");
+ }
// } else if ($mustwait) dol_syslog("There is ".$mustwait." differed payment to process, we do nothing more.");
} else {
// If invoice is a down payment, we also convert down payment to discount
diff --git a/htdocs/compta/prelevement/line.php b/htdocs/compta/prelevement/line.php
index b668880e9d3..7809e894af8 100644
--- a/htdocs/compta/prelevement/line.php
+++ b/htdocs/compta/prelevement/line.php
@@ -119,7 +119,13 @@ if ($action == 'confirm_rejet') {
* View
*/
-$invoicestatic = new Facture($db);
+if ($type == 'bank-transfer') {
+ require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
+ $invoicestatic = new FactureFournisseur($db);
+} else {
+ require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
+ $invoicestatic = new Facture($db);
+}
$title = $langs->trans("WithdrawalsLine");
if ($type == 'bank-transfer') {
@@ -318,7 +324,11 @@ if ($id) {
print ''.$obj->ref." \n";
}
- print '';
+ if ($type == 'bank-transfer') {
+ print ' ';
+ } else {
+ print ' ';
+ }
print img_object($langs->trans("ShowCompany"), "company").' '.$obj->name." \n";
print ''.price($obj->total_ttc)." \n";
diff --git a/htdocs/contact/card.php b/htdocs/contact/card.php
index 7197eabb19c..cbdce0c5d04 100644
--- a/htdocs/contact/card.php
+++ b/htdocs/contact/card.php
@@ -680,6 +680,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
print ' lastname).'" autofocus="autofocus"> ';
print ' ';
+ // Firstname
print '';
print '';
print $form->textwithpicto($langs->trans("Firstname"), $langs->trans("KeepEmptyIfGenericAddress")).' ';
diff --git a/htdocs/contact/class/contact.class.php b/htdocs/contact/class/contact.class.php
index 379cda22d43..eddba0b5ad3 100644
--- a/htdocs/contact/class/contact.class.php
+++ b/htdocs/contact/class/contact.class.php
@@ -1495,10 +1495,11 @@ class Contact extends CommonObject
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
$url = DOL_URL_ROOT.'/contact/card.php?id='.$this->id;
@@ -1521,7 +1522,7 @@ class Contact extends CommonObject
$label = $langs->trans("ShowContact");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
$linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
}
diff --git a/htdocs/contact/consumption.php b/htdocs/contact/consumption.php
index 93acb2ab463..8efae5a836d 100644
--- a/htdocs/contact/consumption.php
+++ b/htdocs/contact/consumption.php
@@ -377,7 +377,7 @@ if ($sql_select) {
$num = $db->num_rows($resql);
- $param = "&socid=".urlencode($socid)."&type_element=".urlencode($type_element);
+ $param = "&socid=".urlencode($socid)."&type_element=".urlencode($type_element)."&id=".urlencode($id);
if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) {
$param .= '&contextpage='.urlencode($contextpage);
}
diff --git a/htdocs/contact/perso.php b/htdocs/contact/perso.php
index 0fd23753fdf..ba5fde87468 100644
--- a/htdocs/contact/perso.php
+++ b/htdocs/contact/perso.php
@@ -27,6 +27,7 @@
// Load Dolibarr environment
require '../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/contact.lib.php';
// Load translation files required by the page
@@ -42,6 +43,9 @@ if ($user->socid) {
$result = restrictedArea($user, 'contact', $id, 'socpeople&societe');
$object = new Contact($db);
+$errors = array();
+
+
/*
* Action
*/
@@ -123,6 +127,7 @@ $help_url = 'EN:Module_Third_Parties|FR:Module_Tiers|ES:Empresas';
llxHeader('', $title, $help_url);
$form = new Form($db);
+$formcompany = new FormCompany($db);
$object->fetch($id, $user);
@@ -143,38 +148,13 @@ if ($action == 'edit') {
print '';
// Ref
- print ''.$langs->trans("Ref").' ';
+ print ' '.$langs->trans("Ref").' ';
print $object->id;
print ' ';
- // Photo
- print '';
- print $form->showphoto('contact', $object)."\n";
- if ($object->photo) {
- print " \n";
- }
-
- print '';
-
- print ' ';
-
// Name
- print ''.$langs->trans("Lastname").' / '.$langs->trans("Label").' '.$object->lastname.' ';
- print ''.$langs->trans("Firstname").' '.$object->firstname.' ';
+ print ''.$langs->trans("Lastname").' / '.$langs->trans("Label").' '.$object->lastname.' ';
+ print ''.$langs->trans("Firstname").' '.$object->firstname.' ';
// Company
if (empty($conf->global->SOCIETE_DISABLE_CONTACTS)) {
@@ -182,31 +162,62 @@ if ($action == 'edit') {
$objsoc = new Societe($db);
$objsoc->fetch($object->socid);
- print ''.$langs->trans("ThirdParty").' '.$objsoc->getNomUrl(1).' ';
+ print ''.$langs->trans("ThirdParty").' '.$objsoc->getNomUrl(1).' ';
} else {
- print ''.$langs->trans("ThirdParty").' ';
+ print ' '.$langs->trans("ThirdParty").' ';
print $langs->trans("ContactNotLinkedToCompany");
print ' ';
}
}
// Civility
- print ''.$langs->trans("UserTitle").' ';
+ print ' '.$langs->trans("UserTitle").' ';
print $object->getCivilityLabel();
+ //print $formcompany->select_civility(GETPOSTISSET("civility_code") ? GETPOST("civility_code", 'alpha') : $object->civility_code, 'civility_code');
print ' ';
+ // Photo
+ print '';
+ print ''.$form->editfieldkey('PhotoFile', 'photoinput', '', $object, 0).' ';
+ print '';
+ if ($object->photo) {
+ print $form->showphoto('contact', $object);
+ }
+ $caneditfield = 1;
+ if ($caneditfield) {
+ if ($object->photo) {
+ print " \n";
+ }
+ print '';
+ }
+ print ' ';
+ print ' ';
+
// Date To Birth
print ''.$langs->trans("DateOfBirth").' ';
$form = new Form($db);
print $form->selectDate($object->birthday, 'birthday', 0, 0, 1, "perso", 1, 0);
- print ' ';
-
- print ''.$langs->trans("Alert").': ';
+ print ' ';
+ print ''.$langs->trans("BirthdayAlert").': ';
if (!empty($object->birthday_alert)) {
- print ' ';
+ print ' ';
} else {
- print ' ';
+ print ' ';
}
+ print '';
print ' ';
print "
";
diff --git a/htdocs/contrat/class/contrat.class.php b/htdocs/contrat/class/contrat.class.php
index 8e6c2ea0161..e05aeb678ee 100644
--- a/htdocs/contrat/class/contrat.class.php
+++ b/htdocs/contrat/class/contrat.class.php
@@ -2075,19 +2075,19 @@ class Contrat extends CommonObject
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
-
$linkclose = '';
if (empty($notooltip) && $user->hasRight('contrat', 'lire')) {
if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
$label = $langs->trans("ShowContract");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
$linkclose .= $dataparams.' class="'.$classfortooltip.'"';
}
$linkstart = ' $this->element,
];
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
}
- $link = ' ';
+
+ $link = ' ';
$linkend = ' ';
$picto = 'service';
diff --git a/htdocs/core/ajax/ajaxcompanies.php b/htdocs/core/ajax/ajaxcompanies.php
index ef0d7303715..086d20e7967 100644
--- a/htdocs/core/ajax/ajaxcompanies.php
+++ b/htdocs/core/ajax/ajaxcompanies.php
@@ -31,6 +31,14 @@ if (!defined('NOREQUIRESOC')) define('NOREQUIRESOC', '1');
// Load Dolibarr environment
require '../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
+
+$object = new Societe($db);
+
+$usesublevelpermission = '';
+
+// Security check
+restrictedArea($user, $object->module, $object, $object->table_element, $usesublevelpermission);
/*
@@ -54,9 +62,9 @@ if (GETPOST('newcompany') || GETPOST('socid', 'int') || GETPOST('id_fourn')) {
$return_arr = array();
// Define filter on text typed
- $socid = $_GET['newcompany'] ? $_GET['newcompany'] : '';
- if (!$socid) $socid = $_GET['socid'] ? $_GET['socid'] : '';
- if (!$socid) $socid = $_GET['id_fourn'] ? $_GET['id_fourn'] : '';
+ $socid = GETPOST('newcompany');
+ if (!$socid) $socid = GETPOST('socid');
+ if (!$socid) $socid = GETPOST('id_fourn');
$sql = "SELECT s.rowid, s.nom, s.name_alias, s.code_client, s.code_fournisseur, s.address, s.zip, s.town, s.email, s.siren, s.siret, s.ape, s.idprof4, s.client, s.fournisseur, s.datec, s.logo";
$sql .= " , c.label as country, d.nom as departement";
@@ -68,17 +76,22 @@ if (GETPOST('newcompany') || GETPOST('socid', 'int') || GETPOST('id_fourn')) {
$sql .= " AND (";
// Add criteria on name/code
if (!empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE)) { // Can use index
- $sql .= "s.nom LIKE '".$db->escape($socid)."%'";
- $sql .= " OR s.code_client LIKE '".$db->escape($socid)."%'";
- $sql .= " OR s.code_fournisseur LIKE '".$db->escape($socid)."%'";
+ $sql .= "s.nom LIKE '".$db->escape($db->escapeforlike($socid))."%'";
+ $sql .= " OR s.code_client LIKE '".$db->escape($db->escapeforlike($socid))."%'";
+ $sql .= " OR s.code_fournisseur LIKE '".$db->escape($db->escapeforlike($socid))."%'";
} else {
- $sql .= "s.nom LIKE '%".$db->escape($socid)."%'";
- $sql .= " OR s.code_client LIKE '%".$db->escape($socid)."%'";
- $sql .= " OR s.code_fournisseur LIKE '%".$db->escape($socid)."%'";
+ $sql .= "s.nom LIKE '%".$db->escape($db->escapeforlike($socid))."%'";
+ $sql .= " OR s.code_client LIKE '%".$db->escape($db->escapeforlike($socid))."%'";
+ $sql .= " OR s.code_fournisseur LIKE '%".$db->escape($db->escapeforlike($socid))."%'";
+ }
+ if (!empty($conf->global->SOCIETE_ALLOW_SEARCH_ON_ROWID)) {
+ $sql .= " OR s.rowid = ".((int) $socid);
}
- if (!empty($conf->global->SOCIETE_ALLOW_SEARCH_ON_ROWID)) $sql .= " OR s.rowid = '".$db->escape($socid)."'";
$sql .= ")";
}
+ if ($user->socid > 0) {
+ $sql .= " AND s.rowid = ".((int) $user->socid);
+ }
//if (GETPOST("filter")) $sql.= " AND (".GETPOST("filter", "alpha").")"; // Add other filters
$sql .= " ORDER BY s.nom ASC";
diff --git a/htdocs/core/ajax/ajaxdirpreview.php b/htdocs/core/ajax/ajaxdirpreview.php
index 2f763c4e576..b5f772212b9 100644
--- a/htdocs/core/ajax/ajaxdirpreview.php
+++ b/htdocs/core/ajax/ajaxdirpreview.php
@@ -117,14 +117,14 @@ if (empty($url)) { // autoset $url but it is better to have it defined before in
// Load translation files required by the page
$langs->loadLangs(array("ecm", "companies", "other"));
+if (empty($modulepart)) {
+ $modulepart = $module;
+}
+
// Security check
if ($user->socid > 0) {
$socid = $user->socid;
}
-
-//print 'xxx'.$upload_dir;
-
-// Security:
// On interdit les remontees de repertoire ainsi que les pipe dans les noms de fichiers.
if (preg_match('/\.\./', $upload_dir) || preg_match('/[<>|]/', $upload_dir)) {
dol_syslog("Refused to deliver file ".$upload_dir);
@@ -132,11 +132,6 @@ if (preg_match('/\.\./', $upload_dir) || preg_match('/[<>|]/', $upload_dir)) {
dol_print_error(0, $langs->trans("ErrorFileNameInvalid", $upload_dir));
exit;
}
-
-if (empty($modulepart)) {
- $modulepart = $module;
-}
-
// Check permissions
if ($modulepart == 'ecm') {
if (!$user->hasRight('ecm', 'read')) {
diff --git a/htdocs/core/ajax/ajaxdirtree.php b/htdocs/core/ajax/ajaxdirtree.php
index 61fdabb70c3..5c281eff5ca 100644
--- a/htdocs/core/ajax/ajaxdirtree.php
+++ b/htdocs/core/ajax/ajaxdirtree.php
@@ -103,7 +103,7 @@ if (empty($modulepart)) {
$modulepart = $module;
}
-// Check permissions
+// Security check
if ($modulepart == 'ecm') {
if (!$user->hasRight('ecm', 'read')) {
accessforbidden();
diff --git a/htdocs/core/ajax/ajaxinvoiceline.php b/htdocs/core/ajax/ajaxinvoiceline.php
index 432ad15b151..f575bf326ac 100644
--- a/htdocs/core/ajax/ajaxinvoiceline.php
+++ b/htdocs/core/ajax/ajaxinvoiceline.php
@@ -39,7 +39,6 @@ $action = GETPOST('action', 'aZ09');
$htmlname = GETPOST('htmlname', 'alpha');
-
// Security check
restrictedArea($user, 'facture', $invoice_id, '', '', 'fk_soc', 'rowid');
diff --git a/htdocs/core/ajax/ajaxtooltip.php b/htdocs/core/ajax/ajaxtooltip.php
index 8652908eced..33cdfb4831f 100644
--- a/htdocs/core/ajax/ajaxtooltip.php
+++ b/htdocs/core/ajax/ajaxtooltip.php
@@ -1,5 +1,5 @@
+/* Copyright (C) 2007-2023 Laurent Destailleur
* Copyright (C) 2018-2023 Frédéric France
*
* This program is free software; you can redistribute it and/or modify
@@ -22,7 +22,6 @@
* \brief This script returns content of tooltip
*/
-
if (!defined('NOTOKENRENEWAL')) {
define('NOTOKENRENEWAL', 1); // Disables token renewal
}
@@ -36,17 +35,13 @@ if (!defined('NOREQUIREAJAX')) {
define('NOREQUIREAJAX', '1');
}
include '../../main.inc.php';
-include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
-top_httphead();
-// opensurvey as aZ09 id
+
$id = GETPOST('id', 'aZ09');
-$objecttype = GETPOST('objecttype', 'aZ09');
+$objecttype = GETPOST('objecttype', 'aZ09arobase'); // 'module' or 'myobject@mymodule', 'mymodule_myobject'
-$html = '';
-$regs = array();
$params = array();
if (GETPOSTISSET('infologin')) {
$params['infologin'] = GETPOST('infologin', 'int');
@@ -54,182 +49,42 @@ if (GETPOSTISSET('infologin')) {
if (GETPOSTISSET('option')) {
$params['option'] = GETPOST('option', 'restricthtml');
}
-// If we ask a resource form external module (instead of default path)
-if (preg_match('/^([^@]+)@([^@]+)$/i', $objecttype, $regs)) {
- $myobject = $regs[1];
- $module = $regs[2];
-} else {
- // Parse $objecttype (ex: project_task)
- $module = $myobject = $objecttype;
- if (preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
- $module = $regs[1];
- $myobject = $regs[2];
- }
-}
-
-// Generic case for $classpath
-$classpath = $module.'/class';
-
-// Special cases, to work with non standard path
-if ($objecttype == 'facture' || $objecttype == 'invoice') {
- $langs->load('bills');
- $classpath = 'compta/facture/class';
- $module = 'facture';
- $myobject = 'facture';
-} elseif ($objecttype == 'bank_account') {
- $langs->loadLangs(['banks', 'compta']);
- $classpath = 'compta/bank/class';
- $module = 'banque';
- $myobject = 'account';
-} elseif ($objecttype == 'category') {
- $langs->loadLangs(['categories']);
- $classpath = 'categories/class';
- $module = 'categorie';
- $myobject = 'categorie';
-} elseif ($objecttype == 'commande' || $objecttype == 'order') {
- $langs->load('orders');
- $classpath = 'commande/class';
- $module = 'commande';
- $myobject = 'commande';
-} elseif ($objecttype == 'propal') {
- $langs->load('propal');
- $classpath = 'comm/propal/class';
-} elseif ($objecttype == 'action') {
- $langs->load('agenda');
- $classpath = 'comm/action/class';
- $module = 'agenda';
- $myobject = 'actioncomm';
-} elseif ($objecttype == 'supplier_proposal') {
- $langs->load('supplier_proposal');
- $classpath = 'supplier_proposal/class';
-} elseif ($objecttype == 'shipping') {
- $langs->load('sendings');
- $classpath = 'expedition/class';
- $myobject = 'expedition';
- $module = 'expedition_bon';
-} elseif ($objecttype == 'delivery') {
- $langs->load('deliveries');
- $classpath = 'delivery/class';
- $myobject = 'delivery';
- $module = 'delivery_note';
-} elseif ($objecttype == 'contract') {
- $langs->load('contracts');
- $classpath = 'contrat/class';
- $module = 'contrat';
- $myobject = 'contrat';
-} elseif ($objecttype == 'member') {
- $classpath = 'adherents/class';
- $module = 'adherent';
- $myobject = 'adherent';
-} elseif ($objecttype == 'fichinter') {
- $langs->load('interventions');
- $classpath = 'fichinter/class';
- $module = 'ficheinter';
- $myobject = 'fichinter';
-} elseif ($objecttype == 'project') {
- $langs->load('projects');
- $classpath = 'projet/class';
- $module = 'projet';
-} elseif ($objecttype == 'project_task') {
- $classpath = 'projet/class';
- $module = 'projet';
- $myobject = 'task';
-} elseif ($objecttype == 'stock') {
- $classpath = 'product/stock/class';
- $module = 'stock';
- $myobject = 'stock';
-} elseif ($objecttype == 'inventory') {
- $classpath = 'product/inventory/class';
- $module = 'stock';
- $myobject = 'inventory';
-} elseif ($objecttype == 'mo') {
- $classpath = 'mrp/class';
- $module = 'mrp';
- $myobject = 'mo';
-} elseif ($objecttype == 'productlot') {
- $classpath = 'product/stock/class';
- $module = 'stock';
- $myobject = 'productlot';
-} elseif ($objecttype == 'usergroup') {
- $classpath = 'user/class';
- $module = 'user';
- $myobject = 'usergroup';
-} elseif ($objecttype == 'dolresource') {
- $classpath = 'resource/class';
- $module = 'resource';
- $myobject = 'dolresource';
-} elseif ($objecttype == 'opensurvey_sondage') {
- $classpath = 'opensurvey/class';
- $module = 'opensurvey';
- $myobject = 'opensurveysondage';
-} elseif ($objecttype == 'knowledgerecord') {
- $classpath = 'knowledgemanagement/class';
- $module = 'knowledgemanagement';
- $myobject = 'knowledgerecord';
-}
-
-// Generic case for $classfile and $classname
-$classfile = strtolower($myobject);
-$classname = ucfirst($myobject);
-
-if ($objecttype == 'invoice_supplier') {
- $classfile = 'fournisseur.facture';
- $classname = 'FactureFournisseur';
- $classpath = 'fourn/class';
- $module = 'fournisseur';
-} elseif ($objecttype == 'order_supplier') {
- $classfile = 'fournisseur.commande';
- $classname = 'CommandeFournisseur';
- $classpath = 'fourn/class';
- $module = 'fournisseur';
-} elseif ($objecttype == 'supplier_proposal') {
- $classfile = 'supplier_proposal';
- $classname = 'SupplierProposal';
- $classpath = 'supplier_proposal/class';
- $module = 'supplier_proposal';
-} elseif ($objecttype == 'stock') {
- $classpath = 'product/stock/class';
- $classfile = 'entrepot';
- $classname = 'Entrepot';
-} elseif ($objecttype == 'facturerec') {
- $classpath = 'compta/facture/class';
- $classfile = 'facture-rec';
- $classname = 'FactureRec';
- $module = 'facture';
-} elseif ($objecttype == 'mailing') {
- $classpath = 'comm/mailing/class';
- $classfile = 'mailing';
- $classname = 'Mailing';
-} elseif ($objecttype == 'adherent_type') {
- $classpath = 'adherents/class';
- $classfile = 'adherent_type';
- $module = 'adherent';
- $myobject = 'adherent_type';
- $classname = 'AdherentType';
-} elseif ($objecttype == 'contact') {
- $module = 'societe';
-} elseif ($objecttype == 'salary') {
- $classpath = 'salaries/class';
- $module = 'salaries';
-}
-// print "objecttype=".$objecttype." module=".$module." subelement=".$subelement." classfile=".$classfile." classname=".$classname." classpath=".$classpath." ";
-
-if (isModEnabled($module)) {
- $res = dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
- if ($res) {
- if (class_exists($classname)) {
- $object = new $classname($db);
- $res = $object->fetch($id);
- if ($res > 0) {
- $html = $object->getTooltipContent($params);
- } elseif ($res == 0) {
- $html = $langs->trans('Deleted');
- }
- unset($object);
- } else {
- dol_syslog("Class with classname ".$classname." is unknown even after the include", LOG_ERR);
- }
+
+// Load object according to $element
+$object = fetchObjectByElement($id, $objecttype);
+if (empty($object->element)) {
+ httponly_accessforbidden('Failed to get object with fetchObjectByElement(id='.$id.', objectype='.$objecttype.')');
+}
+
+$module = $object->module;
+$element = $object->element;
+
+$usesublevelpermission = ($module != $element ? $element : '');
+if ($usesublevelpermission && !isset($user->rights->$module->$element)) { // There is no permission on object defined, we will check permission on module directly
+ $usesublevelpermission = '';
+}
+
+//print $object->id.' - '.$object->module.' - '.$object->element.' - '.$object->table_element.' - '.$usesublevelpermission."\n";
+
+// Security check
+restrictedArea($user, $object->module, $object, $object->table_element, $usesublevelpermission);
+
+
+/*
+ * View
+ */
+
+top_httphead();
+
+$html = '';
+
+if (is_object($object)) {
+ if ($object->id > 0 || !empty($object->ref)) {
+ $html = $object->getTooltipContent($params);
+ } elseif ($res == 0) {
+ $html = $langs->trans('Deleted');
}
+ unset($object);
}
print $html;
diff --git a/htdocs/core/ajax/bankconciliate.php b/htdocs/core/ajax/bankconciliate.php
index 5407c30545d..c340dd47a06 100644
--- a/htdocs/core/ajax/bankconciliate.php
+++ b/htdocs/core/ajax/bankconciliate.php
@@ -44,6 +44,9 @@ require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
$action = GETPOST('action', 'aZ09');
+// Security check
+// Checks are done later
+
/*
* View
diff --git a/htdocs/core/ajax/box.php b/htdocs/core/ajax/box.php
index a6a93c69f68..621d6878098 100644
--- a/htdocs/core/ajax/box.php
+++ b/htdocs/core/ajax/box.php
@@ -18,7 +18,7 @@
/**
* \file htdocs/core/ajax/box.php
- * \brief File to return Ajax response on Box move or close
+ * \brief File to return Ajax response on a Box move or close
*/
if (!defined('NOTOKENRENEWAL')) {
@@ -46,16 +46,16 @@ $boxorder = GETPOST('boxorder');
$zone = GETPOST('zone', 'int');
$userid = GETPOST('userid', 'int');
+// Security check
+if ($userid != $user->id) {
+ httponly_accessforbidden('Bad userid parameter. Must match logged user.');
+}
+
/*
* View
*/
-// Ajout directives pour resoudre bug IE
-//header('Cache-Control: Public, must-revalidate');
-//header('Pragma: public');
-
-//top_htmlhead("", "", 1); // Replaced with top_httphead. An ajax page does not need html header.
top_httphead();
print ''."\n";
diff --git a/htdocs/core/ajax/check_notifications.php b/htdocs/core/ajax/check_notifications.php
index 344a2b19229..18c1a1ece79 100644
--- a/htdocs/core/ajax/check_notifications.php
+++ b/htdocs/core/ajax/check_notifications.php
@@ -44,6 +44,9 @@ $time = dol_now();
$action = GETPOST('action', 'aZ09');
$listofreminderids = GETPOST('listofreminderids', 'aZ09');
+// Security check
+// No permission check at top, but action later are all done with a test on $user->id.
+
/*
* Actions
@@ -68,6 +71,7 @@ if ($action == 'stopreminder') {
// Clean database
$sql = 'DELETE FROM '.MAIN_DB_PREFIX.'actioncomm_reminder';
$sql .= " WHERE dateremind < '".$db->idate(dol_time_plus_duree(dol_now(), -1, 'm'))."'";
+ $sql .= " AND fk_user = ".((int) $user->id).' AND entity = '.((int) $conf->entity);
$resql = $db->query($sql);
if (!$resql) {
dol_print_error($db);
@@ -124,18 +128,10 @@ if (empty($_SESSION['auto_check_events_not_before']) || $time >= $_SESSION['auto
$sql = 'SELECT a.id as id_agenda, a.code, a.datep, a.label, a.location, ar.rowid as id_reminder, ar.dateremind, ar.fk_user as id_user_reminder';
$sql .= ' FROM '.MAIN_DB_PREFIX.'actioncomm as a';
- if (!empty($user->conf->MAIN_USER_WANT_ALL_EVENTS_NOTIFICATIONS)) {
- $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'actioncomm_reminder as ar ON a.id = ar.fk_actioncomm AND ar.fk_user = '.((int) $user->id);
- $sql .= ' WHERE a.code <> "AC_OTH_AUTO"';
- $sql .= ' AND (';
- $sql .= " ar.typeremind = 'browser' AND ar.dateremind < '".$db->idate(dol_now())."' AND ar.status = 0 AND ar.entity = ".$conf->entity;
- $sql .= ' )';
- } else {
- $sql .= ' JOIN '.MAIN_DB_PREFIX.'actioncomm_reminder as ar ON a.id = ar.fk_actioncomm AND ar.fk_user = '.((int) $user->id);
- $sql .= " AND ar.typeremind = 'browser' AND ar.dateremind < '".$db->idate(dol_now())."' AND ar.status = 0 AND ar.entity = ".$conf->entity;
- }
+ $sql .= ' INNER JOIN '.MAIN_DB_PREFIX.'actioncomm_reminder as ar ON a.id = ar.fk_actioncomm AND ar.fk_user = '.((int) $user->id);
+ $sql .= " AND ar.typeremind = 'browser' AND ar.dateremind < '".$db->idate(dol_now())."' AND ar.status = 0 AND ar.entity = ".((int) $conf->entity); // No sharing of entity for alerts
$sql .= $db->order('datep', 'ASC');
- $sql .= ' LIMIT 10'; // Avoid too many notification at once
+ $sql .= $db->plimit(10); // Avoid too many notification at once
$resql = $db->query($sql);
if ($resql) {
diff --git a/htdocs/core/ajax/constantonoff.php b/htdocs/core/ajax/constantonoff.php
index 3a8ffda3ae6..603de7408c0 100644
--- a/htdocs/core/ajax/constantonoff.php
+++ b/htdocs/core/ajax/constantonoff.php
@@ -52,28 +52,26 @@ $name = GETPOST('name', 'alpha');
$entity = GETPOST('entity', 'int');
$value = (GETPOST('value', 'aZ09') != '' ? GETPOST('value', 'aZ09') : 1);
+// Security check
+if (empty($user->admin)) {
+ httponly_accessforbidden('This ajax component can be called by admin user only');
+}
+
/*
* View
*/
-// Ajout directives pour resoudre bug IE
-//header('Cache-Control: Public, must-revalidate');
-//header('Pragma: public');
-
-//top_htmlhead("", "", 1); // Replaced with top_httphead. An ajax page does not need html header.
top_httphead();
//print ''."\n";
// Registering the new value of constant
if (!empty($action) && !empty($name)) {
- if ($user->admin) {
- if ($action == 'set') {
- dolibarr_set_const($db, $name, $value, 'chaine', 0, '', $entity);
- } elseif ($action == 'del') {
- dolibarr_del_const($db, $name, $entity);
- }
+ if ($action == 'set') {
+ dolibarr_set_const($db, $name, $value, 'chaine', 0, '', $entity);
+ } elseif ($action == 'del') {
+ dolibarr_del_const($db, $name, $entity);
}
} else {
http_response_code(403);
diff --git a/htdocs/core/ajax/extraparams.php b/htdocs/core/ajax/extraparams.php
index ccd8d1bc2ce..80f280e7a06 100644
--- a/htdocs/core/ajax/extraparams.php
+++ b/htdocs/core/ajax/extraparams.php
@@ -17,7 +17,8 @@
/**
* \file /htdocs/core/ajax/extraparams.php
- * \brief File to make Ajax action on setting extra parameters of elements
+ * \brief File to make Ajax action on setting extra parameters of elements.
+ * Called bu bloc_showhide.tpl.php, itself called when MAIN_DISABLE_CONTACTS_TAB or MAIN_DISABLE_NOTES_TAB are set
*/
if (!defined('NOTOKENRENEWAL')) {
@@ -39,10 +40,29 @@ if (!defined('NOREQUIRESOC')) {
include '../../main.inc.php';
$id = GETPOST('id', 'int');
-$element = GETPOST('element', 'alpha');
+$element = GETPOST('element', 'aZ09arobase');
$htmlelement = GETPOST('htmlelement', 'alpha');
$type = GETPOST('type', 'alpha');
+// Load object according to $id and $element
+$object = fetchObjectByElement($id, $element);
+
+$module = $object->module;
+$element = $object->element;
+$usesublevelpermission = ($module != $element ? $element : '');
+if ($usesublevelpermission && !isset($user->rights->$module->$element)) { // There is no permission on object defined, we will check permission on module directly
+ $usesublevelpermission = '';
+}
+
+//print $object->id.' - '.$object->module.' - '.$object->element.' - '.$object->table_element.' - '.$usesublevelpermission."\n";
+
+// Security check
+$result = restrictedArea($user, $object->module, $object, $object->table_element, $usesublevelpermission, 'fk_soc', 'rowid', 0, 1); // Call with mode return
+if (!$result) {
+ httponly_accessforbidden('Not allowed by restrictArea');
+}
+
+
/*
* View
*/
@@ -57,47 +77,10 @@ if (!empty($id) && !empty($element) && !empty($htmlelement) && !empty($type)) {
dol_syslog("AjaxSetExtraParameters id=".$id." element=".$element." htmlelement=".$htmlelement." type=".$type." value=".$value, LOG_DEBUG);
- $classpath = $subelement = $element;
+ if (is_object($object)) {
+ $params[$htmlelement] = array($type => $value);
+ $object->extraparams = array_merge($object->extraparams, $params);
- // For compatibility
- if ($element == 'order' || $element == 'commande') {
- $classpath = $subelement = 'commande';
- } elseif ($element == 'propal') {
- $classpath = 'comm/propal';
- $subelement = 'propal';
- } elseif ($element == 'facture') {
- $classpath = 'compta/facture';
- $subelement = 'facture';
- } elseif ($element == 'contract') {
- $classpath = $subelement = 'contrat';
- } elseif ($element == 'shipping') {
- $classpath = $subelement = 'expedition';
- } elseif ($element == 'deplacement') {
- $classpath = 'compta/deplacement';
- $subelement = 'deplacement';
- } elseif ($element == 'order_supplier') {
- $classpath = 'fourn';
- $subelement = 'fournisseur.commande';
- } elseif ($element == 'invoice_supplier') {
- $classpath = 'fourn';
- $subelement = 'fournisseur.facture';
+ $result = $object->setExtraParameters();
}
-
- dol_include_once('/'.$classpath.'/class/'.$subelement.'.class.php');
-
- if ($element == 'order_supplier') {
- $classname = 'CommandeFournisseur';
- } elseif ($element == 'invoice_supplier') {
- $classname = 'FactureFournisseur';
- } else {
- $classname = ucfirst($subelement);
- }
-
- $object = new $classname($db);
- $object->fetch($id);
-
- $params[$htmlelement] = array($type => $value);
- $object->extraparams = array_merge($object->extraparams, $params);
-
- $result = $object->setExtraParameters();
}
diff --git a/htdocs/core/ajax/fetchKnowledgeRecord.php b/htdocs/core/ajax/fetchKnowledgeRecord.php
index 295d164022f..6e4f1ddd953 100644
--- a/htdocs/core/ajax/fetchKnowledgeRecord.php
+++ b/htdocs/core/ajax/fetchKnowledgeRecord.php
@@ -36,7 +36,7 @@ if (!defined('NOREQUIREMENU')) {
define('NOREQUIREMENU', '1');
}
// If there is no need to load and show top and left menu
-if (!empty($_GET['public'])) {
+if (!empty($_GET['public'])) { // GETPOST() is not yet defined so we use $_GET
if (!defined("NOLOGIN")) {
define("NOLOGIN", '1');
}
@@ -54,10 +54,10 @@ $idticketgroup = GETPOST('idticketgroup', 'aZ09');
$idticketgroup = GETPOST('idticketgroup', 'aZ09');
$lang = GETPOST('lang', 'aZ09');
-/*if (defined("NOLOGIN") && !getDolGlobalString('TICKET_ENABLE_PUBLIC_INTERFACE')) {
- // If we ask public content (so without login), we block if option TICKET_ENABLE_PUBLIC_INTERFACE is not enabled
- httponly_accessforbidden('');
-}*/
+// Security check
+if (!defined("NOLOGIN")) { // No need of restrictedArea if not logged: Later the select will filter on public articles only if not logged.
+ restrictedArea($user, 'knowledgemanagement', 0, 'knowledgemanagement_knowledgerecord', 'knowledgerecord');
+}
/*
diff --git a/htdocs/core/ajax/fileupload.php b/htdocs/core/ajax/fileupload.php
index 4e05c7d8cb6..963fa60b05b 100644
--- a/htdocs/core/ajax/fileupload.php
+++ b/htdocs/core/ajax/fileupload.php
@@ -19,24 +19,25 @@
/**
* \file htdocs/core/ajax/fileupload.php
* \brief File to return Ajax response on file upload
- *
- * Option MAIN_USE_JQUERY_FILEUPLOAD must be enabled to have this feature working. Use is NOT secured !
*/
-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('NOREQUIRESOC')) {
+ define('NOREQUIRESOC', '1');
+}
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/fileupload.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/class/genericobject.class.php';
error_reporting(E_ALL | E_STRICT);
@@ -44,14 +45,33 @@ error_reporting(E_ALL | E_STRICT);
//print_r($_GET);
//print 'upload_dir='.GETPOST('upload_dir');
-$fk_element = GETPOST('fk_element', 'int');
-$element = GETPOST('element', 'alpha');
+$id = GETPOST('fk_element', 'int');
+$element = GETPOST('element', 'alpha'); // 'myobject' (myobject=mymodule) or 'myobject@mymodule' or 'myobject_mysubobject' (myobject=mymodule)
+$elementupload = $element;
-$upload_handler = new FileUpload(null, $fk_element, $element);
+// Load object according to $id and $element
+$object = fetchObjectByElement($id, $element);
-// Feature not enabled. Warning feature not used and not secured so disabled.
-if (!getDolGlobalInt('MAIN_USE_JQUERY_FILEUPLOAD')) {
- return;
+$module = $object->module;
+$element = $object->element;
+$usesublevelpermission = ($module != $element ? $element : '');
+if ($usesublevelpermission && !isset($user->rights->$module->$element)) { // There is no permission on object defined, we will check permission on module directly
+ $usesublevelpermission = '';
+}
+
+//print $object->id.' - '.$object->module.' - '.$object->element.' - '.$object->table_element.' - '.$usesublevelpermission."\n";
+
+// Security check
+if (!empty($user->socid)) {
+ $socid = $user->socid;
+ if (!empty($object->socid) && $socid != $object->socid) {
+ httponly_accessforbidden("Access on object not allowed for this external user."); // This includes the exit.
+ }
+}
+
+$result = restrictedArea($user, $object->module, $object, $object->table_element, $usesublevelpermission, 'fk_soc', 'rowid', 0, 1); // Call with mode return
+if (!$result) {
+ httponly_accessforbidden('Not allowed by restrictArea');
}
@@ -59,6 +79,8 @@ if (!getDolGlobalInt('MAIN_USE_JQUERY_FILEUPLOAD')) {
* View
*/
+$upload_handler = new FileUpload(null, $id, $elementupload);
+
top_httphead();
header('Pragma: no-cache');
diff --git a/htdocs/core/ajax/flowjs-server.php b/htdocs/core/ajax/flowjs-server.php
index d520c57c9c5..901d119332f 100644
--- a/htdocs/core/ajax/flowjs-server.php
+++ b/htdocs/core/ajax/flowjs-server.php
@@ -1,5 +1,5 @@
+/* Copyright (C) 2023 Laurent Destailleur
*
* 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
@@ -16,8 +16,8 @@
*/
/**
- * \file htdocs/core/ajax/bankconciliate.php
- * \brief File to set data for bank concilation
+ * \file htdocs/core/ajax/flowjs-server.php
+ * \brief File to upload very large file, higher than PHP limit. Using flowjs library.
*/
if (!defined('NOTOKENRENEWAL')) {
@@ -46,20 +46,33 @@ require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
$action = GETPOST('action', 'aZ09');
-$module = GETPOST('module', 'aZ09');
-$upload_dir = GETPOST('upload_dir', 'alpha');
+
+$module = GETPOST('module', 'aZ09arobase');
+
$flowFilename = GETPOST('flowFilename', 'alpha');
$flowIdentifier = GETPOST('flowIdentifier', 'alpha');
$flowChunkNumber = GETPOST('flowChunkNumber', 'alpha');
$flowChunkSize = GETPOST('flowChunkSize', 'alpha');
$flowTotalSize = GETPOST('flowTotalSize', 'alpha');
+$result = restrictedArea($user, $module, 0, '', 0, 'fk_soc', 'rowid', 0, 1); // Call with mode return
+
+if ($action != 'upload') {
+ httponly_accessforbidden("Param action must be 'upload'");
+}
+
+if (!empty($conf->$module->dir_temp)) {
+ $upload_dir = $conf->$module->dir_temp;
+} else {
+ httponly_accessforbidden("Param module does not has a dir_temp directory. Module does not exists or is not activated.");
+}
+
/*
* Action
*/
-
top_httphead();
+
dol_syslog(join(',', $_GET));
$result = false;
@@ -123,19 +136,19 @@ if ($result) {
/**
- * Check if all the parts exist, and
- * gather all the parts of the file together
- * @param string $temp_dir - the temporary directory holding all the parts of the file
- * @param string $upload_dir - the temporary directory to create file
- * @param string $fileName - the original file name
- * @param string $chunkSize - each chunk size (in bytes)
- * @param string $totalSize - original file size (in bytes)
- * @return bool true if Ok false else
+ * Check if all the parts exist, and gather all the parts of the file together.
+ *
+ * @param string $temp_dir the temporary directory holding all the parts of the file
+ * @param string $upload_dir the temporary directory to create file
+ * @param string $fileName the original file name
+ * @param string $chunkSize each chunk size (in bytes)
+ * @param string $totalSize original file size (in bytes)
+ * @return bool true if Ok false else
*/
function createFileFromChunks($temp_dir, $upload_dir, $fileName, $chunkSize, $totalSize)
{
-
dol_syslog(__METHOD__, LOG_DEBUG);
+
// count all the parts of this file
$total_files = 0;
$files = dol_dir_list($temp_dir, 'files');
@@ -164,5 +177,6 @@ function createFileFromChunks($temp_dir, $upload_dir, $fileName, $chunkSize, $to
// concurrent chunks uploads)
@rename($temp_dir, $temp_dir.'_UNUSED');
}
+
return true;
}
diff --git a/htdocs/core/ajax/getaccountcurrency.php b/htdocs/core/ajax/getaccountcurrency.php
index ff27812aaa2..20061dcdf2b 100644
--- a/htdocs/core/ajax/getaccountcurrency.php
+++ b/htdocs/core/ajax/getaccountcurrency.php
@@ -35,6 +35,9 @@ require '../../main.inc.php';
$id = GETPOST('id', 'int');
+// Security check
+$result = restrictedArea($user, 'banque', $id, 'bank_account&bank_account');
+
/*
* View
diff --git a/htdocs/core/ajax/loadinplace.php b/htdocs/core/ajax/loadinplace.php
index af033c8cdd3..415e51af8e5 100644
--- a/htdocs/core/ajax/loadinplace.php
+++ b/htdocs/core/ajax/loadinplace.php
@@ -17,7 +17,7 @@
/**
* \file htdocs/core/ajax/loadinplace.php
- * \brief File to load field value
+ * \brief File to load field value. used only when option "Edit In Place" is set (MAIN_USE_JQUERY_JEDITABLE).
*/
if (!defined('NOTOKENRENEWAL')) {
@@ -41,6 +41,30 @@ $field = GETPOST('field', 'alpha');
$element = GETPOST('element', 'alpha');
$table_element = GETPOST('table_element', 'alpha');
$fk_element = GETPOST('fk_element', 'alpha');
+$id = $fk_element;
+
+// Load object according to $id and $element
+$object = fetchObjectByElement($id, $element);
+
+$module = $object->module;
+$element = $object->element;
+$usesublevelpermission = ($module != $element ? $element : '');
+if ($usesublevelpermission && !isset($user->rights->$module->$element)) { // There is no permission on object defined, we will check permission on module directly
+ $usesublevelpermission = '';
+}
+
+//print $object->id.' - '.$object->module.' - '.$object->element.' - '.$object->table_element.' - '.$usesublevelpermission."\n";
+
+// Security check
+$result = restrictedArea($user, $object->module, $object, $object->table_element, $usesublevelpermission, 'fk_soc', 'rowid', 0, 1); // Call with mode return
+if (!$result) {
+ httponly_accessforbidden('Not allowed by restrictArea');
+}
+
+if (!getDolGlobalString('MAIN_USE_JQUERY_JEDITABLE')) {
+ httponly_accessforbidden('Can be used only when option MAIN_USE_JQUERY_JEDITABLE is set');
+}
+
/*
* View
@@ -94,6 +118,7 @@ if (!empty($field) && !empty($element) && !empty($table_element) && !empty($fk_e
}
} elseif (!empty($ext_element)) {
$module = $subelement = $ext_element;
+ $regs = array();
if (preg_match('/^([^_]+)_([^_]+)/i', $ext_element, $regs)) {
$module = $regs[1];
$subelement = $regs[2];
diff --git a/htdocs/core/ajax/locationincoterms.php b/htdocs/core/ajax/locationincoterms.php
index 057322ec5fb..f3e621abc85 100644
--- a/htdocs/core/ajax/locationincoterms.php
+++ b/htdocs/core/ajax/locationincoterms.php
@@ -43,6 +43,12 @@ if (!defined('NOREQUIRESOC')) {
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
+// Security check
+if (!isModEnabled('incoterm')) {
+ httponly_accessforbidden("Module incoterm not enabled"); // This includes the exit.
+}
+// There is no other permission on this component. Everybody connected can read content of the incoterm table
+
/*
* View
@@ -70,13 +76,12 @@ if (GETPOST('location_incoterms')) {
if (!empty($conf->global->MAIN_USE_LOCATION_INCOTERMS_DICTIONNARY)) { // Use location_incoterms
$sql = "SELECT z.location as location_incoterms, z.label as label";
$sql .= " FROM ".MAIN_DB_PREFIX."c_location_incoterms as z";
- $sql .= " WHERE z.active = 1 AND UPPER(z.location) LIKE UPPER('%".$db->escape($location_incoterms)."%')";
+ $sql .= " WHERE z.active = 1 AND z.location LIKE '%".$db->escape($db->escapeforlike($location_incoterms))."%'";
$sql .= " ORDER BY z.location";
$sql .= $db->plimit(100); // Avoid pb with bad criteria
- } else // Use table of commande
- {
+ } else { // Use table of sale orders
$sql = "SELECT DISTINCT s.location_incoterms FROM ".MAIN_DB_PREFIX.'commande as s';
- $sql .= " WHERE UPPER(s.location_incoterms) LIKE UPPER('%".$db->escape($location_incoterms)."%')";
+ $sql .= " WHERE s.location_incoterms LIKE '%".$db->escape($db->escapeforlike($location_incoterms))."%'";
//Todo: merge with data from table of supplier order
/* $sql .=" UNION";
diff --git a/htdocs/core/ajax/objectonoff.php b/htdocs/core/ajax/objectonoff.php
index 77cd3234cd8..ed670627a90 100644
--- a/htdocs/core/ajax/objectonoff.php
+++ b/htdocs/core/ajax/objectonoff.php
@@ -18,7 +18,7 @@
/**
* \file htdocs/core/ajax/objectonoff.php
* \brief File to set status for an object
- * This Ajax service is called when option MAIN_DIRECT_STATUS_UPDATE is set.
+ * This Ajax service is oftenly called when option MAIN_DIRECT_STATUS_UPDATE is set.
*/
if (!defined('NOTOKENRENEWAL')) {
@@ -45,37 +45,46 @@ require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/genericobject.class.php';
$action = GETPOST('action', 'aZ09');
+
$id = GETPOST('id', 'int');
-$value = GETPOST('value', 'int');
+$element = GETPOST('element', 'alpha'); // 'myobject' (myobject=mymodule) or 'myobject@mymodule' or 'myobject_mysubobject' (myobject=mymodule)
$field = GETPOST('field', 'alpha');
-$element = GETPOST('element', 'alpha');
+$value = GETPOST('value', 'int');
$format = 'int';
-$object = new GenericObject($db);
-
-$tablename = $element;
-if ($tablename == 'websitepage') {
- $tablename = 'website_page';
+// Load object according to $id and $element
+$object = fetchObjectByElement($id, $element);
+if (!is_object($object)) {
+ httponly_accessforbidden("Bad value for combination of parameters element/field: Object not found."); // This includes the exit.
}
-$object->table_element = $tablename;
-$object->id = $id;
$object->fields[$field] = array('type' => $format, 'enabled' => 1);
+$module = $object->module;
+$element = $object->element;
+$usesublevelpermission = ($module != $element ? $element : '');
+if ($usesublevelpermission && !isset($user->rights->$module->$element)) { // There is no permission on object defined, we will check permission on module directly
+ $usesublevelpermission = '';
+}
+
+//print $object->id.' - '.$object->module.' - '.$object->element.' - '.$object->table_element.' - '.$usesublevelpermission."\n";
+
// Security check
if (!empty($user->socid)) {
$socid = $user->socid;
+ if (!empty($object->socid) && $socid != $object->socid) {
+ httponly_accessforbidden("Access on object not allowed for this external user."); // This includes the exit.
+ }
}
-//$user->hasRight('societe', 'lire') = 0;$user->rights->fournisseur->lire = 0;
-//restrictedArea($user, 'societe', $id);
-
-if (in_array($field, array('status'))) {
- restrictedArea($user, $element, $id);
+// We check permission.
+// Check is done on $user->rights->element->create or $user->rights->element->subelement->create (because $action = 'set')
+if (preg_match('/status$/', $field)) {
+ restrictedArea($user, $object->module, $object, $object->table_element, $usesublevelpermission);
} elseif ($element == 'product' && in_array($field, array('tosell', 'tobuy', 'tobatch'))) { // Special case for products
- restrictedArea($user, 'produit|service', $id, 'product&product', '', '', 'rowid');
+ restrictedArea($user, 'produit|service', $object, 'product&product', '', '', 'rowid');
} else {
- httponly_accessforbidden("Bad value for combination of parameters element/field.");
+ httponly_accessforbidden("Bad value for combination of parameters element/field: Field not supported."); // This includes the exit.
}
@@ -89,7 +98,7 @@ print ''."\n";
diff --git a/htdocs/core/ajax/price.php b/htdocs/core/ajax/price.php
index 283afb77653..e2a8829fc14 100644
--- a/htdocs/core/ajax/price.php
+++ b/htdocs/core/ajax/price.php
@@ -40,6 +40,10 @@ $output = GETPOST('output', 'alpha');
$amount = price2num(GETPOST('amount', 'alpha'));
$tva_tx = str_replace('*', '', GETPOST('tva_tx', 'alpha'));
+// Security check
+// None. This is a formatting only component.
+
+
/*
* View
*/
diff --git a/htdocs/core/ajax/row.php b/htdocs/core/ajax/row.php
index ec3ee105420..a20034ba0fe 100644
--- a/htdocs/core/ajax/row.php
+++ b/htdocs/core/ajax/row.php
@@ -49,7 +49,9 @@ if (!defined('NOREQUIRETRAN')) {
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/genericobject.class.php';
+
$hookmanager->initHooks(array('rowinterface'));
+
// Security check
// This is done later into view.
diff --git a/htdocs/core/ajax/saveinplace.php b/htdocs/core/ajax/saveinplace.php
index 32fdae72835..9cbecc8e9c7 100644
--- a/htdocs/core/ajax/saveinplace.php
+++ b/htdocs/core/ajax/saveinplace.php
@@ -17,7 +17,7 @@
/**
* \file htdocs/core/ajax/saveinplace.php
- * \brief File to save field value
+ * \brief File to load field value. used only when option "Edit In Place" is set (MAIN_USE_JQUERY_JEDITABLE).
*/
if (!defined('NOTOKENRENEWAL')) {
@@ -41,6 +41,7 @@ $field = GETPOST('field', 'alpha', 2);
$element = GETPOST('element', 'alpha', 2);
$table_element = GETPOST('table_element', 'alpha', 2);
$fk_element = GETPOST('fk_element', 'alpha', 2);
+$id = $fk_element;
/* Example:
field:editval_ref_customer (8 first chars will removed to know name of property)
@@ -54,6 +55,28 @@ savemethod:
savemethodname:
*/
+// Load object according to $id and $element
+$object = fetchObjectByElement($id, $element);
+
+$module = $object->module;
+$element = $object->element;
+$usesublevelpermission = ($module != $element ? $element : '');
+if ($usesublevelpermission && !isset($user->rights->$module->$element)) { // There is no permission on object defined, we will check permission on module directly
+ $usesublevelpermission = '';
+}
+
+//print $object->id.' - '.$object->module.' - '.$object->element.' - '.$object->table_element.' - '.$usesublevelpermission."\n";
+
+// Security check
+$result = restrictedArea($user, $object->module, $object, $object->table_element, $usesublevelpermission, 'fk_soc', 'rowid', 0, 1); // Call with mode return
+if (!$result) {
+ httponly_accessforbidden('Not allowed by restrictArea');
+}
+
+if (!getDolGlobalString('MAIN_USE_JQUERY_JEDITABLE')) {
+ httponly_accessforbidden('Can be used only when option MAIN_USE_JQUERY_JEDITABLE is set');
+}
+
/*
* View
diff --git a/htdocs/core/ajax/security.php b/htdocs/core/ajax/security.php
index 2a836359e1c..8602190db73 100644
--- a/htdocs/core/ajax/security.php
+++ b/htdocs/core/ajax/security.php
@@ -17,8 +17,8 @@
/**
* \file htdocs/core/ajax/security.php
- * \brief This ajax component is used to generated hash keys for security purposes
- * like key to use into URL to protect them.
+ * \brief This ajax component is used to generated hash keys for security purposes,
+ * like the key to use into URL to protect them.
*/
if (!defined('NOTOKENRENEWAL')) {
@@ -46,6 +46,9 @@ require '../../main.inc.php';
$action = GETPOST('action');
+// Security check
+// None. This is public component with no effect on data.
+
/*
* View
diff --git a/htdocs/core/ajax/selectobject.php b/htdocs/core/ajax/selectobject.php
index 079224c62a2..14372a405b4 100644
--- a/htdocs/core/ajax/selectobject.php
+++ b/htdocs/core/ajax/selectobject.php
@@ -38,33 +38,18 @@ if (!defined('NOREQUIRESOC')) {
// Load Dolibarr environment
require '../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
$objectdesc = GETPOST('objectdesc', 'alpha');
$htmlname = GETPOST('htmlname', 'aZ09');
$outjson = (GETPOST('outjson', 'int') ? GETPOST('outjson', 'int') : 0);
$id = GETPOST('id', 'int');
-$filter = GETPOST('filter', 'alphanohtml');
-
-
-/*
- * View
- */
-
-//print ''."\n";
-//print_r($_GET);
-
-require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
-$form = new Form($db);
-
-//$langs->load("companies");
-
-top_httphead();
+$filter = GETPOST('filter', 'alphanohtml'); // Universal Syntax filter
if (empty($htmlname)) {
- return;
+ httponly_accessforbidden('Bad value for param htmlname');
}
-
$InfoFieldList = explode(":", $objectdesc);
$classname = $InfoFieldList[0];
$classpath = $InfoFieldList[1];
@@ -75,16 +60,41 @@ if (!empty($classpath)) {
}
}
if (!is_object($objecttmp)) {
- dol_syslog('Error bad param objectdesc', LOG_WARNING);
- print 'Error bad param objectdesc';
+ httponly_accessforbidden('Bad value for param objectdesc');
}
+/*
+// Load object according to $id and $element
+$object = fetchObjectByElement($id, $element);
+
+$module = $object->module;
+$element = $object->element;
+$usesublevelpermission = ($module != $element ? $element : '');
+if ($usesublevelpermission && !isset($user->rights->$module->$element)) { // There is no permission on object defined, we will check permission on module directly
+ $usesublevelpermission = '';
+}
+*/
+
// When used from jQuery, the search term is added as GET param "term".
$searchkey = (($id && GETPOST($id, 'alpha')) ? GETPOST($id, 'alpha') : (($htmlname && GETPOST($htmlname, 'alpha')) ? GETPOST($htmlname, 'alpha') : ''));
// Add a security test to avoid to get content of all tables
restrictedArea($user, $objecttmp->element, $id);
+
+/*
+ * View
+ */
+
+//print ''."\n";
+//print_r($_GET);
+
+//$langs->load("companies");
+
+$form = new Form($db);
+
+top_httphead($outjson ? 'application/json' : 'text/html');
+
$arrayresult = $form->selectForFormsList($objecttmp, $htmlname, '', 0, $searchkey, '', '', '', 0, 1, 0, '', $filter);
$db->close();
diff --git a/htdocs/core/ajax/selectsearchbox.php b/htdocs/core/ajax/selectsearchbox.php
index d2379f46966..615872eb101 100644
--- a/htdocs/core/ajax/selectsearchbox.php
+++ b/htdocs/core/ajax/selectsearchbox.php
@@ -1,5 +1,5 @@
+/* Copyright (C) 2015-2023 Laurent Destailleur
*
* 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
@@ -18,7 +18,7 @@
/**
* \file htdocs/core/ajax/selectsearchbox.php
* \ingroup core
- * \brief This script returns content of possible search
+ * \brief This script returns json array of possible searches or just set the array if called by an include
*/
// This script is called with a POST method or as an include.
@@ -43,6 +43,9 @@ if (!isset($usedbyinclude) || empty($usedbyinclude)) {
$res = @include '../../main.inc.php';
+ // Security check
+ // None. Beeing connected is enough.
+
top_httphead('application/json');
if ($res == 'ERROR_NOT_LOGGED') {
diff --git a/htdocs/core/ajax/vatrates.php b/htdocs/core/ajax/vatrates.php
index 56cb4c788e3..1b4a0c79c80 100644
--- a/htdocs/core/ajax/vatrates.php
+++ b/htdocs/core/ajax/vatrates.php
@@ -17,7 +17,7 @@
/**
* \file htdocs/core/ajax/vatrates.php
- * \brief File to load vat rates combobox
+ * \brief File to load vat rates combobox according to thirdparty ID. Values are returned in JSON format.
*/
if (!defined('NOTOKENRENEWAL')) {
@@ -34,16 +34,20 @@ if (!defined('NOREQUIREAJAX')) {
require '../../main.inc.php';
$id = GETPOST('id', 'int');
-$action = GETPOST('action', 'aZ09');
+$action = GETPOST('action', 'aZ09'); // 'getSellerVATRates' or 'getBuyerVATRates'
$htmlname = GETPOST('htmlname', 'alpha');
$selected = (GETPOST('selected') ?GETPOST('selected') : '-1');
$productid = (GETPOST('productid', 'int') ?GETPOST('productid', 'int') : 0);
+// Security check
+$result = restrictedArea($user, 'societe', $id, '&societe', '', 'fk_soc', 'rowid', 0);
+
+
/*
* View
*/
-top_httphead();
+top_httphead('application/json');
//print ''."\n";
@@ -63,7 +67,6 @@ if (!empty($id) && !empty($action) && !empty($htmlname)) {
}
$return = array();
-
$return['value'] = $form->load_tva('tva_tx', $selected, $seller, $buyer, $productid, 0, '', true);
$return['num'] = $form->num;
$return['error'] = $form->error;
diff --git a/htdocs/core/ajax/ziptown.php b/htdocs/core/ajax/ziptown.php
index f6869e9ffcf..92db98c250d 100644
--- a/htdocs/core/ajax/ziptown.php
+++ b/htdocs/core/ajax/ziptown.php
@@ -42,6 +42,11 @@ if (!defined('NOREQUIRESOC')) {
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
+// Security check
+if (!getDolGlobalString('MAIN_USE_ZIPTOWN_DICTIONNARY')) {
+ // If MAIN_USE_ZIPTOWN_DICTIONNARY is set, we make a search into a public page. If not we search into societe so we must check we have read permission.
+ $result = restrictedArea($user, 'societe', 0, '&societe', '', 'fk_soc', 'rowid', 0);
+}
/*
@@ -53,11 +58,11 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
//header('Pragma: public');
//top_htmlhead("", "", 1); // Replaced with top_httphead. An ajax page does not need html header.
-top_httphead();
+top_httphead('application/json');
//print ''."\n";
-dol_syslog('ziptown call with MAIN_USE_ZIPTOWN_DICTIONNARY='.(empty($conf->global->MAIN_USE_ZIPTOWN_DICTIONNARY) ? '' : $conf->global->MAIN_USE_ZIPTOWN_DICTIONNARY));
+dol_syslog('ziptown call with MAIN_USE_ZIPTOWN_DICTIONNARY='.getDolGlobalString('MAIN_USE_ZIPTOWN_DICTIONNARY'));
//var_dump($_GET);
// Generation of list of zip-town
@@ -69,7 +74,7 @@ if (GETPOST('zipcode') || GETPOST('town')) {
$zipcode = GETPOST('zipcode');
$town = GETPOST('town');
- if (!empty($conf->global->MAIN_USE_ZIPTOWN_DICTIONNARY)) { // Use zip-town table
+ if (getDolGlobalString('MAIN_USE_ZIPTOWN_DICTIONNARY')) { // Use zip-town table
$sql = "SELECT z.rowid, z.zip, z.town, z.fk_county, z.fk_pays as fk_country";
$sql .= ", c.rowid as fk_country, c.code as country_code, c.label as country";
$sql .= ", d.rowid as fk_county, d.code_departement as county_code, d.nom as county";
@@ -80,15 +85,14 @@ if (GETPOST('zipcode') || GETPOST('town')) {
$sql .= " WHERE z.fk_pays = c.rowid";
$sql .= " AND z.active = 1 AND c.active = 1";
if ($zipcode) {
- $sql .= " AND z.zip LIKE '".$db->escape($zipcode)."%'";
+ $sql .= " AND z.zip LIKE '".$db->escape($db->escapeforlike($zipcode))."%'";
}
if ($town) {
- $sql .= " AND z.town LIKE '%".$db->escape($town)."%'";
+ $sql .= " AND z.town LIKE '%".$db->escape($db->escapeforlike($town))."%'";
}
$sql .= " ORDER BY z.zip, z.town";
$sql .= $db->plimit(100); // Avoid pb with bad criteria
- } else // Use table of third parties
- {
+ } else { // Use table of third parties
$sql = "SELECT DISTINCT s.zip, s.town, s.fk_departement as fk_county, s.fk_pays as fk_country";
$sql .= ", c.code as country_code, c.label as country";
$sql .= ", d.code_departement as county_code , d.nom as county";
@@ -97,10 +101,10 @@ if (GETPOST('zipcode') || GETPOST('town')) {
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX.'c_country as c ON s.fk_pays = c.rowid';
$sql .= " WHERE";
if ($zipcode) {
- $sql .= " s.zip LIKE '".$db->escape($zipcode)."%'";
+ $sql .= " s.zip LIKE '".$db->escape($db->escapeforlike($zipcode))."%'";
}
if ($town) {
- $sql .= " s.town LIKE '%".$db->escape($town)."%'";
+ $sql .= " s.town LIKE '%".$db->escape($db->escapeforlike($town))."%'";
}
$sql .= " ORDER BY s.fk_pays, s.zip, s.town";
$sql .= $db->plimit(100); // Avoid pb with bad criteria
diff --git a/htdocs/core/boxes/box_members_by_tags.php b/htdocs/core/boxes/box_members_by_tags.php
index d4e78f7efe9..6341fb9f899 100644
--- a/htdocs/core/boxes/box_members_by_tags.php
+++ b/htdocs/core/boxes/box_members_by_tags.php
@@ -2,7 +2,7 @@
/* Copyright (C) 2003-2007 Rodolphe Quiedeville
* Copyright (C) 2004-2017 Laurent Destailleur
* Copyright (C) 2005-2012 Regis Houssin
- * Copyright (C) 2015-2020 Frederic France
+ * Copyright (C) 2015-2023 Frédéric France
* Copyright (C) 2021-2022 Waël Almoman
*
* This program is free software; you can redistribute it and/or modify
@@ -87,6 +87,7 @@ class box_members_by_tags extends ModeleBoxes
include_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
$staticmember = new Adherent($this->db);
+ $now = dol_now();
$year = date('Y');
$numberyears = empty(getDolGlobalInt("MAIN_NB_OF_YEAR_IN_WIDGET_GRAPH")) ? 2 : getDolGlobalInt("MAIN_NB_OF_YEAR_IN_WIDGET_GRAPH");
diff --git a/htdocs/core/class/CMailFile.class.php b/htdocs/core/class/CMailFile.class.php
index 04da241f16c..eef112a00e2 100644
--- a/htdocs/core/class/CMailFile.class.php
+++ b/htdocs/core/class/CMailFile.class.php
@@ -641,7 +641,7 @@ class CMailFile
*/
public function sendfile()
{
- global $conf, $db, $langs;
+ global $conf, $db, $langs, $hookmanager;
$errorlevel = error_reporting();
//error_reporting($errorlevel ^ E_WARNING); // Desactive warnings
@@ -649,8 +649,10 @@ class CMailFile
$res = false;
if (empty($conf->global->MAIN_DISABLE_ALL_MAILS)) {
- require_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
- $hookmanager = new HookManager($db);
+ if (!is_object($hookmanager)) {
+ include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
+ $hookmanager = new HookManager($db);
+ }
$hookmanager->initHooks(array('mail'));
$parameters = array();
@@ -686,6 +688,8 @@ class CMailFile
$this->error .= ' '.$langs->trans("MailSendSetupIs3", $conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS);
$this->errors[] = $langs->trans("MailSendSetupIs3", $conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS);
}
+
+ dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_WARNING);
return false;
}
@@ -756,7 +760,7 @@ class CMailFile
// Use mail php function (default PHP method)
// ------------------------------------------
dol_syslog("CMailFile::sendfile addr_to=".$this->addr_to.", subject=".$this->subject, LOG_DEBUG);
- dol_syslog("CMailFile::sendfile header=\n".$this->headers, LOG_DEBUG);
+ //dol_syslog("CMailFile::sendfile header=\n".$this->headers, LOG_DEBUG);
//dol_syslog("CMailFile::sendfile message=\n".$message);
// If Windows, sendmail_from must be defined
@@ -845,7 +849,7 @@ class CMailFile
dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
if (!empty($conf->global->MAIN_MAIL_DEBUG)) {
- $this->save_dump_mail_in_err();
+ $this->save_dump_mail_in_err('Mail with topic '.$this->subject);
}
} else {
dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG);
@@ -995,7 +999,26 @@ class CMailFile
$this->dump_mail();
}
- $result = $this->smtps->getErrors();
+ if (! $result) {
+ $smtperrorcode = $this->smtps->lastretval; // SMTP error code
+ dol_syslog("CMailFile::sendfile: mail SMTP error code ".$smtperrorcode, LOG_WARNING);
+
+ if ($smtperrorcode == '421') { // Try later
+ // TODO Add a delay and try again
+ /*
+ dol_syslog("CMailFile::sendfile: Try later error, so we wait and we retry");
+ sleep(2);
+
+ $result = $this->smtps->sendMsg();
+
+ if (!empty($conf->global->MAIN_MAIL_DEBUG)) {
+ $this->dump_mail();
+ }
+ */
+ }
+ }
+
+ $result = $this->smtps->getErrors(); // applicative error code (not SMTP error code)
if (empty($this->error) && empty($result)) {
dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG);
$res = true;
@@ -1007,7 +1030,7 @@ class CMailFile
$res = false;
if (!empty($conf->global->MAIN_MAIL_DEBUG)) {
- $this->save_dump_mail_in_err();
+ $this->save_dump_mail_in_err('Mail smtp error '.$smtperrorcode.' with topic '.$this->subject);
}
}
}
@@ -1147,7 +1170,7 @@ class CMailFile
$res = false;
if (!empty($conf->global->MAIN_MAIL_DEBUG)) {
- $this->save_dump_mail_in_err();
+ $this->save_dump_mail_in_err('Mail with topic '.$this->subject);
}
} else {
dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG);
@@ -1258,16 +1281,40 @@ class CMailFile
* Save content if mail is in error
* Used for debugging.
*
+ * @param string $message Add also a message
* @return void
*/
- public function save_dump_mail_in_err()
+ public function save_dump_mail_in_err($message = '')
{
global $dolibarr_main_data_root;
if (@is_writeable($dolibarr_main_data_root)) { // Avoid fatal error on fopen with open_basedir
$srcfile = $dolibarr_main_data_root."/dolibarr_mail.log";
- $destfile = $dolibarr_main_data_root."/dolibarr_mail.err";
+ // Add message to dolibarr_mail.log. We do not use dol_syslog() on purpose,
+ // to be sure to write into dolibarr_mail.log
+ if ($message) {
+ // Test constant SYSLOG_FILE_NO_ERROR (should stay a constant defined with define('SYSLOG_FILE_NO_ERROR',1);
+ if (defined('SYSLOG_FILE_NO_ERROR')) {
+ $filefd = @fopen($srcfile, 'a+');
+ } else {
+ $filefd = fopen($srcfile, 'a+');
+ }
+ if ($filefd) {
+ fwrite($filefd, $message."\n");
+ fclose($filefd);
+ dolChmod($srcfile);
+ }
+ }
+
+ // Move dolibarr_mail.log into a dolibarr_mail.err or dolibarr_mail.date.err
+ if (getDolGlobalString('MAIN_MAIL_DEBUG_ERR_WITH_DATE')) {
+ $destfile = $dolibarr_main_data_root."/dolibarr_mail.".dol_print_date(dol_now(), 'dayhourlog', 'gmt').".err";
+ } else {
+ $destfile = $dolibarr_main_data_root."/dolibarr_mail.err";
+ }
+
+ require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
dol_move($srcfile, $destfile, 0, 1, 0, 0);
}
}
diff --git a/htdocs/core/class/commondocgenerator.class.php b/htdocs/core/class/commondocgenerator.class.php
index cec7408f1ca..127584e3f9f 100644
--- a/htdocs/core/class/commondocgenerator.class.php
+++ b/htdocs/core/class/commondocgenerator.class.php
@@ -1582,7 +1582,7 @@ abstract class CommonDocGenerator
$colDef['title']['label'] = !empty($colDef['title']['label']) ? $colDef['title']['label'] : $outputlangs->transnoentities($colDef['title']['textkey']);
// Add column separator
- if (!empty($colDef['border-left'])) {
+ if (!empty($colDef['border-left']) && isset($colDef['xStartPos'])) {
$pdf->line($colDef['xStartPos'], $tab_top, $colDef['xStartPos'], $tab_top + $tab_height);
}
@@ -1599,17 +1599,21 @@ abstract class CommonDocGenerator
// set cell padding with column title definition
$pdf->setCellPaddings($colDef['title']['padding'][3], $colDef['title']['padding'][0], $colDef['title']['padding'][1], $colDef['title']['padding'][2]);
}
-
+ if (isset($colDef['title']['align'])) {
+ $align = $colDef['title']['align'];
+ } else {
+ $align = '';
+ }
$pdf->SetXY($colDef['xStartPos'], $tab_top);
$textWidth = $colDef['width'];
- $pdf->MultiCell($textWidth, 2, $colDef['title']['label'], '', $colDef['title']['align']);
+ $pdf->MultiCell($textWidth, 2, $colDef['title']['label'], '', $align);
// Add variant of translation if $outputlangsbis is an object
if (is_object($outputlangsbis) && trim($colDef['title']['label'])) {
$pdf->setCellPaddings($colDef['title']['padding'][3], 0, $colDef['title']['padding'][1], $colDef['title']['padding'][2]);
$pdf->SetXY($colDef['xStartPos'], $pdf->GetY());
$textbis = $outputlangsbis->transnoentities($colDef['title']['textkey']);
- $pdf->MultiCell($textWidth, 2, $textbis, '', $colDef['title']['align']);
+ $pdf->MultiCell($textWidth, 2, $textbis, '', $align);
}
$this->tabTitleHeight = max($pdf->GetY() - $tab_top, $this->tabTitleHeight);
diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php
index c85f66ca2c6..e53cc85c21f 100644
--- a/htdocs/core/class/commonobject.class.php
+++ b/htdocs/core/class/commonobject.class.php
@@ -86,6 +86,11 @@ abstract class CommonObject
*/
public $element;
+ /**
+ * @var int The related element
+ */
+ public $fk_element;
+
/**
* @var string Name to use for 'features' parameter to check module permissions user->rights->feature with restrictedArea().
* Undefined means same value than $element. Can be use to force a check on another element for example for class of line, we mention here the parent element.
@@ -391,6 +396,22 @@ abstract class CommonObject
*/
public $shipping_method_id;
+ /**
+ * @var string Shipping method label
+ * @see setShippingMethod()
+ */
+ public $shipping_method;
+
+ /**
+ * @var string multicurrency code
+ */
+ public $multicurrency_code;
+
+ /**
+ * @var string multicurrency tx
+ */
+ public $multicurrency_tx;
+
/**
* @var string
* @see SetDocModel()
@@ -710,19 +731,24 @@ abstract class CommonObject
{
global $action, $extrafields, $langs, $hookmanager;
+ $MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP = 5; // If there is too much extrafields, we do not include them into tooltip
+
$datas = $this->getTooltipContentArray($params);
+ // Add extrafields
if (!empty($extrafields->attributes[$this->table_element]['label'])) {
- foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
- if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
- $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
- }
- $labelextra = $langs->trans((string) $extrafields->attributes[$this->table_element]['label'][$key]);
- if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
- $datas[$key]= ''. $labelextra . ' ';
- } else {
- $value = $this->array_options['options_' . $key];
- $datas[$key]= ''. $labelextra . ': ' . $extrafields->showOutputField($key, $value, '', $this->table_element);
+ if (count($extrafields->attributes[$this->table_element]['label']) < $MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP) {
+ foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
+ if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
+ $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
+ }
+ $labelextra = $langs->trans((string) $extrafields->attributes[$this->table_element]['label'][$key]);
+ if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
+ $datas[$key]= ''. $labelextra . ' ';
+ } else {
+ $value = (empty($this->array_options['options_' . $key]) ? '' : $this->array_options['options_' . $key]);
+ $datas[$key]= ''. $labelextra . ': ' . $extrafields->showOutputField($key, $value, '', $this->table_element);
+ }
}
}
}
@@ -2122,6 +2148,8 @@ abstract class CommonObject
$error = 0;
+ dol_syslog(__METHOD__, LOG_DEBUG);
+
$this->db->begin();
$sql = "UPDATE ".$this->db->prefix().$table." SET ";
@@ -2146,7 +2174,6 @@ abstract class CommonObject
$sql .= " WHERE ".$id_field." = ".((int) $id);
- dol_syslog(__METHOD__, LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql) {
if ($trigkey) {
@@ -4623,7 +4650,7 @@ abstract class CommonObject
}
/**
- * Function to check if an object is used by others.
+ * Function to check if an object is used by others (by children).
* Check is done into this->childtables. There is no check into llx_element_element.
*
* @param int $id Force id of object
@@ -4644,8 +4671,8 @@ abstract class CommonObject
return -1;
}
- $arraytoscan = $this->childtables;
- // For backward compatibility, we check if array is old format array('table1', 'table2', ...)
+ $arraytoscan = $this->childtables; // array('tablename'=>array('fk_element'=>'parentfield'), ...) or array('tablename'=>array('parent'=>table_parent, 'parentkey'=>'nameoffieldforparentfkkey'), ...)
+ // For backward compatibility, we check if array is old format array('tablename1', 'tablename2', ...)
$tmparray = array_keys($this->childtables);
if (is_numeric($tmparray[0])) {
$arraytoscan = array_flip($this->childtables);
@@ -4661,7 +4688,11 @@ abstract class CommonObject
if (!empty($element['parent']) && !empty($element['parentkey'])) {
$sql.= ", ".$this->db->prefix().$element['parent']." as p";
}
- $sql.= " WHERE c.".$this->fk_element." = ".((int) $id);
+ if (!empty($element['fk_element'])) {
+ $sql.= " WHERE c.".$element['fk_element']." = ".((int) $id);
+ } else {
+ $sql.= " WHERE c.".$this->fk_element." = ".((int) $id);
+ }
if (!empty($element['parent']) && !empty($element['parentkey'])) {
$sql.= " AND c.".$element['parentkey']." = p.rowid";
}
@@ -4675,6 +4706,7 @@ abstract class CommonObject
$sql.= " AND c.entity = ".((int) $entity);
}
}
+
$resql = $this->db->query($sql);
if ($resql) {
$obj = $this->db->fetch_object($resql);
@@ -9684,7 +9716,7 @@ abstract class CommonObject
return -1;
}
}
- } elseif (!empty($this->fk_element) && !empty($this->childtables)) { // If object has childs linked with a foreign key field, we check all child tables.
+ } elseif (!empty($this->childtables)) { // If object has childs linked with a foreign key field, we check all child tables.
$objectisused = $this->isObjectUsed($this->id);
if (!empty($objectisused)) {
dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
diff --git a/htdocs/core/class/extrafields.class.php b/htdocs/core/class/extrafields.class.php
index fd6e9aa6efb..73a8b86ea47 100644
--- a/htdocs/core/class/extrafields.class.php
+++ b/htdocs/core/class/extrafields.class.php
@@ -1124,6 +1124,9 @@ class ExtraFields
continue;
}
+ $valarray = explode('|', $val);
+ $val = $valarray[0];
+
if ($langfile && $val) {
$options[$okey] = $langs->trans($val);
} else {
diff --git a/htdocs/core/class/fileupload.class.php b/htdocs/core/class/fileupload.class.php
index 980f87e2d28..4dcfb55f781 100644
--- a/htdocs/core/class/fileupload.class.php
+++ b/htdocs/core/class/fileupload.class.php
@@ -19,8 +19,6 @@
/**
* \file htdocs/core/class/fileupload.class.php
* \brief File to return Ajax response on file upload
- *
- * Option MAIN_USE_JQUERY_FILEUPLOAD must be enabled to have feature working. Use is NOT secured !
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
@@ -49,17 +47,13 @@ class FileUpload
global $object;
global $hookmanager;
- // Feature not enabled. Warning feature not used and not secured so disabled.
- if (!getDolGlobalInt('MAIN_USE_JQUERY_FILEUPLOAD')) {
- return;
- }
-
$hookmanager->initHooks(array('fileupload'));
$this->fk_element = $fk_element;
$this->element = $element;
$pathname = $filename = $element;
+ $regs = array();
if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) {
$pathname = $regs[1];
$filename = $regs[2];
@@ -265,9 +259,6 @@ class FileUpload
*/
protected function getFileObject($file_name)
{
- if (!getDolGlobalInt('MAIN_USE_JQUERY_FILEUPLOAD')) {
- return null;
- }
$file_path = $this->options['upload_dir'].$file_name;
if (is_file($file_path) && $file_name[0] !== '.') {
@@ -309,10 +300,6 @@ class FileUpload
{
global $maxwidthmini, $maxheightmini;
- if (!getDolGlobalInt('MAIN_USE_JQUERY_FILEUPLOAD')) {
- return false;
- }
-
$file_path = $this->options['upload_dir'].$file_name;
$new_file_path = $options['upload_dir'].$file_name;
@@ -344,10 +331,6 @@ class FileUpload
*/
protected function validate($uploaded_file, $file, $error, $index)
{
- if (!getDolGlobalInt('MAIN_USE_JQUERY_FILEUPLOAD')) {
- return false;
- }
-
if ($error) {
$file->error = $error;
return false;
@@ -463,10 +446,6 @@ class FileUpload
*/
protected function handleFileUpload($uploaded_file, $name, $size, $type, $error, $index)
{
- if (!getDolGlobalInt('MAIN_USE_JQUERY_FILEUPLOAD')) {
- return null;
- }
-
$file = new stdClass();
$file->name = $this->trimFileName($name, $type, $index);
$file->mime = dol_mimetype($file->name, '', 2);
@@ -513,10 +492,6 @@ class FileUpload
*/
public function get()
{
- if (!getDolGlobalInt('MAIN_USE_JQUERY_FILEUPLOAD')) {
- return;
- }
-
$file_name = isset($_REQUEST['file']) ?
basename(stripslashes($_REQUEST['file'])) : null;
if ($file_name) {
@@ -535,10 +510,6 @@ class FileUpload
*/
public function post()
{
- if (!getDolGlobalInt('MAIN_USE_JQUERY_FILEUPLOAD')) {
- return;
- }
-
if (isset($_REQUEST['_method']) && $_REQUEST['_method'] === 'DELETE') {
return $this->delete();
}
@@ -594,10 +565,6 @@ class FileUpload
*/
public function delete()
{
- if (!getDolGlobalInt('MAIN_USE_JQUERY_FILEUPLOAD')) {
- return null;
- }
-
$file_name = isset($_REQUEST['file']) ?
basename(stripslashes($_REQUEST['file'])) : null;
$file_path = $this->options['upload_dir'].$file_name;
diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php
index 96aa566ab2e..cf512d5f173 100644
--- a/htdocs/core/class/html.form.class.php
+++ b/htdocs/core/class/html.form.class.php
@@ -191,23 +191,23 @@ class Form
/**
* Output value of a field for an editable field
*
- * @param string $text Text of label (not used in this function)
- * @param string $htmlname Name of select field
- * @param string $value Value to show/edit
- * @param object $object Object (that we want to show)
- * @param boolean $perm Permission to allow button to edit parameter
- * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols%', 'datepicker' ('day' do not work, don't know why), 'dayhour' or 'datehourpicker', 'ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols', 'select;xkey:xval,ykey:yval,...')
- * @param string $editvalue When in edit mode, use this value as $value instead of value (for example, you can provide here a formated price instead of numeric value). Use '' to use same than $value
- * @param object $extObject External object ???
- * @param mixed $custommsg String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage')
- * @param string $moreparam More param to add on the form on action href URL parameter
- * @param int $notabletag Do no output table tags
- * @param string $formatfunc Call a specific function to output field in view mode (For example: 'dol_print_email')
- * @param string $paramid Key of parameter for id ('id', 'socid')
- * @param string $gm 'auto' or 'tzuser' or 'tzuserrel' or 'tzserver' (when $typeofdata is a date)
- * @param array $moreoptions Array with more options. For example array('addnowlink'=>1), array('valuealreadyhtmlescaped'=>1)
- * @param string $editaction [=''] use GETPOST default action or set action to edit mode
- * @return string HTML edit field
+ * @param string $text Text of label (not used in this function)
+ * @param string $htmlname Name of select field
+ * @param string $value Value to show/edit
+ * @param object $object Object (that we want to show)
+ * @param boolean $perm Permission to allow button to edit parameter
+ * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols%', 'datepicker' ('day' do not work, don't know why), 'dayhour' or 'datehourpicker', 'ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols', 'select;xkey:xval,ykey:yval,...')
+ * @param string $editvalue When in edit mode, use this value as $value instead of value (for example, you can provide here a formated price instead of numeric value, or a select combo). Use '' to use same than $value
+ * @param object $extObject External object ???
+ * @param mixed $custommsg String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage')
+ * @param string $moreparam More param to add on the form on action href URL parameter
+ * @param int $notabletag Do no output table tags
+ * @param string $formatfunc Call a specific method of $object->$formatfunc to output field in view mode (For example: 'dol_print_email')
+ * @param string $paramid Key of parameter for id ('id', 'socid')
+ * @param string $gm 'auto' or 'tzuser' or 'tzuserrel' or 'tzserver' (when $typeofdata is a date)
+ * @param array $moreoptions Array with more options. For example array('addnowlink'=>1), array('valuealreadyhtmlescaped'=>1)
+ * @param string $editaction [=''] use GETPOST default action or set action to edit mode
+ * @return string HTML edit field
*/
public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 1, $formatfunc = '', $paramid = 'id', $gm = 'auto', $moreoptions = array(), $editaction = '')
{
@@ -310,6 +310,8 @@ class Form
require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
$doleditor = new DolEditor($htmlname, ($editvalue ? $editvalue : $value), (empty($tmp[2]) ? '' : $tmp[2]), (empty($tmp[3]) ? '100' : $tmp[3]), (empty($tmp[1]) ? 'dolibarr_notes' : $tmp[1]), 'In', (empty($tmp[5]) ? 0 : $tmp[5]), (isset($tmp[8]) ? ($tmp[8] ? true : false) : true), true, (empty($tmp[6]) ? '20' : $tmp[6]), (empty($tmp[7]) ? '100' : $tmp[7]));
$ret .= $doleditor->Create(1);
+ } elseif ($typeofdata == 'asis') {
+ $ret .= ($editvalue ? $editvalue : $value);
}
if (empty($notabletag)) {
$ret .= '';
@@ -607,22 +609,22 @@ class Form
}
/**
- * Show a text and picto with tooltip on text or picto.
+ * Show a text and picto with tooltip on text or picto.
* Can be called by an instancied $form->textwithtooltip or by a static call Form::textwithtooltip
*
- * @param string $text Text to show
- * @param string $htmltext HTML content of tooltip. Must be HTML/UTF8 encoded.
- * @param int $tooltipon 1=tooltip on text, 2=tooltip on image, 3=tooltip sur les 2
- * @param int $direction -1=image is before, 0=no image, 1=image is after
- * @param string $img Html code for image (use img_xxx() function to get it)
- * @param string $extracss Add a CSS style to td tags
- * @param int $notabs 0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span
- * @param string $incbefore Include code before the text
- * @param int $noencodehtmltext Do not encode into html entity the htmltext
- * @param string $tooltiptrigger ''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key)
- * @param int $forcenowrap Force no wrap between text and picto (works with notabs=2 only)
- * @return string Code html du tooltip (texte+picto)
- * @see textwithpicto() Use thisfunction if you can.
+ * @param string $text Text to show
+ * @param string $htmltext HTML content of tooltip. Must be HTML/UTF8 encoded.
+ * @param int $tooltipon 1=tooltip on text, 2=tooltip on image, 3=tooltip sur les 2
+ * @param int $direction -1=image is before, 0=no image, 1=image is after
+ * @param string $img Html code for image (use img_xxx() function to get it)
+ * @param string $extracss Add a CSS style to td tags
+ * @param int $notabs 0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span
+ * @param string $incbefore Include code before the text
+ * @param int $noencodehtmltext Do not encode into html entity the htmltext
+ * @param string $tooltiptrigger ''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key)
+ * @param int $forcenowrap Force no wrap between text and picto (works with notabs=2 only)
+ * @return string Code html du tooltip (texte+picto)
+ * @see textwithpicto() Use thisfunction if you can.
*/
public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
{
@@ -723,16 +725,16 @@ class Form
/**
* Show a text with a picto and a tooltip on picto
*
- * @param string $text Text to show
- * @param string $htmltext Content of tooltip
- * @param int $direction 1=Icon is after text, -1=Icon is before text, 0=no icon
- * @param string $type Type of picto ('info', 'infoclickable', 'help', 'helpclickable', 'warning', 'superadmin', 'mypicto@mymodule', ...) or image filepath or 'none'
- * @param string $extracss Add a CSS style to td, div or span tag
- * @param int $noencodehtmltext Do not encode into html entity the htmltext
- * @param int $notabs 0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span
- * @param string $tooltiptrigger ''=Tooltip on hover and hidden on smartphone, 'abconsmartphone'=Tooltip on hover and on click on smartphone, 'abc'=Tooltip on click (abc is a unique key, clickable link is on image or on link if param $type='none' or on both if $type='xxxclickable')
- * @param int $forcenowrap Force no wrap between text and picto (works with notabs=2 only)
- * @return string HTML code of text, picto, tooltip
+ * @param string $text Text to show
+ * @param string $htmltext Content of tooltip
+ * @param int $direction 1=Icon is after text, -1=Icon is before text, 0=no icon
+ * @param string $type Type of picto ('info', 'infoclickable', 'help', 'helpclickable', 'warning', 'superadmin', 'mypicto@mymodule', ...) or image filepath or 'none'
+ * @param string $extracss Add a CSS style to td, div or span tag
+ * @param int $noencodehtmltext Do not encode into html entity the htmltext
+ * @param int $notabs 0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span
+ * @param string $tooltiptrigger ''=Tooltip on hover and hidden on smartphone, 'abconsmartphone'=Tooltip on hover and on click on smartphone, 'abc'=Tooltip on click (abc is a unique key, clickable link is on image or on link if param $type='none' or on both if $type='xxxclickable')
+ * @param int $forcenowrap Force no wrap between text and picto (works with notabs=2 only)
+ * @return string HTML code of text, picto, tooltip
*/
public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
{
@@ -1953,25 +1955,25 @@ class Form
/**
* Return select list of users
*
- * @param string $selected User id or user object of user preselected. If 0 or < -2, we use id of current user. If -1, keep unselected (if empty is allowed)
- * @param string $htmlname Field name in form
- * @param int|string $show_empty 0=list with no empty value, 1=add also an empty value into list
- * @param array $exclude Array list of users id to exclude
- * @param int $disabled If select list must be disabled
- * @param array|string $include Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me
- * @param array $enableonly Array list of users id to be enabled. If defined, it means that others will 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 $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 integer $show_every 0=default list, 1=add also a value "Everybody" at beginning of list
- * @param string $enableonlytext If option $enableonlytext is set, we use this text to explain into label why record is disabled. Not used if enableonly is empty.
- * @param string $morecss More css
- * @param int $notdisabled Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on).
- * @param int $outputmode 0=HTML select string, 1=Array
- * @param bool $multiple add [] in the name of element and add 'multiple' attribut
- * @param int $forcecombo Force the component to be a simple combo box without ajax
- * @return array|string HTML select string
+ * @param string $selected User id or user object of user preselected. If 0 or < -2, we use id of current user. If -1, keep unselected (if empty is allowed)
+ * @param string $htmlname Field name in form
+ * @param int|string $show_empty 0=list with no empty value, 1=add also an empty value into list
+ * @param array $exclude Array list of users id to exclude
+ * @param int $disabled If select list must be disabled
+ * @param array|string $include Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me
+ * @param array|string $enableonly Array list of users id to be enabled. If defined, it means that others will 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 $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 integer $show_every 0=default list, 1=add also a value "Everybody" at beginning of list
+ * @param string $enableonlytext If option $enableonlytext is set, we use this text to explain into label why record is disabled. Not used if enableonly is empty.
+ * @param string $morecss More css
+ * @param int $notdisabled Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on).
+ * @param int $outputmode 0=HTML select string, 1=Array
+ * @param bool $multiple add [] in the name of element and add 'multiple' attribut
+ * @param int $forcecombo Force the component to be a simple combo box without ajax
+ * @return array|string HTML select string
* @see select_dolgroups()
*/
public function select_dolusers($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $show_every = 0, $enableonlytext = '', $morecss = '', $notdisabled = 0, $outputmode = 0, $multiple = false, $forcecombo = 0)
@@ -2011,6 +2013,7 @@ class Form
$out = '';
$outarray = array();
+ $outarray2 = array();
// Forge request to select users
$sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.photo";
@@ -2149,7 +2152,7 @@ class Form
}
}
$moreinfo .= ($moreinfo ? ')' : '');
- $moreinfohtml .= ($moreinfohtml ? ')' : '');
+ $moreinfohtml .= ($moreinfohtml ? ')' : '');
if ($disableline && $disableline != '1') {
// Add text from $enableonlytext parameter
$moreinfo .= ' - ' . $disableline;
@@ -2180,6 +2183,11 @@ class Form
$out .= '';
$outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength) . $moreinfo;
+ $outarray2[$userstatic->id] = array(
+ 'id'=>$userstatic->id,
+ 'label'=>$labeltoshow,
+ 'labelhtml'=>$labeltoshowhtml
+ );
$i++;
}
@@ -2198,7 +2206,9 @@ class Form
dol_print_error($this->db);
}
- if ($outputmode) {
+ if ($outputmode == 2) {
+ return $outarray2;
+ } elseif ($outputmode) {
return $outarray;
}
@@ -2604,7 +2614,15 @@ class Form
}
$sql = "SELECT ";
- $sql .= $selectFields . $selectFieldsGrouped;
+
+ // Add select from hooks
+ $parameters = array();
+ $reshook = $hookmanager->executeHooks('selectProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
+ if (empty($reshook)) {
+ $sql .= $selectFields.$selectFieldsGrouped.$hookmanager->resPrint;
+ } else {
+ $sql .= $hookmanager->resPrint;
+ }
if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
//Product category
@@ -2650,7 +2668,13 @@ class Form
$sql .= " DESC LIMIT 1) as price_by_qty";
$selectFields .= ", price_rowid, price_by_qty";
}
- $sql .= " FROM " . $this->db->prefix() . "product as p";
+
+ $sql .= " FROM ".$this->db->prefix()."product as p";
+ // Add from (left join) from hooks
+ $parameters = array();
+ $reshook = $hookmanager->executeHooks('selectProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
+ $sql .= $hookmanager->resPrint;
+
if (count($warehouseStatusArray)) {
$sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps on ps.fk_product = p.rowid";
$sql .= " LEFT JOIN " . $this->db->prefix() . "entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (" . getEntity('stock') . ")";
@@ -2922,6 +2946,7 @@ class Form
protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
{
global $langs, $conf, $user;
+ global $hookmanager;
$outkey = '';
$outval = '';
@@ -3207,6 +3232,14 @@ class Form
}
}
+ $parameters = array('objp'=>$objp);
+ $reshook = $hookmanager->executeHooks('constructProductListOption', $parameters); // Note that $action and $object may have been modified by hook
+ if (empty($reshook)) {
+ $opt .= $hookmanager->resPrint;
+ } else {
+ $opt = $hookmanager->resPrint;
+ }
+
$opt .= "\n";
$optJson = array(
'key' => $outkey,
@@ -4877,6 +4910,8 @@ class Form
print '' . $langs->trans("NoEstablishmentFound") . ' ';
}
}
+
+ return $num;
} else {
dol_print_error($this->db);
return -1;
@@ -5194,15 +5229,20 @@ class Form
$more .= '' . $input['label'] . '
';
$more .= '
';
$addnowlink = (empty($input['datenow']) ? 0 : 1);
- $more .= $this->selectDate($input['value'], $input['name'], ($input['type'] == 'datetime' ? 1 : 0), ($input['type'] == 'datetime' ? 1 : 0), 0, '', 1, $addnowlink);
- $more .= '
' . "\n";
- $formquestion[] = array('name' => $input['name'] . 'day');
- $formquestion[] = array('name' => $input['name'] . 'month');
- $formquestion[] = array('name' => $input['name'] . 'year');
- $formquestion[] = array('name' => $input['name'] . 'hour');
- $formquestion[] = array('name' => $input['name'] . 'min');
- } elseif ($input['type'] == 'other') { // can be 1 column or 2 depending if label is set or not
- $more .= '';
+ $h = $m = 0;
+ if ($input['type'] == 'datetime') {
+ $h = isset($input['hours']) ? $input['hours'] : 1;
+ $m = isset($input['minutes']) ? $input['minutes'] : 1;
+ }
+ $more .= $this->selectDate($input['value'], $input['name'], $h, $m, 0, '', 1, $addnowlink);
+ $more .= '
'."\n";
+ $formquestion[] = array('name'=>$input['name'].'day');
+ $formquestion[] = array('name'=>$input['name'].'month');
+ $formquestion[] = array('name'=>$input['name'].'year');
+ $formquestion[] = array('name'=>$input['name'].'hour');
+ $formquestion[] = array('name'=>$input['name'].'min');
+ } elseif ($input['type'] == 'other') { // can be 1 column or 2 depending if label is set or not
+ $more .= '';
if (!empty($input['label'])) {
$more .= $input['label'] . '
';
}
@@ -6989,7 +7029,12 @@ class Form
if (empty($labeladddateof)) {
$labeladddateof = $langs->trans("DateInvoice");
}
- $retstring .= ' - ' . $labeladddateof . '';
+ $reset_scripts = 'console.log(\'Click on now link\'); ';
+ $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($adddateof, 'dayinputnoreduce').'\');';
+ $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.$tmparray['mday'].'\');';
+ $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.$tmparray['mon'].'\');';
+ $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.$tmparray['year'].'\');';
+ $retstring .= ' - '.$labeladddateof.'';
}
return $retstring;
@@ -7870,20 +7915,20 @@ class Form
* Output html form to select an object.
* Note, this function is called by selectForForms or by ajax selectobject.php
*
- * @param Object $objecttmp Object to knwo the table to scan for combo.
- * @param string $htmlname Name of HTML select component
- * @param int $preselectedvalue Preselected value (ID of element)
- * @param string $showempty ''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
- * @param string $searchkey Search value
- * @param string $placeholder Place holder
- * @param string $morecss More CSS
- * @param string $moreparams More params provided to ajax call
- * @param int $forcecombo Force to load all values and output a standard combobox (with no beautification)
- * @param int $outputmode 0=HTML select string, 1=Array
- * @param int $disabled 1=Html component is disabled
- * @param string $sortfield Sort field
- * @param string $filter Add more filter
- * @return string|array Return HTML string
+ * @param Object $objecttmp Object to knwo the table to scan for combo.
+ * @param string $htmlname Name of HTML select component
+ * @param int $preselectedvalue Preselected value (ID of element)
+ * @param string $showempty ''=empty values not allowed, 'string'=value show if we allow empty values (for example 'All', ...)
+ * @param string $searchkey Search value
+ * @param string $placeholder Place holder
+ * @param string $morecss More CSS
+ * @param string $moreparams More params provided to ajax call
+ * @param int $forcecombo Force to load all values and output a standard combobox (with no beautification)
+ * @param int $outputmode 0=HTML select string, 1=Array
+ * @param int $disabled 1=Html component is disabled
+ * @param string $sortfield Sort field
+ * @param string $filter Add more filter
+ * @return string|array Return HTML string
* @see selectForForms()
*/
public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0, $sortfield = '', $filter = '')
@@ -8452,19 +8497,19 @@ class Form
/**
* Show a multiselect form from an array. WARNING: Use this only for short lists.
*
- * @param string $htmlname Name of select
- * @param array $array Array(key=>value) or Array(key=>array('id'=> , 'label'=> ))
- * @param array $selected Array of keys preselected
- * @param int $key_in_label 1 to show key like in "[key] value"
- * @param int $value_as_key 1 to use value as key
- * @param string $morecss Add more css style
- * @param int $translate Translate and encode value
- * @param int|string $width Force width of select box. May be used only when using jquery couch. Example: 250, '95%'
- * @param string $moreattrib Add more options on select component. Example: 'disabled'
- * @param string $elemtype Type of element we show ('category', ...). Will execute a formating function on it. To use in readonly mode if js component support HTML formatting.
- * @param string $placeholder String to use as placeholder
- * @param int $addjscombo Add js combo
- * @return string HTML multiselect string
+ * @param string $htmlname Name of select
+ * @param array $array Array(key=>value) or Array(key=>array('id'=> , 'label'=> , 'color'=> , 'picto'=> , 'labelhtml'=> ))
+ * @param array $selected Array of keys preselected
+ * @param int $key_in_label 1 to show key like in "[key] value"
+ * @param int $value_as_key 1 to use value as key
+ * @param string $morecss Add more css style
+ * @param int $translate Translate and encode value
+ * @param int|string $width Force width of select box. May be used only when using jquery couch. Example: 250, '95%'
+ * @param string $moreattrib Add more options on select component. Example: 'disabled'
+ * @param string $elemtype Type of element we show ('category', ...). Will execute a formating function on it. To use in readonly mode if js component support HTML formatting.
+ * @param string $placeholder String to use as placeholder
+ * @param int $addjscombo Add js combo
+ * @return string HTML multiselect string
* @see selectarray(), selectArrayAjax(), selectArrayFilter()
*/
public static function multiselectarray($htmlname, $array, $selected = array(), $key_in_label = 0, $value_as_key = 0, $morecss = '', $translate = 0, $width = 0, $moreattrib = '', $elemtype = '', $placeholder = '', $addjscombo = -1)
@@ -8499,11 +8544,13 @@ class Form
$tmpvalue = $value;
$tmpcolor = '';
$tmppicto = '';
+ $tmplabelhtml = '';
if (is_array($value) && array_key_exists('id', $value) && array_key_exists('label', $value)) {
$tmpkey = $value['id'];
$tmpvalue = $value['label'];
$tmpcolor = $value['color'];
$tmppicto = $value['picto'];
+ $tmplabelhtml = !empty($value['labelhtml']) ? $value['labelhtml'] : '';
}
$newval = ($translate ? $langs->trans($tmpvalue) : $tmpvalue);
$newval = ($key_in_label ? $tmpkey . ' - ' . $newval : $newval);
@@ -8512,7 +8559,11 @@ class Form
if (is_array($selected) && !empty($selected) && in_array((string) $tmpkey, $selected) && ((string) $tmpkey != '')) {
$out .= ' selected';
}
- $out .= ' data-html="' . dol_escape_htmltag(($tmppicto ? img_picto('', $tmppicto, 'class="pictofixedwidth" style="color: #' . $tmpcolor . '"') : '') . $newval) . '"';
+ if (!empty($tmplabelhtml)) {
+ $out .= ' data-html="' . dol_escape_htmltag($tmplabelhtml) . '"';
+ } else {
+ $out .= ' data-html="' . dol_escape_htmltag(($tmppicto ? img_picto('', $tmppicto, 'class="pictofixedwidth" style="color: #' . $tmpcolor . '"') : '') . $newval) . '"';
+ }
$out .= '>';
$out .= dol_htmlentitiesbr($newval);
$out .= '' . "\n";
@@ -8763,9 +8814,9 @@ class Form
);
$reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
- if (empty($reshook)) {
- $nbofdifferenttypes = count($object->linkedObjects);
+ $nbofdifferenttypes = count($object->linkedObjects);
+ if (empty($reshook)) {
print '';
print load_fiche_titre($langs->trans($title), $morehtmlright, '', 0, 0, 'showlinkedobjectblock');
@@ -8891,20 +8942,19 @@ class Form
$res = @include dol_buildpath('core/tpl/objectlinked_lineimport.tpl.php');
}
-
print '
';
-
- return $nbofdifferenttypes;
}
+
+ return $nbofdifferenttypes;
}
/**
* Show block with links to link to other objects.
*
- * @param CommonObject $object Object we want to show links to
- * @param array $restrictlinksto Restrict links to some elements, for exemple array('order') or array('supplier_order'). null or array() if no restriction.
- * @param array $excludelinksto Do not show links of this type, for exemple array('order') or array('supplier_order'). null or array() if no exclusion.
- * @return string <0 if KO, >0 if OK
+ * @param CommonObject $object Object we want to show links to
+ * @param array $restrictlinksto Restrict links to some elements, for exemple array('order') or array('supplier_order'). null or array() if no restriction.
+ * @param array $excludelinksto Do not show links of this type, for exemple array('order') or array('supplier_order'). null or array() if no exclusion.
+ * @return string HTML block
*/
public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array())
{
@@ -9829,8 +9879,8 @@ class Form
{
$out = '
';
if ($pos == 'left') {
- $out .= '
';
$out .= '
';
+ $out .= '
';
} else {
$out .= '
';
$out .= '
';
diff --git a/htdocs/core/class/html.formaccounting.class.php b/htdocs/core/class/html.formaccounting.class.php
index d12c5e5eb99..47dab9f6fda 100644
--- a/htdocs/core/class/html.formaccounting.class.php
+++ b/htdocs/core/class/html.formaccounting.class.php
@@ -451,7 +451,7 @@ class FormAccounting extends Form
* @param string $labelhtmlname HTML name of label for autofill of account from name.
* @return string String with HTML select
*/
- public function select_auxaccount($selectid, $htmlname = 'account_num_aux', $showempty = 0, $morecss = 'maxwidth250', $usecache = '', $labelhtmlname = '')
+ public function select_auxaccount($selectid, $htmlname = 'account_num_aux', $showempty = 0, $morecss = 'minwidth100 maxwidth300 maxwidthonsmartphone', $usecache = '', $labelhtmlname = '')
{
// phpcs:enable
diff --git a/htdocs/core/class/html.formfile.class.php b/htdocs/core/class/html.formfile.class.php
index 215e729a278..b9c4239d4dc 100644
--- a/htdocs/core/class/html.formfile.class.php
+++ b/htdocs/core/class/html.formfile.class.php
@@ -1457,10 +1457,13 @@ class FormFile
if ($modulepart == 'medias' && !GETPOST('website')) {
$moreparaminurl .= '&backtourl='.urlencode(DOL_URL_ROOT.'/ecm/index_medias.php?file_manager=1&modulepart='.$modulepart.'§ion_dir='.$relativepath);
}
- if ($modulepart == 'medias' && !GETPOST('website')) {
- print '
'.img_picto('', 'images', 'class="flip marginrightonly"').' ';
- } elseif ($modulepart == 'medias' && GETPOST('website')) {
- print '
'.img_picto('', 'images', 'class="flip marginrightonly"').' ';
+ // Link to convert into webp
+ if (!preg_match('/\.webp$/i', $file['name'])) {
+ if ($modulepart == 'medias' && !GETPOST('website')) {
+ print '
'.img_picto('', 'images', 'class="flip marginrightonly"').' ';
+ } elseif ($modulepart == 'medias' && GETPOST('website')) {
+ print '
'.img_picto('', 'images', 'class="flip marginrightonly"').' ';
+ }
}
}
}
diff --git a/htdocs/core/class/html.formmail.class.php b/htdocs/core/class/html.formmail.class.php
index e3a60fbf6ab..1eb3ec15f9f 100644
--- a/htdocs/core/class/html.formmail.class.php
+++ b/htdocs/core/class/html.formmail.class.php
@@ -1324,10 +1324,10 @@ class FormMail extends Form
$sql .= " AND (lang = '".$dbs->escape($languagetosearch)."'".($languagetosearchmain ? " OR lang = '".$dbs->escape($languagetosearchmain)."'" : "")." OR lang IS NULL OR lang = '')";
}
if ($id > 0) {
- $sql .= " AND rowid=".(int) $id;
+ $sql .= " AND rowid = ".(int) $id;
}
if ($id == -1) {
- $sql .= " AND position=0";
+ $sql .= " AND position = 0";
}
if ($languagetosearch) {
$sql .= $dbs->order("position,lang,label", "ASC,DESC,ASC"); // We want line with lang set first, then with lang null or ''
diff --git a/htdocs/core/class/html.formother.class.php b/htdocs/core/class/html.formother.class.php
index 21e5b608a41..118e75773c1 100644
--- a/htdocs/core/class/html.formother.class.php
+++ b/htdocs/core/class/html.formother.class.php
@@ -1289,18 +1289,21 @@ class FormOther
containment: \'document\',
connectWith: \'#boxhalfleft, #boxhalfright\',
stop: function(event, ui) {
+ console.log("We moved box so we call updateBoxOrder with ajax actions");
updateBoxOrder(1); /* 1 to avoid message after a move */
}
});
jQuery(".boxclose").click(function() {
var self = this; // because JQuery can modify this
- var boxid=self.id.substring(8);
- var label=jQuery(\'#boxlabelentry\'+boxid).val();
- console.log("We close box "+boxid);
- jQuery(\'#boxto_\'+boxid).remove();
- if (boxid > 0) jQuery(\'#boxcombo\').append(new Option(label, boxid));
- updateBoxOrder(1); /* 1 to avoid message after a remove */
+ var boxid = self.id.substring(8);
+ if (boxid > 0) {
+ var label = jQuery(\'#boxlabelentry\'+boxid).val();
+ console.log("We close box "+boxid);
+ jQuery(\'#boxto_\'+boxid).remove();
+ jQuery(\'#boxcombo\').append(new Option(label, boxid));
+ updateBoxOrder(1); /* 1 to avoid message after a remove */
+ }
});
});'."\n";
diff --git a/htdocs/core/class/html.formsetup.class.php b/htdocs/core/class/html.formsetup.class.php
index 13afc43cb51..a3908df0adb 100644
--- a/htdocs/core/class/html.formsetup.class.php
+++ b/htdocs/core/class/html.formsetup.class.php
@@ -834,6 +834,8 @@ class FormSetupItem
$out.= $this->generateInputFieldMultiSelect();
} elseif ($this->type == 'select') {
$out.= $this->generateInputFieldSelect();
+ } elseif ($this->type == 'selectUser') {
+ $out.= $this->generateInputFieldSelectUser();
} elseif ($this->type == 'textarea') {
$out.= $this->generateInputFieldTextarea();
} elseif ($this->type== 'html') {
@@ -993,6 +995,14 @@ class FormSetupItem
return $this->form->selectarray($this->confKey, $this->fieldOptions, $this->fieldValue);
}
+ /**
+ * @return string
+ */
+ public function generateInputFieldSelectUser()
+ {
+ return $this->form->select_dolusers($this->fieldValue, $this->confKey);
+ }
+
/**
* get the type : used for old module builder setup conf style conversion and tests
* because this two class will quickly evolve it's important to not set or get directly $this->type (will be protected) so this method exist
@@ -1067,6 +1077,8 @@ class FormSetupItem
$out.= $this->generateOutputFieldMultiSelect();
} elseif ($this->type == 'select') {
$out.= $this->generateOutputFieldSelect();
+ } elseif ($this->type == 'selectUser') {
+ $out.= $this->generateOutputFieldSelectUser();
} elseif ($this->type== 'html') {
$out.= $this->fieldValue;
} elseif ($this->type== 'color') {
@@ -1136,6 +1148,8 @@ class FormSetupItem
/**
+ * generateOutputFieldMultiSelect
+ *
* @return string
*/
public function generateOutputFieldMultiSelect()
@@ -1157,6 +1171,8 @@ class FormSetupItem
}
/**
+ * generateOutputFieldColor
+ *
* @return string
*/
public function generateOutputFieldColor()
@@ -1165,6 +1181,8 @@ class FormSetupItem
return $this->generateInputField();
}
/**
+ * generateInputFieldColor
+ *
* @return string
*/
public function generateInputFieldColor()
@@ -1174,6 +1192,8 @@ class FormSetupItem
}
/**
+ * generateOutputFieldSelect
+ *
* @return string
*/
public function generateOutputFieldSelect()
@@ -1186,12 +1206,27 @@ class FormSetupItem
return $outPut;
}
+ /**
+ * generateOutputFieldSelectUser
+ *
+ * @return string
+ */
+ public function generateOutputFieldSelectUser()
+ {
+ $outPut = '';
+ $user = new User($this->db);
+ $user->fetch($this->fieldValue);
+ $outPut = $user->firstname . " " . $user->lastname;
+ return $outPut;
+ }
+
/*
* METHODS FOR SETTING DISPLAY TYPE
*/
/**
* Set type of input as string
+ *
* @return self
*/
public function setAsString()
@@ -1202,6 +1237,7 @@ class FormSetupItem
/**
* Set type of input as color
+ *
* @return self
*/
public function setAsColor()
@@ -1212,6 +1248,7 @@ class FormSetupItem
/**
* Set type of input as textarea
+ *
* @return self
*/
public function setAsTextarea()
@@ -1222,6 +1259,7 @@ class FormSetupItem
/**
* Set type of input as html editor
+ *
* @return self
*/
public function setAsHtml()
@@ -1232,6 +1270,7 @@ class FormSetupItem
/**
* Set type of input as emailtemplate selector
+ *
* @param string $templateType email template type
* @return self
*/
@@ -1243,6 +1282,7 @@ class FormSetupItem
/**
* Set type of input as thirdparty_type selector
+ *
* @return self
*/
public function setAsThirdpartyType()
@@ -1253,6 +1293,7 @@ class FormSetupItem
/**
* Set type of input as Yes
+ *
* @return self
*/
public function setAsYesNo()
@@ -1263,6 +1304,7 @@ class FormSetupItem
/**
* Set type of input as secure key
+ *
* @return self
*/
public function setAsSecureKey()
@@ -1273,6 +1315,7 @@ class FormSetupItem
/**
* Set type of input as product
+ *
* @return self
*/
public function setAsProduct()
@@ -1284,6 +1327,7 @@ class FormSetupItem
/**
* Set type of input as a category selector
* TODO add default value
+ *
* @param int $catType Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated.
* @return self
*/
@@ -1294,8 +1338,8 @@ class FormSetupItem
}
/**
- * Set type of input as a simple title
- * no data to store
+ * Set type of input as a simple title. No data to store
+ *
* @return self
*/
public function setAsTitle()
@@ -1306,8 +1350,8 @@ class FormSetupItem
/**
- * Set type of input as a simple title
- * no data to store
+ * Set type of input as a simple title. No data to store
+ *
* @param array $fieldOptions A table of field options
* @return self
*/
@@ -1322,8 +1366,8 @@ class FormSetupItem
}
/**
- * Set type of input as a simple title
- * no data to store
+ * Set type of input as a simple title. No data to store
+ *
* @param array $fieldOptions A table of field options
* @return self
*/
@@ -1336,4 +1380,15 @@ class FormSetupItem
$this->type = 'select';
return $this;
}
+
+ /**
+ * Set type of input as a simple title. No data to store
+ *
+ * @return self
+ */
+ public function setAsSelectUser()
+ {
+ $this->type = 'selectUser';
+ return $this;
+ }
}
diff --git a/htdocs/core/class/html.formticket.class.php b/htdocs/core/class/html.formticket.class.php
index 3f3a038b88c..2257e563865 100644
--- a/htdocs/core/class/html.formticket.class.php
+++ b/htdocs/core/class/html.formticket.class.php
@@ -701,7 +701,7 @@ class FormTicket
{
global $langs, $user;
- $selected = is_array($selected) ? $selected : (!empty($selected) ? array($selected) : array());
+ $selected = is_array($selected) ? $selected : (!empty($selected) ? explode(',', $selected) : array());
$ticketstat = new Ticket($this->db);
dol_syslog(get_class($this) . "::select_types_tickets " . implode(';', $selected) . ", " . $htmlname . ", " . $filtertype . ", " . $format . ", " . $multiselect, LOG_DEBUG);
diff --git a/htdocs/core/class/menubase.class.php b/htdocs/core/class/menubase.class.php
index 3630d137136..2965db0f7c5 100644
--- a/htdocs/core/class/menubase.class.php
+++ b/htdocs/core/class/menubase.class.php
@@ -49,6 +49,11 @@ class Menubase
*/
public $id;
+ /**
+ * @var int Entity
+ */
+ public $entity;
+
/**
* @var string Menu handler
*/
@@ -185,6 +190,7 @@ class Menubase
if (!isset($this->enabled)) {
$this->enabled = '1';
}
+ $this->entity = (isset($this->entity) && (int) $this->entity >= 0 ? (int) $this->entity : $conf->entity);
$this->menu_handler = trim((string) $this->menu_handler);
$this->module = trim((string) $this->module);
$this->type = trim((string) $this->type);
@@ -246,7 +252,7 @@ class Menubase
$sql .= " AND fk_menu = ".((int) $this->fk_menu);
$sql .= " AND position = ".((int) $this->position);
$sql .= " AND url = '".$this->db->escape($this->url)."'";
- $sql .= " AND entity = ".$conf->entity;
+ $sql .= " AND entity IN (0, ".$conf->entity.")";
$result = $this->db->query($sql);
if ($result) {
@@ -275,7 +281,7 @@ class Menubase
$sql .= "usertype";
$sql .= ") VALUES (";
$sql .= " '".$this->db->escape($this->menu_handler)."',";
- $sql .= " '".$this->db->escape($conf->entity)."',";
+ $sql .= " '".$this->db->escape($this->entity)."',";
$sql .= " '".$this->db->escape($this->module)."',";
$sql .= " '".$this->db->escape($this->type)."',";
$sql .= " ".($this->mainmenu ? "'".$this->db->escape($this->mainmenu)."'" : "''").","; // Can't be null
diff --git a/htdocs/core/class/notify.class.php b/htdocs/core/class/notify.class.php
index 3a668d3c0c6..d7bfd4a3f21 100644
--- a/htdocs/core/class/notify.class.php
+++ b/htdocs/core/class/notify.class.php
@@ -69,6 +69,7 @@ class Notify
'BILL_PAYED',
'ORDER_CREATE',
'ORDER_VALIDATE',
+ 'ORDER_CLOSE',
'PROPAL_VALIDATE',
'PROPAL_CLOSE_SIGNED',
'PROPAL_CLOSE_REFUSED',
@@ -503,6 +504,13 @@ class Notify
$labeltouse = $conf->global->ORDER_VALIDATE_TEMPLATE;
$mesg = $outputlangs->transnoentitiesnoconv("EMailTextOrderValidated", $link);
break;
+ case 'ORDER_CLOSE':
+ $link = '
'.$newref.' ';
+ $dir_output = $conf->commande->dir_output."/".get_exdir(0, 0, 0, 1, $object, 'commande');
+ $object_type = 'order';
+ $labeltouse = $conf->global->ORDER_CLOSE_TEMPLATE;
+ $mesg = $outputlangs->transnoentitiesnoconv("EMailTextOrderClose", $link);
+ break;
case 'PROPAL_VALIDATE':
$link = '
'.$newref.' ';
$dir_output = $conf->propal->multidir_output[$object->entity]."/".get_exdir(0, 0, 0, 1, $object, 'propal');
@@ -783,6 +791,12 @@ class Notify
$object_type = 'order';
$mesg = $langs->transnoentitiesnoconv("EMailTextOrderValidated", $link);
break;
+ case 'ORDER_CLOSE':
+ $link = '
'.$newref.' ';
+ $dir_output = $conf->commande->dir_output."/".get_exdir(0, 0, 0, 1, $object, 'commande');
+ $object_type = 'order';
+ $mesg = $langs->transnoentitiesnoconv("EMailTextOrderClose", $link);
+ break;
case 'PROPAL_VALIDATE':
$link = '
'.$newref.' ';
$dir_output = $conf->propal->multidir_output[$object->entity]."/".get_exdir(0, 0, 0, 1, $object, 'propal');
diff --git a/htdocs/core/class/smtps.class.php b/htdocs/core/class/smtps.class.php
index 46193e1efea..c6eb249be5c 100644
--- a/htdocs/core/class/smtps.class.php
+++ b/htdocs/core/class/smtps.class.php
@@ -228,6 +228,7 @@ class SMTPs
// @CHANGE LDR
public $log = '';
+ public $lastretval = '';
private $_errorsTo = '';
private $_deliveryReceipt = 0;
private $_trackId = '';
@@ -564,7 +565,7 @@ class SMTPs
// Most servers expect a 2nd pass of EHLO after TLS is established to get another time
// the answer with list of supported AUTH methods. They may differs between non STARTTLS and with STARTTLS.
if (! $_retVal = $this->socket_send_str('EHLO '.$hosth, '250')) {
- $this->_setErr(126, '"'.$hosth.'" does not support authenticated connections. Error after sending EHLO '.$hosth);
+ $this->_setErr(126, '"'.$hosth.'" does not support authenticated connections or temporary error. Error after 2nd sending EHLO '.$hosth.' : '.$this->lastretval);
return $_retVal;
}
}
@@ -614,7 +615,7 @@ class SMTPs
$this->_setErr(130, 'Invalid Authentication Credentials.');
}
} else {
- $this->_setErr(126, '"'.$host.'" does not support authenticated connections. Error after sending EHLO '.$hosth);
+ $this->_setErr(126, '"'.$host.'" does not support authenticated connections or temporary error. Error after sending EHLO '.$hosth.' : '.$this->lastretval);
}
return $_retVal;
@@ -670,10 +671,11 @@ class SMTPs
}
}
+ // Send the HELO message to the SMTP server
$_retVal = $this->socket_send_str('HELO '.$hosth, '250');
}
- // Well, did we get to the server?
+ // Well, did we get the server answer with correct code ?
if ($_retVal) {
// From this point onward most server response codes should be 250
// Specify who the mail is from....
@@ -716,8 +718,11 @@ class SMTPs
// Now tell the server we are done and close the socket...
fputs($this->socket, 'QUIT');
- fclose($this->socket);
+ } else {
+ // We got error code into $this->lastretval
}
+
+ fclose($this->socket);
}
return $_retVal;
@@ -1871,7 +1876,9 @@ class SMTPs
* using SMTP Extensions
*
* @param resource $socket Socket handler
- * @param string $response Response. Example: "550 5.7.1 https://support.google.com/a/answer/6140680#invalidcred j21sm814390wre.3"
+ * @param string $response Expected response ('250', ...). Example of response we can get:
+ * "421 4.7.0 Try again later, closing connection. (EHLO) nb21-20020a1709071c9500b0093d0d964affsm869534ejc.73 - gsmtp"
+ * "550 5.7.1 https://support.google.com/a/answer/6140680#invalidcred j21sm814390wre.3"
* @return boolean True or false
*/
public function server_parse($socket, $response)
@@ -1898,8 +1905,10 @@ class SMTPs
$limit++;
}
+ $this->lastretval = substr($server_response, 0, 3);
+
if (!(substr($server_response, 0, 3) == $response)) {
- $this->_setErr(120, "Ran into problems sending Mail.\r\nResponse:".$server_response);
+ $this->_setErr(120, "Ran into problems sending Mail.\r\nResponse: ".$server_response);
$_retVal = false;
}
@@ -1911,9 +1920,9 @@ class SMTPs
* Send str
*
* @param string $_strSend String to send
- * @param string $_returnCode Return code
+ * @param string $_returnCode Expected return code
* @param string $CRLF CRLF
- * @return boolean|null True or false
+ * @return boolean|null True or false
*/
public function socket_send_str($_strSend, $_returnCode = null, $CRLF = "\r\n")
{
@@ -1929,6 +1938,8 @@ class SMTPs
if ($_returnCode) {
return $this->server_parse($this->socket, $_returnCode);
}
+
+ return null;
}
// =============================================================
@@ -1950,7 +1961,7 @@ class SMTPs
}
/**
- * Returns errors codes and messages for Class
+ * Returns applicative errors codes and messages for Class (not the SMTP error code)
*
* @return string $_errMsg Error Message
*/
diff --git a/htdocs/core/class/timespent.class.php b/htdocs/core/class/timespent.class.php
index f6ae2b45c71..0f91b53e26a 100755
--- a/htdocs/core/class/timespent.class.php
+++ b/htdocs/core/class/timespent.class.php
@@ -752,10 +752,11 @@ class TimeSpent extends CommonObject
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = " data-params='".json_encode($params)."'";
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
$url = dol_buildpath('/timespent/timespent_card.php', 1).'?id='.$this->id;
@@ -776,7 +777,7 @@ class TimeSpent extends CommonObject
$label = $langs->trans("ShowTimeSpent");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
$linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
} else {
$linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
diff --git a/htdocs/core/class/vcard.class.php b/htdocs/core/class/vcard.class.php
index 7b5a320f678..0bb532e33c4 100644
--- a/htdocs/core/class/vcard.class.php
+++ b/htdocs/core/class/vcard.class.php
@@ -421,6 +421,9 @@ class vCard
if (!empty($object->socialnetworks)) {
foreach ($object->socialnetworks as $key => $val) {
+ if (empty($val)) { // Disacard social network if empty
+ continue;
+ }
$urlsn = '';
if ($key == 'linkedin') {
if (!preg_match('/^http/', $val)) {
diff --git a/htdocs/core/db/mysqli.class.php b/htdocs/core/db/mysqli.class.php
index 593fa770536..e36fc547fa0 100644
--- a/htdocs/core/db/mysqli.class.php
+++ b/htdocs/core/db/mysqli.class.php
@@ -935,17 +935,17 @@ class DoliDBMysqli extends DoliDB
if ($field_desc['null'] == 'not null' || $field_desc['null'] == 'NOT NULL') {
// We will try to change format of column to NOT NULL. To be sure the ALTER works, we try to update fields that are NULL
if ($field_desc['type'] == 'varchar' || $field_desc['type'] == 'text') {
- $sqlbis = "UPDATE ".$table." SET ".$field_name." = '".$this->escape($field_desc['default'] ? $field_desc['default'] : '')."' WHERE ".$field_name." IS NULL";
+ $sqlbis = "UPDATE ".$table." SET ".$field_name." = '".$this->escape(isset($field_desc['default']) ? $field_desc['default'] : '')."' WHERE ".$field_name." IS NULL";
$this->query($sqlbis);
} elseif ($field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') {
- $sqlbis = "UPDATE ".$table." SET ".$field_name." = ".((int) $this->escape($field_desc['default'] ? $field_desc['default'] : 0))." WHERE ".$field_name." IS NULL";
+ $sqlbis = "UPDATE ".$table." SET ".$field_name." = ".((int) $this->escape(isset($field_desc['default']) ? $field_desc['default'] : 0))." WHERE ".$field_name." IS NULL";
$this->query($sqlbis);
}
$sql .= " NOT NULL";
}
- if ($field_desc['default'] != '') {
+ if (isset($field_desc['default']) && $field_desc['default'] != '') {
if ($field_desc['type'] == 'double' || $field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') {
$sql .= " DEFAULT ".$this->escape($field_desc['default']);
} elseif ($field_desc['type'] != 'text') {
diff --git a/htdocs/core/db/pgsql.class.php b/htdocs/core/db/pgsql.class.php
index 069afeca4ff..fb18ed0f161 100644
--- a/htdocs/core/db/pgsql.class.php
+++ b/htdocs/core/db/pgsql.class.php
@@ -1183,7 +1183,7 @@ class DoliDBPgsql extends DoliDB
$sql = "ALTER TABLE ".$table." ADD ".$field_name." ";
$sql .= $field_desc['type'];
if (preg_match("/^[^\s]/i", $field_desc['value'])) {
- if (!in_array($field_desc['type'], array('int', 'date', 'datetime')) && $field_desc['value']) {
+ if (!in_array($field_desc['type'], array('smallint', 'int', 'date', 'datetime')) && $field_desc['value']) {
$sql .= "(".$field_desc['value'].")";
}
}
@@ -1225,23 +1225,25 @@ class DoliDBPgsql extends DoliDB
{
// phpcs:enable
$sql = "ALTER TABLE ".$table;
- $sql .= " MODIFY COLUMN ".$field_name." ".$field_desc['type'];
- if (in_array($field_desc['type'], array('double', 'varchar')) && $field_desc['value']) {
- $sql .= "(".$field_desc['value'].")";
+ $sql .= " ALTER COLUMN ".$this->escape($field_name)." TYPE ".$field_desc['type'];
+ if (preg_match("/^[^\s]/i", $field_desc['value'])) {
+ if (!in_array($field_desc['type'], array('smallint', 'int', 'date', 'datetime')) && $field_desc['value']) {
+ $sql .= "(".$field_desc['value'].")";
+ }
}
if ($field_desc['null'] == 'not null' || $field_desc['null'] == 'NOT NULL') {
// We will try to change format of column to NOT NULL. To be sure the ALTER works, we try to update fields that are NULL
if ($field_desc['type'] == 'varchar' || $field_desc['type'] == 'text') {
- $sqlbis = "UPDATE ".$table." SET ".$field_name." = '".$this->escape($field_desc['default'] ? $field_desc['default'] : '')."' WHERE ".$field_name." IS NULL";
+ $sqlbis = "UPDATE ".$table." SET ".$this->escape($field_name)." = '".$this->escape(isset($field_desc['default']) ? $field_desc['default'] : '')."' WHERE ".$this->escape($field_name)." IS NULL";
$this->query($sqlbis);
} elseif ($field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') {
- $sqlbis = "UPDATE ".$table." SET ".$field_name." = ".((int) $this->escape($field_desc['default'] ? $field_desc['default'] : 0))." WHERE ".$field_name." IS NULL";
+ $sqlbis = "UPDATE ".$table." SET ".$this->escape($field_name)." = ".((int) $this->escape(isset($field_desc['default']) ? $field_desc['default'] : 0))." WHERE ".$this->escape($field_name)." IS NULL";
$this->query($sqlbis);
}
}
- if ($field_desc['default'] != '') {
+ if (isset($field_desc['default']) && $field_desc['default'] != '') {
if ($field_desc['type'] == 'double' || $field_desc['type'] == 'tinyint' || $field_desc['type'] == 'int') {
$sql .= " DEFAULT ".$this->escape($field_desc['default']);
} elseif ($field_desc['type'] != 'text') {
diff --git a/htdocs/core/js/lib_foot.js.php b/htdocs/core/js/lib_foot.js.php
index 963e357199a..297a33b93f5 100644
--- a/htdocs/core/js/lib_foot.js.php
+++ b/htdocs/core/js/lib_foot.js.php
@@ -85,12 +85,13 @@ if (empty($conf->dol_no_mouse_hover)) {
tooltipClass: "mytooltip",
open: function (event, ui) {
var elem = $(this);
- var params = $(this).attr("data-params");
+ var params = JSON.parse($(this).attr("data-params"));
+ params.token = "'.currentToken().'";
$.ajax({
url:"' . dol_buildpath('/core/ajax/ajaxtooltip.php', 1) . '",
type: "post",
async: false,
- data: JSON.parse(params),
+ data: params,
success: function(response){
// Setting content option
elem.tooltip("option","content",response);
diff --git a/htdocs/core/js/lib_head.js.php b/htdocs/core/js/lib_head.js.php
index 8c4d958abac..d721ec78873 100644
--- a/htdocs/core/js/lib_head.js.php
+++ b/htdocs/core/js/lib_head.js.php
@@ -1292,21 +1292,4 @@ $(document).ready(function() {
});
-/*
- * Hacky fix for a bug in select2 with jQuery 3.6.0's new nested-focus "protection"
- * see: https://github.com/select2/select2/issues/5993
- * see: https://github.com/jquery/jquery/issues/4382
- *
- * TODO: Recheck with the select2 GH issue and remove once this is fixed on their side
- */
-$(document).on('select2:open', () => {
- console.log("Execute the focus (click on combo or use space when on component");
- let allFound = document.querySelectorAll('.select2-container--open .select2-search__field');
- $(this).one('mouseup keyup',()=>{
- setTimeout(()=>{
- allFound[allFound.length - 1].focus();
- },0);
- });
-});
-
// End of lib_head.js.php
diff --git a/htdocs/core/lib/admin.lib.php b/htdocs/core/lib/admin.lib.php
index 04a04ee8cac..0064fad3bc7 100644
--- a/htdocs/core/lib/admin.lib.php
+++ b/htdocs/core/lib/admin.lib.php
@@ -1716,7 +1716,7 @@ function form_constantes($tableau, $strictw3c = 0, $helptext = '', $text = 'Valu
print '
';
- // Show constant
+ // Show label of parameter
print '';
if (empty($strictw3c)) {
print ' ';
@@ -1725,8 +1725,11 @@ function form_constantes($tableau, $strictw3c = 0, $helptext = '', $text = 'Valu
print ' ';
print ' ';
print ' ';
-
- print ($label ? $label : $langs->trans('Desc'.$const));
+ if (!empty($tableau[$key]['tooltip'])) {
+ print $form->textwithpicto($label ? $label : $langs->trans('Desc'.$const), $tableau[$key]['tooltip']);
+ } else {
+ print ($label ? $label : $langs->trans('Desc'.$const));
+ }
if ($const == 'ADHERENT_MAILMAN_URL') {
print '. '.$langs->trans("Example").': '.img_down().' ';
diff --git a/htdocs/core/lib/ajax.lib.php b/htdocs/core/lib/ajax.lib.php
index fde2a988cf7..043d9e6e18e 100644
--- a/htdocs/core/lib/ajax.lib.php
+++ b/htdocs/core/lib/ajax.lib.php
@@ -696,8 +696,8 @@ function ajax_constantonoff($code, $input = array(), $entity = null, $revertonof
* @param Object $object Object to set
* @param string $code Name of property in object : 'status' or 'status_buy' for product by example
* @param string $field Name of database field : 'tosell' or 'tobuy' for product by example
- * @param string $text_on Text if on
- * @param string $text_off Text if off
+ * @param string $text_on Text if on ('Text' or 'Text:css picto on')
+ * @param string $text_off Text if off ('Text' or 'Text:css picto on')
* @param array $input Array of type->list of CSS element to switch. Example: array('disabled'=>array(0=>'cssid'))
* @param string $morecss More CSS
* @param string $htmlname Name of HTML component. Keep '' or use a different value if you need to use this component several time on same page for same property.
@@ -710,6 +710,7 @@ function ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input =
if (empty($htmlname)) {
$htmlname = $code;
}
+ //var_dump($object->module); var_dump($object->element);
$out = '';
- $out .= ''.img_picto($langs->trans($text_off), 'switch_off').' ';
- $out .= ''.img_picto($langs->trans($text_on), 'switch_on').' ';
+
+ $switchon = 'switch_on';
+ $switchoff = 'switch_off';
+ $tmparray = explode(':', $text_on);
+ if (!empty($tmparray[1])) {
+ $text_on = $tmparray[0];
+ $switchon = $tmparray[1];
+ }
+ $tmparray = explode(':', $text_off);
+ if (!empty($tmparray[1])) {
+ $text_off = $tmparray[0];
+ $switchoff = $tmparray[1];
+ }
+
+ $out .= ''.img_picto($langs->trans($text_off), $switchoff).' ';
+ $out .= ''.img_picto($langs->trans($text_on), $switchon).' ';
return $out;
}
diff --git a/htdocs/core/lib/company.lib.php b/htdocs/core/lib/company.lib.php
index 04874dd9d90..e2e10a8de48 100644
--- a/htdocs/core/lib/company.lib.php
+++ b/htdocs/core/lib/company.lib.php
@@ -1380,7 +1380,7 @@ function show_contacts($conf, $langs, $db, $object, $backtopage = '', $showuserl
// Birthday
if (!empty($arrayfields['t.birthday']['checked'])) {
print ' ';
- print dol_print_date($obj->birthday);
+ print dol_print_date($db->jdate($obj->birthday));
print ' ';
}
@@ -2297,3 +2297,109 @@ function addMailingEventTypeSQL($actioncode, $objcon, $filterobj)
return '';
}
}
+
+
+
+/**
+ * Show footer of company in HTML pages
+ *
+ * @param Societe $fromcompany Third party
+ * @param Translate $langs Output language
+ * @param int $addformmessage Add the payment form message
+ * @param string $suffix Suffix to use on constants
+ * @param Object $object Object related to payment
+ * @return void
+ */
+function htmlPrintOnlineFooter($fromcompany, $langs, $addformmessage = 0, $suffix = '', $object = null)
+{
+ global $conf;
+
+ $reg = array();
+
+ // Juridical status
+ $line1 = "";
+ if ($fromcompany->forme_juridique_code) {
+ $line1 .= ($line1 ? " - " : "").getFormeJuridiqueLabel($fromcompany->forme_juridique_code);
+ }
+ // Capital
+ if ($fromcompany->capital) {
+ $line1 .= ($line1 ? " - " : "").$langs->transnoentities("CapitalOf", $fromcompany->capital)." ".$langs->transnoentities("Currency".$conf->currency);
+ }
+ // Prof Id 1
+ if ($fromcompany->idprof1 && ($fromcompany->country_code != 'FR' || !$fromcompany->idprof2)) {
+ $field = $langs->transcountrynoentities("ProfId1", $fromcompany->country_code);
+ if (preg_match('/\((.*)\)/i', $field, $reg)) {
+ $field = $reg[1];
+ }
+ $line1 .= ($line1 ? " - " : "").$field.": ".$fromcompany->idprof1;
+ }
+ // Prof Id 2
+ if ($fromcompany->idprof2) {
+ $field = $langs->transcountrynoentities("ProfId2", $fromcompany->country_code);
+ if (preg_match('/\((.*)\)/i', $field, $reg)) {
+ $field = $reg[1];
+ }
+ $line1 .= ($line1 ? " - " : "").$field.": ".$fromcompany->idprof2;
+ }
+
+ // Second line of company infos
+ $line2 = "";
+ // Prof Id 3
+ if ($fromcompany->idprof3) {
+ $field = $langs->transcountrynoentities("ProfId3", $fromcompany->country_code);
+ if (preg_match('/\((.*)\)/i', $field, $reg)) {
+ $field = $reg[1];
+ }
+ $line2 .= ($line2 ? " - " : "").$field.": ".$fromcompany->idprof3;
+ }
+ // Prof Id 4
+ if ($fromcompany->idprof4) {
+ $field = $langs->transcountrynoentities("ProfId4", $fromcompany->country_code);
+ if (preg_match('/\((.*)\)/i', $field, $reg)) {
+ $field = $reg[1];
+ }
+ $line2 .= ($line2 ? " - " : "").$field.": ".$fromcompany->idprof4;
+ }
+ // IntraCommunautary VAT
+ if ($fromcompany->tva_intra != '') {
+ $line2 .= ($line2 ? " - " : "").$langs->transnoentities("VATIntraShort").": ".$fromcompany->tva_intra;
+ }
+
+ print ''."\n";
+
+ print ''."\n";
+ print ' ';
+ if ($addformmessage) {
+ print '';
+ print ' ';
+
+ $parammessageform = 'ONLINE_PAYMENT_MESSAGE_FORM_'.$suffix;
+ if (!empty($conf->global->$parammessageform)) {
+ print $langs->transnoentities($conf->global->$parammessageform);
+ } elseif (!empty($conf->global->ONLINE_PAYMENT_MESSAGE_FORM)) {
+ print $langs->transnoentities($conf->global->ONLINE_PAYMENT_MESSAGE_FORM);
+ }
+
+ // Add other message if VAT exists
+ if (!empty($object->total_vat) || !empty($object->total_tva)) {
+ $parammessageform = 'ONLINE_PAYMENT_MESSAGE_FORMIFVAT_'.$suffix;
+ if (!empty($conf->global->$parammessageform)) {
+ print $langs->transnoentities($conf->global->$parammessageform);
+ } elseif (!empty($conf->global->ONLINE_PAYMENT_MESSAGE_FORMIFVAT)) {
+ print $langs->transnoentities($conf->global->ONLINE_PAYMENT_MESSAGE_FORMIFVAT);
+ }
+ }
+ }
+
+ print ' '."\n";
+ print $fromcompany->name.' ';
+ print $line1;
+ if (strlen($line1.$line2) > 50) {
+ print ' ';
+ } else {
+ print ' - ';
+ }
+ print $line2;
+ print ' ';
+ print ' '."\n";
+}
diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php
index 60adcc64003..d0c2d9998b4 100644
--- a/htdocs/core/lib/files.lib.php
+++ b/htdocs/core/lib/files.lib.php
@@ -5,6 +5,7 @@
* Copyright (C) 2015 Marcos García
* Copyright (C) 2016 Raphaël Doursenaud
* Copyright (C) 2019 Frédéric France
+ * Copyright (C) 2023 Lenin Rivas
*
* 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
@@ -707,12 +708,14 @@ function dolReplaceInFile($srcfile, $arrayreplacement, $destfile = '', $newmask
* @param string $destfile Destination file (can't be a directory)
* @param int $newmask Mask for new file (0 by default means $conf->global->MAIN_UMASK). Example: '0666'
* @param int $overwriteifexists Overwrite file if exists (1 by default)
+ * @param int $testvirus Do an antivirus test. Move is canceled if a virus is found.
+ * @param int $indexdatabase Index new file into database.
* @return int <0 if error, 0 if nothing done (dest file already exists and overwriteifexists=0), >0 if OK
* @see dol_delete_file() dolCopyDir()
*/
-function dol_copy($srcfile, $destfile, $newmask = 0, $overwriteifexists = 1)
+function dol_copy($srcfile, $destfile, $newmask = 0, $overwriteifexists = 1, $testvirus = 0, $indexdatabase = 0)
{
- global $conf;
+ global $conf, $db, $user;
dol_syslog("files.lib.php::dol_copy srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists);
@@ -737,6 +740,17 @@ function dol_copy($srcfile, $destfile, $newmask = 0, $overwriteifexists = 1)
dol_syslog("files.lib.php::dol_copy failed Permission denied to write into target directory ".$newdirdestfile, LOG_WARNING);
return -2;
}
+
+ // Check virus
+ $testvirusarray = array();
+ if ($testvirus) {
+ $testvirusarray = dolCheckVirus($srcfile);
+ if (count($testvirusarray)) {
+ dol_syslog("files.lib.php::dol_copy canceled because a virus was found into source file. we ignore the copy request.", LOG_WARNING);
+ return -3;
+ }
+ }
+
// Copy with overwriting if exists
$result = @copy($newpathofsrcfile, $newpathofdestfile);
//$result=copy($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @
@@ -754,7 +768,64 @@ function dol_copy($srcfile, $destfile, $newmask = 0, $overwriteifexists = 1)
dolChmod($newpathofdestfile, $newmask);
- return 1;
+ if ($result && $indexdatabase) {
+ // Add entry into ecm database
+ $rel_filetocopyafter = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $newpathofdestfile);
+ if (!preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filetocopyafter)) { // If not a tmp file
+ $rel_filetocopyafter = preg_replace('/^[\\/]/', '', $rel_filetocopyafter);
+ //var_dump($rel_filetorenamebefore.' - '.$rel_filetocopyafter);exit;
+
+ dol_syslog("Try to copy also entries in database for: ".$rel_filetocopyafter, LOG_DEBUG);
+ include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
+
+ $ecmfiletarget = new EcmFiles($db);
+ $resultecmtarget = $ecmfiletarget->fetch(0, '', $rel_filetocopyafter);
+ if ($resultecmtarget > 0) { // An entry for target name already exists for target, we delete it, a new one will be created.
+ dol_syslog("ECM dest file found, remove it", LOG_DEBUG);
+ $ecmfiletarget->delete($user);
+ } else {
+ dol_syslog("ECM dest file not found, create it", LOG_DEBUG);
+ }
+
+ $ecmSrcfile = new EcmFiles($db);
+ $resultecm = $ecmSrcfile->fetch(0, '', $srcfile);
+ if ($resultecm) {
+ dol_syslog("Fetch src file ok", LOG_DEBUG);
+ } else {
+ dol_syslog("Fetch src file error", LOG_DEBUG);
+ }
+
+ $ecmfile = new EcmFiles($db);
+ $filename = basename($rel_filetocopyafter);
+ $rel_dir = dirname($rel_filetocopyafter);
+ $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
+ $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
+
+ $ecmfile->filepath = $rel_dir;
+ $ecmfile->filename = $filename;
+ $ecmfile->label = md5_file(dol_osencode($destfile)); // $destfile is a full path to file
+ $ecmfile->fullpath_orig = $srcfile;
+ $ecmfile->gen_or_uploaded = 'copy';
+ $ecmfile->description = $ecmSrcfile->description;
+ $ecmfile->keywords = $ecmSrcfile->keywords;
+ $resultecm = $ecmfile->create($user);
+ if ($resultecm < 0) {
+ dol_syslog("Create ECM file ok", LOG_DEBUG);
+ setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
+ } else {
+ dol_syslog("Create ECM file error", LOG_DEBUG);
+ setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
+ }
+
+ if ($resultecm > 0) {
+ $result = 1;
+ } else {
+ $result = -1;
+ }
+ }
+ }
+
+ return $result;
}
/**
@@ -2488,6 +2559,10 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity,
if ($modulepart == 'tva') {
$modulepart = 'tax-vat';
}
+ // Fix modulepart delivery
+ if ($modulepart == 'expedition' && strpos($original_file, 'receipt/') === 0) {
+ $modulepart = 'delivery';
+ }
//print 'dol_check_secure_access_document modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity;
dol_syslog('dol_check_secure_access_document modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity);
@@ -2901,7 +2976,7 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity,
}
$original_file = $conf->commande->multidir_output[$entity].'/'.$original_file;
$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('order').")";
- } elseif ($modulepart == 'project' && !empty($conf->project->dir_output)) {
+ } elseif ($modulepart == 'project' && !empty($conf->project->multidir_output[$entity])) {
// Wrapping pour les projets
if ($fuser->hasRight('projet', $lire) || preg_match('/^specimen/i', $original_file)) {
$accessallowed = 1;
@@ -2913,9 +2988,9 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity,
$accessallowed = checkUserAccessToObject($user, array('projet'), $tmpproject->id, 'projet&project', '', '', 'rowid', '');
}
}
- $original_file = $conf->project->dir_output.'/'.$original_file;
+ $original_file = $conf->project->multidir_output[$entity].'/'.$original_file;
$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")";
- } elseif ($modulepart == 'project_task' && !empty($conf->project->dir_output)) {
+ } elseif ($modulepart == 'project_task' && !empty($conf->project->multidir_output[$entity])) {
if ($fuser->hasRight('projet', $lire) || preg_match('/^specimen/i', $original_file)) {
$accessallowed = 1;
// If we known $id of project, call checkUserAccessToObject to check permission on properties and contact of project
@@ -2926,7 +3001,7 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity,
$accessallowed = checkUserAccessToObject($user, array('projet_task'), $tmptask->id, 'projet_task&project', '', '', 'rowid', '');
}
}
- $original_file = $conf->project->dir_output.'/'.$original_file;
+ $original_file = $conf->project->multidir_output[$entity].'/'.$original_file;
$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")";
} elseif (($modulepart == 'commande_fournisseur' || $modulepart == 'order_supplier') && !empty($conf->fournisseur->commande->dir_output)) {
// Wrapping pour les commandes fournisseurs
@@ -2970,14 +3045,14 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity,
if ($fuser->hasRight('expedition', $lire) || preg_match('/^specimen/i', $original_file)) {
$accessallowed = 1;
}
- $original_file = $conf->expedition->dir_output."/".(strpos('sending/', $original_file) === 0 ? '' : 'sending/').$original_file;
+ $original_file = $conf->expedition->dir_output."/".(strpos($original_file, 'sending/') === 0 ? '' : 'sending/').$original_file;
//$original_file = $conf->expedition->dir_output."/".$original_file;
} elseif (($modulepart == 'livraison' || $modulepart == 'delivery') && !empty($conf->expedition->dir_output)) {
// Delivery Note Wrapping
if ($fuser->hasRight('expedition', 'delivery', $lire) || preg_match('/^specimen/i', $original_file)) {
$accessallowed = 1;
}
- $original_file = $conf->expedition->dir_output."/".(strpos('receipt/', $original_file) === 0 ? '' : 'receipt/').$original_file;
+ $original_file = $conf->expedition->dir_output."/".(strpos($original_file, 'receipt/') === 0 ? '' : 'receipt/').$original_file;
} elseif ($modulepart == 'actions' && !empty($conf->agenda->dir_output)) {
// Wrapping pour les actions
if ($fuser->hasRight('agenda', 'myactions', $read) || preg_match('/^specimen/i', $original_file)) {
@@ -3129,41 +3204,45 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity,
// Define $accessallowed
$reg = array();
if (preg_match('/^([a-z]+)_user_temp$/i', $modulepart, $reg)) {
- if (empty($conf->{$reg[1]}->dir_temp)) { // modulepart not supported
+ $tmpmodule = $reg[1];
+ if (empty($conf->$tmpmodule->dir_temp)) { // modulepart not supported
dol_print_error('', 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
exit;
}
- if ($fuser->rights->{$reg[1]}->{$lire} || $fuser->rights->{$reg[1]}->{$read} || ($fuser->rights->{$reg[1]}->{$download})) {
+ if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
$accessallowed = 1;
}
$original_file = $conf->{$reg[1]}->dir_temp.'/'.$fuser->id.'/'.$original_file;
} elseif (preg_match('/^([a-z]+)_temp$/i', $modulepart, $reg)) {
- if (empty($conf->{$reg[1]}->dir_temp)) { // modulepart not supported
+ $tmpmodule = $reg[1];
+ if (empty($conf->$tmpmodule->dir_temp)) { // modulepart not supported
dol_print_error('', 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
exit;
}
- if ($fuser->rights->{$reg[1]}->{$lire} || $fuser->rights->{$reg[1]}->{$read} || ($fuser->rights->{$reg[1]}->{$download})) {
+ if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
$accessallowed = 1;
}
- $original_file = $conf->{$reg[1]}->dir_temp.'/'.$original_file;
+ $original_file = $conf->$tmpmodule->dir_temp.'/'.$original_file;
} elseif (preg_match('/^([a-z]+)_user$/i', $modulepart, $reg)) {
- if (empty($conf->{$reg[1]}->dir_output)) { // modulepart not supported
+ $tmpmodule = $reg[1];
+ if (empty($conf->$tmpmodule->dir_output)) { // modulepart not supported
dol_print_error('', 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
exit;
}
- if ($fuser->rights->{$reg[1]}->{$lire} || $fuser->rights->{$reg[1]}->{$read} || ($fuser->rights->{$reg[1]}->{$download})) {
+ if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
$accessallowed = 1;
}
- $original_file = $conf->{$reg[1]}->dir_output.'/'.$fuser->id.'/'.$original_file;
+ $original_file = $conf->$tmpmodule->dir_output.'/'.$fuser->id.'/'.$original_file;
} elseif (preg_match('/^massfilesarea_([a-z]+)$/i', $modulepart, $reg)) {
- if (empty($conf->{$reg[1]}->dir_output)) { // modulepart not supported
+ $tmpmodule = $reg[1];
+ if (empty($conf->$tmpmodule->dir_output)) { // modulepart not supported
dol_print_error('', 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
exit;
}
- if ($fuser->rights->{$reg[1]}->{$lire} || preg_match('/^specimen/i', $original_file)) {
+ if ($fuser->hasRight($tmpmodule, $lire) || preg_match('/^specimen/i', $original_file)) {
$accessallowed = 1;
}
- $original_file = $conf->{$reg[1]}->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
+ $original_file = $conf->$tmpmodule->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
} else {
if (empty($conf->$modulepart->dir_output)) { // modulepart not supported
dol_print_error('', 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.'). The module for this modulepart value may not be activated.');
@@ -3178,7 +3257,7 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity,
$accessallowed = 1;
}
}
- if (!empty($fuser->rights->$modulepart->{$lire}) || !empty($fuser->rights->$modulepart->{$read})) {
+ if ($fuser->hasRight($modulepart, $lire) || $fuser->hasRight($modulepart, $read)) {
$accessallowed = 1;
}
@@ -3316,3 +3395,87 @@ function getFilesUpdated(&$file_list, SimpleXMLElement $dir, $path = '', $pathre
return $file_list;
}
+
+/**
+ * Function to manage the drag and drop of a file.
+ * We use global variable $object
+ *
+ * @param string $htmlname The id of the component where we need to drag and drop
+ * @return string Js script to display
+ */
+function dragAndDropFileUpload($htmlname)
+{
+ global $object, $langs;
+
+ $out = "";
+ $out .= ''.img_picto("", 'download').' '.$langs->trans("DropFileToAddItToObject").'
';
+ $out .= "\n\n";
+ $out .= "\n";
+ return $out;
+}
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index 24dafa06f69..2da236006c0 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -210,7 +210,8 @@ function getEntity($element, $shared = 1, $currentobject = null)
{
global $conf, $mc, $hookmanager, $object, $action, $db;
- if (! is_object($hookmanager)) {
+ if (!is_object($hookmanager)) {
+ include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
$hookmanager = new HookManager($db);
}
@@ -535,6 +536,7 @@ function GETPOSTISARRAY($paramname, $method = 0)
* 'alphanohtml'=check there is no html content and no " and no ../
* 'aZ'=check it's a-z only
* 'aZ09'=check it's simple alpha string (recommended for keys)
+ * 'aZ09arobase'=check it's a string for an element type
* 'aZ09comma'=check it's a string for a sortfield or sortorder
* 'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string)
* 'nohtml'=check there is no html content
@@ -617,7 +619,7 @@ function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null
}
}
if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) {
- if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
+ if (!empty($_GET['action']) && (preg_match('/^create|^add_price|^make/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
// Now search in setup to overwrite default values
if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
if (isset($user->default_values[$relativepathstring]['createform'])) {
@@ -934,6 +936,14 @@ function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options
}
}
break;
+ case 'aZ09arobase': // great to sanitize objecttype parameter
+ if (!is_array($out)) {
+ $out = trim($out);
+ if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
+ $out = '';
+ }
+ }
+ break;
case 'aZ09comma': // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh
if (!is_array($out)) {
$out = trim($out);
@@ -1651,7 +1661,7 @@ function dol_ucwords($string, $encoding = "UTF-8")
* On Windows LOG_ERR=4, LOG_WARNING=5, LOG_NOTICE=LOG_INFO=6, LOG_DEBUG=6 si define_syslog_variables ou PHP 5.3+, 7 si dolibarr
* On Linux LOG_ERR=3, LOG_WARNING=4, LOG_NOTICE=5, LOG_INFO=6, LOG_DEBUG=7
* @param int $ident 1=Increase ident of 1 (after log), -1=Decrease ident of 1 (before log)
- * @param string $suffixinfilename When output is a file, append this suffix into default log filename.
+ * @param string $suffixinfilename When output is a file, append this suffix into default log filename. Example '_stripe', '_mail'
* @param string $restricttologhandler Force output of log only to this log handler
* @param array|null $logcontext If defined, an array with extra informations (can be used by some log handlers)
* @return void
@@ -1890,9 +1900,10 @@ function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0
* @param string $morecss More CSS on the link
* @param int $limittoshow Limit number of tabs to show. Use 0 to use automatic default value.
* @param string $moretabssuffix A suffix to use when you have several dol_get_fiche_head() in same page
+ * @param int $dragdropfile 0 (default) or 1. 1 enable a drop zone for file to be upload, 0 disable it
* @return string
*/
-function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
+function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
{
global $conf, $langs, $hookmanager;
@@ -2058,9 +2069,11 @@ function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab
}
if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
- $out .= "\n".''."\n";
+ $out .= "\n".'
'."\n";
+ }
+ if (!empty($dragdropfile)) {
+ $out .= dragAndDropFileUpload("dragDropAreaTabBar");
}
-
$parameters = array('tabname' => $active, 'out' => $out);
$reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
if ($reshook > 0) {
@@ -4118,9 +4131,9 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $
if (empty($srconly) && in_array($pictowithouttext, array(
'1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'angle-double-down', 'angle-double-up', 'asset',
- 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'bookmark', 'bom', 'briefcase-medical', 'bug', 'building',
+ 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bookmark', 'bom', 'briefcase-medical', 'bug', 'building',
'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
- 'cash-register', 'category', 'chart', 'check', 'clock', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cubes',
+ 'cash-register', 'category', 'chart', 'check', 'clock', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
'currency', 'multicurrency',
'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
@@ -4134,7 +4147,7 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $
'off', 'on', 'order',
'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
'stock', 'resize', 'service', 'stats', 'trip',
- 'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'switch_on_red', 'tools', 'unlink', 'uparrow', 'user', 'user-tie', 'vcard', 'wrench',
+ 'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'switch_on_warning', 'switch_on_red', 'tools', 'unlink', 'uparrow', 'user', 'user-tie', 'vcard', 'wrench',
'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
@@ -4166,7 +4179,7 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $
'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'billd'=>'file-medical',
'supplier_invoice'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'supplier_invoicer'=>'file-invoice-dollar', 'supplier_invoiced'=>'file-medical',
'bom'=>'shapes',
- 'card'=>'address-card', 'chart'=>'chart-line', 'company'=>'building', 'contact'=>'address-book', 'contract'=>'suitcase', 'collab'=>'people-arrows', 'conversation'=>'comments', 'country'=>'globe-americas', 'cron'=>'business-time',
+ 'card'=>'address-card', 'chart'=>'chart-line', 'company'=>'building', 'contact'=>'address-book', 'contract'=>'suitcase', 'collab'=>'people-arrows', 'conversation'=>'comments', 'country'=>'globe-americas', 'cron'=>'business-time', 'cross'=>'times',
'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd',
'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins',
'accounting'=>'search-dollar', 'category'=>'tag', 'dollyrevert'=>'dolly',
@@ -4180,7 +4193,7 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $
'member'=>'user-alt', 'meeting'=>'chalkboard-teacher', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right',
'trip'=>'wallet', 'expensereport'=>'wallet', 'group'=>'users', 'movement'=>'people-carry',
'sign-out'=>'sign-out-alt',
- 'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'switch_on_red'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star',
+ 'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'switch_on_warning'=>'toggle-on', 'switch_on_red'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star',
'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'filter'=>'filter',
'list-alt'=>'list-alt', 'calendarlist'=>'bars', 'calendar'=>'calendar-alt', 'calendarmonth'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table',
'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'currency'=>'dollar-sign', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice',
@@ -4264,7 +4277,7 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $
'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account',
'order'=>'infobox-commande',
'user'=>'infobox-adherent', 'users'=>'infobox-adherent',
- 'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 'switch_on_red'=>'font-status8',
+ 'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 'switch_on_warning'=>'font-status4 warning', 'switch_on_red'=>'font-status8',
'holiday'=>'infobox-holiday', 'info'=>'opacityhigh', 'invoice'=>'infobox-commande',
'knowledgemanagement'=>'infobox-contrat rotate90', 'loan'=>'infobox-bank_account',
'payment'=>'infobox-bank_account', 'payment_vat'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'pos'=>'infobox-bank_account', 'project'=>'infobox-project', 'projecttask'=>'infobox-project',
@@ -5727,8 +5740,8 @@ function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0,
* @param integer $form Type of format, HTML or not (not by default)
* @param Translate|string $outlangs Object langs for output. '' use default lang. 'none' use international separators.
* @param int $trunc 1=Truncate if there is more decimals than MAIN_MAX_DECIMALS_SHOWN (default), 0=Does not truncate. Deprecated because amount are rounded (to unit or total amount accurancy) before beeing inserted into database or after a computation, so this parameter should be useless.
- * @param int $rounding MINIMUM number of decimal to show. 0=no change, -1=we use min($conf->global->MAIN_MAX_DECIMALS_UNIT,$conf->global->MAIN_MAX_DECIMALS_TOT)
- * @param int|string $forcerounding Force the MAXIMUM of decimal to forcerounding decimal (-1=no change, 'MU' or 'MT' or numeric to round to MU or MT or to a given number of decimal)
+ * @param int $rounding MINIMUM number of decimal to show: 0=no change, -1=we use min($conf->global->MAIN_MAX_DECIMALS_UNIT,$conf->global->MAIN_MAX_DECIMALS_TOT)
+ * @param int|string $forcerounding MAXIMUM number of decimal to forcerounding decimal: -1=no change, 'MU' or 'MT' or numeric to round to MU or MT or to a given number of decimal
* @param string $currency_code To add currency symbol (''=add nothing, 'auto'=Use default currency, 'XXX'=add currency symbols for XXX currency)
* @return string String with formated amount
*
@@ -5743,7 +5756,7 @@ function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $
$amount = 0; // To have a numeric value if amount not defined or = ''
}
$amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
- if ($rounding < 0) {
+ if ($rounding == -1) {
$rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT);
}
$nbdecimal = $rounding;
@@ -7564,7 +7577,7 @@ function dol_textishtml($msg, $option = 0)
return true;
} elseif (preg_match('/<\/textarea/i', $msg)) {
return true;
- } elseif (preg_match('/<(b|em|i|u)>/i', $msg)) {
+ } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
return true;
} elseif (preg_match('/ /i', $msg)) {
+ } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
return true;
} elseif (preg_match('/ /i', $msg)) {
return true;
@@ -7642,16 +7655,17 @@ function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
* @param int $onlykey 1=Do not calculate some heavy values of keys (performance enhancement when we need only the keys), 2=Values are trunc and html sanitized (to use for help tooltip)
* @param array $exclude Array of family keys we want to exclude. For example array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...)
* @param Object $object Object for keys on object
+ * @param array $include Array of family keys we want to include. For example array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...)
* @return array Array of substitutions
* @see setSubstitFromObject()
*/
-function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null)
+function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
{
global $db, $conf, $mysoc, $user, $extrafields;
$substitutionarray = array();
- if (empty($exclude) || !in_array('user', $exclude)) {
+ if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include))) {
// Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
// this will include signature content first and then replace var found into content of signature
//var_dump($onlykey);
@@ -7680,7 +7694,7 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
));
}
}
- if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc)) {
+ if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
$substitutionarray = array_merge($substitutionarray, array(
'__MYCOMPANY_NAME__' => $mysoc->name,
'__MYCOMPANY_EMAIL__' => $mysoc->email,
@@ -7704,7 +7718,7 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
));
}
- if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude))) {
+ if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
if ($onlykey) {
$substitutionarray['__ID__'] = '__ID__';
$substitutionarray['__REF__'] = '__REF__';
@@ -7738,7 +7752,7 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
$substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
$substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
}
- if (isModEnabled('adherent') && (!is_object($object) || $object->element == 'adherent')) {
+ if (isModEnabled('adherent') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
$substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
$substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
$substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
@@ -7748,7 +7762,7 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
$substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
}
// add variables subtitutions ticket
- if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket')) {
+ if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
$substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
$substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
$substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
@@ -7760,28 +7774,28 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
$substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
}
- if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature')) {
+ if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
$substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
$substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
$substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
}
- if (isModEnabled('project')) { // Most objects
+ if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
$substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
$substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
$substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
/*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
$substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
}
- if (isModEnabled('contrat') && (!is_object($object) || $object->element == 'contract')) {
+ if (isModEnabled('contrat') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
$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 (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal')) {
+ if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
$substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
}
- if (isModEnabled("ficheinter") && (!is_object($object) || $object->element == 'fichinter')) {
+ if (isModEnabled("ficheinter") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
$substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
}
$substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
@@ -8035,6 +8049,10 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
}
if (is_object($object) && $object->element == 'member') {
$typeforonlinepayment = 'member';
+ $amounttouse = 0;
+ if (!empty($object->last_subscription_amount)) {
+ $amounttouse = $object->last_subscription_amount;
+ }
}
if (is_object($object) && $object->element == 'contrat') {
$typeforonlinepayment = 'contract';
@@ -8042,7 +8060,8 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
if (is_object($object) && $object->element == 'fichinter') {
$typeforonlinepayment = 'ficheinter';
}
- $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__']);
+
+ $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
$paymenturl = $url;
}
@@ -8050,14 +8069,6 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
$substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ?str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
$substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
- if (is_object($object) && $object->element == 'propal') {
- require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
- $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref);
- }
- if (is_object($object) && $object->element == 'fichinter') {
- require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
- $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref);
- }
if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal') {
$substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
} else {
@@ -8091,6 +8102,8 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
if (is_object($object) && $object->element == 'propal') {
$substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
+ require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
+ $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref);
}
if (is_object($object) && $object->element == 'commande') {
$substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
@@ -8100,9 +8113,13 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
}
if (is_object($object) && $object->element == 'contrat') {
$substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
+ require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
+ $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref);
}
if (is_object($object) && $object->element == 'fichinter') {
$substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
+ require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
+ $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref);
}
if (is_object($object) && $object->element == 'supplier_proposal') {
$substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
@@ -8119,7 +8136,7 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
}
}
}
- if (empty($exclude) || !in_array('objectamount', $exclude)) {
+ if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
$substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
@@ -8175,7 +8192,7 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
}
//var_dump($substitutionarray['__AMOUNT_FORMATED__']);
- if (empty($exclude) || !in_array('date', $exclude)) {
+ if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
$now = dol_now();
@@ -8212,7 +8229,7 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
if (isModEnabled('multicompany')) {
$substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
}
- if (empty($exclude) || !in_array('system', $exclude)) {
+ if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
$substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
$substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
$substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
@@ -11193,10 +11210,13 @@ function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $u
/**
* Get an array with properties of an element.
- * Called by fetchObjectByElement.
*
- * @param string $element_type Element type (Value of $object->element). Example: 'action', 'facture', 'project_task' or 'object@mymodule'...
+ * @param string $element_type Element type (Value of $object->element). Example:
+ * 'action', 'facture', 'project_task',
+ * 'myobject@mymodule' or
+ * 'myobject_mysubobject' (where mymodule = myobject, like 'project_task')
* @return array (module, classpath, element, subelement, classfile, classname)
+ * @see fetchObjectByElement()
*/
function getElementProperties($element_type)
{
@@ -11204,102 +11224,150 @@ function getElementProperties($element_type)
$classfile = $classname = $classpath = '';
- // Parse element/subelement (ex: project_task)
+ // Parse element/subelement
$module = $element_type;
$element = $element_type;
$subelement = $element_type;
- // If we ask an resource form external module (instead of default path)
- if (preg_match('/^([^@]+)@([^@]+)$/i', $element_type, $regs)) {
+ // If we ask a resource form external module (instead of default path)
+ if (preg_match('/^([^@]+)@([^@]+)$/i', $element_type, $regs)) { // 'myobject@mymodule'
$element = $subelement = $regs[1];
$module = $regs[2];
}
- //print ' 1. element : '.$element.' - module : '.$module .' ';
- if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) {
+ // If we ask a resource for a string with an element and a subelement
+ // Example 'project_task'
+ if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
$module = $element = $regs[1];
$subelement = $regs[2];
}
- // For compat
+ // For compat and To work with non standard path
if ($element_type == "action") {
$classpath = 'comm/action/class';
$subelement = 'Actioncomm';
$module = 'agenda';
- }
-
- // To work with non standard path
- if ($element_type == 'facture' || $element_type == 'invoice') {
+ } elseif ($element_type == 'adherent_type') {
+ $classpath = 'adherents/class';
+ $classfile = 'adherent_type';
+ $module = 'adherent';
+ $subelement = 'adherent_type';
+ $classname = 'AdherentType';
+ } elseif ($element_type == 'bank_account') {
+ $classpath = 'compta/bank/class';
+ $module = 'banque';
+ $classfile = 'account';
+ $classname = 'Account';
+ } elseif ($element_type == 'category') {
+ $classpath = 'categories/class';
+ $module = 'categorie';
+ $subelement = 'categorie';
+ } elseif ($element_type == 'contact') {
+ $classpath = 'contact/class';
+ $classfile = 'contact';
+ $module = 'societe';
+ $subelement = 'contact';
+ } elseif ($element_type == 'stock') {
+ $classpath = 'product/stock/class';
+ $classfile = 'entrepot';
+ $classname = 'Entrepot';
+ } elseif ($element_type == 'project') {
+ $classpath = 'projet/class';
+ $module = 'projet';
+ } elseif ($element_type == 'project_task') {
+ $classpath = 'projet/class';
+ $module = 'projet';
+ $subelement = 'task';
+ } elseif ($element_type == 'facture' || $element_type == 'invoice') {
$classpath = 'compta/facture/class';
$module = 'facture';
$subelement = 'facture';
- }
- if ($element_type == 'commande' || $element_type == 'order') {
+ } elseif ($element_type == 'commande' || $element_type == 'order') {
$classpath = 'commande/class';
$module = 'commande';
$subelement = 'commande';
- }
- if ($element_type == 'propal') {
+ } elseif ($element_type == 'propal') {
$classpath = 'comm/propal/class';
- }
- if ($element_type == 'supplier_proposal') {
+ } elseif ($element_type == 'shipping') {
+ $classpath = 'expedition/class';
+ $classfile = 'expedition';
+ $classname = 'Expedition';
+ $module = 'expedition';
+ } elseif ($element_type == 'supplier_proposal') {
$classpath = 'supplier_proposal/class';
- }
- if ($element_type == 'shipping') {
+ $module = 'supplier_proposal';
+ $element = 'supplierproposal';
+ $classfile = 'supplier_proposal';
+ $subelement = 'supplierproposal';
+ } elseif ($element_type == 'shipping') {
$classpath = 'expedition/class';
$subelement = 'expedition';
$module = 'expedition_bon';
- }
- if ($element_type == 'delivery') {
+ } elseif ($element_type == 'delivery') {
$classpath = 'delivery/class';
$subelement = 'delivery';
$module = 'delivery_note';
- }
- if ($element_type == 'contract') {
+ } elseif ($element_type == 'contract') {
$classpath = 'contrat/class';
$module = 'contrat';
$subelement = 'contrat';
- }
- if ($element_type == 'member') {
+ } elseif ($element_type == 'member') {
$classpath = 'adherents/class';
$module = 'adherent';
$subelement = 'adherent';
- }
- if ($element_type == 'cabinetmed_cons') {
+ } elseif ($element_type == 'cabinetmed_cons') {
$classpath = 'cabinetmed/class';
$module = 'cabinetmed';
$subelement = 'cabinetmedcons';
- }
- if ($element_type == 'fichinter') {
+ } elseif ($element_type == 'fichinter') {
$classpath = 'fichinter/class';
$module = 'ficheinter';
$subelement = 'fichinter';
- }
- if ($element_type == 'dolresource' || $element_type == 'resource') {
+ } elseif ($element_type == 'dolresource' || $element_type == 'resource') {
$classpath = 'resource/class';
$module = 'resource';
$subelement = 'dolresource';
- }
- if ($element_type == 'propaldet') {
+ } elseif ($element_type == 'propaldet') {
$classpath = 'comm/propal/class';
$module = 'propal';
$subelement = 'propaleligne';
- }
- if ($element_type == 'order_supplier') {
+ } elseif ($element_type == 'opensurvey_sondage') {
+ $classpath = 'opensurvey/class';
+ $module = 'opensurvey';
+ $subelement = 'opensurveysondage';
+ } elseif ($element_type == 'order_supplier') {
$classpath = 'fourn/class';
$module = 'fournisseur';
- $subelement = 'commandefournisseur';
$classfile = 'fournisseur.commande';
- }
- if ($element_type == 'invoice_supplier') {
+ $element = 'commande';
+ $subelement = '';
+ $classname = 'CommandeFournisseur';
+ } elseif ($element_type == 'invoice_supplier') {
$classpath = 'fourn/class';
$module = 'fournisseur';
- $subelement = 'facturefournisseur';
$classfile = 'fournisseur.facture';
- }
- if ($element_type == "service") {
+ $element = 'facture';
+ $subelement = '';
+ $classname = 'FactureFournisseur';
+ } elseif ($element_type == "service") {
$classpath = 'product/class';
$subelement = 'product';
+ } elseif ($element_type == 'salary') {
+ $classpath = 'salaries/class';
+ $module = 'salaries';
+ } elseif ($element_type == 'productlot') {
+ $module = 'productbatch';
+ $classpath = 'product/stock/class';
+ $classfile = 'productlot';
+ $classname = 'Productlot';
+ $element = 'productlot';
+ $subelement = '';
+ } elseif ($element_type == 'websitepage') {
+ $classpath = 'website/class';
+ $classfile = 'websitepage';
+ $classname = 'Websitepage';
+ $module = 'website';
+ $subelement = 'websitepage';
}
if (empty($classfile)) {
@@ -11328,25 +11396,39 @@ function getElementProperties($element_type)
* Inclusion of classes is automatic
*
* @param int $element_id Element id
- * @param string $element_type Element type
+ * @param string $element_type Element type ('module' or 'myobject@mymodule' or 'mymodule_myobject')
* @param string $element_ref Element ref (Use this or element_id but not both)
- * @return int|object object || 0 || -1 if error
+ * @return int|object object || 0 || <0 if error
*/
function fetchObjectByElement($element_id, $element_type, $element_ref = '')
{
- global $conf, $db;
+ global $db;
+
+ $ret = 0;
$element_prop = getElementProperties($element_type);
- if (is_array($element_prop) && $conf->{$element_prop['module']}->enabled) {
+ //var_dump($element_prop);
+
+ if (is_array($element_prop) && isModEnabled($element_prop['module'])) {
dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
- $objecttmp = new $element_prop['classname']($db);
- $ret = $objecttmp->fetch($element_id, $element_ref);
- if ($ret >= 0) {
- return $objecttmp;
+ if (class_exists($element_prop['classname'])) {
+ $classname = $element_prop['classname'];
+ $objecttmp = new $classname($db);
+ $ret = $objecttmp->fetch($element_id, $element_ref);
+ if ($ret >= 0) {
+ if (empty($objecttmp->module)) {
+ $objecttmp->module = $element_prop['module'];
+ }
+
+ return $objecttmp;
+ }
+ } else {
+ return -1;
}
}
- return 0;
+
+ return $ret;
}
/**
@@ -11368,6 +11450,7 @@ function isAFileWithExecutableContent($filename)
* Return the value of token currently saved into session with name 'newtoken'.
* This token must be send by any POST as it will be used by next page for comparison with value in session.
*
+ * @since Dolibarr v10.0.7
* @return string
*/
function newToken()
@@ -11379,6 +11462,7 @@ function newToken()
* Return the value of token currently saved into session with name 'token'.
* For ajax call, you must use this token as a parameter of the call into the js calling script (the called ajax php page must also set constant NOTOKENRENEWAL).
*
+ * @since Dolibarr v10.0.7
* @return string
*/
function currentToken()
diff --git a/htdocs/core/lib/functions2.lib.php b/htdocs/core/lib/functions2.lib.php
index 188b30116d7..ce639624f1a 100644
--- a/htdocs/core/lib/functions2.lib.php
+++ b/htdocs/core/lib/functions2.lib.php
@@ -1314,8 +1314,8 @@ function get_next_value($db, $mask, $table, $field, $where = '', $objsoc = '', $
if (empty($counter)) {
$counter = $maskoffset;
} elseif (preg_match('/[^0-9]/i', $counter)) {
- $counter = 0;
dol_syslog("Error, the last counter found is '".$counter."' so is not a numeric value. We will restart to 1.", LOG_ERR);
+ $counter = 0;
} elseif ($counter < $maskoffset && empty($conf->global->MAIN_NUMBERING_OFFSET_ONLY_FOR_FIRST)) {
$counter = $maskoffset;
}
@@ -1936,14 +1936,14 @@ function getListOfModels($db, $type, $maxfilenamelength = 0)
$sql .= " ORDER BY description DESC";
dol_syslog('/core/lib/function2.lib.php::getListOfModels', LOG_DEBUG);
- $resql = $db->query($sql);
- if ($resql) {
- $num = $db->num_rows($resql);
+ $resql_models = $db->query($sql);
+ if ($resql_models) {
+ $num = $db->num_rows($resql_models);
$i = 0;
while ($i < $num) {
$found = 1;
- $obj = $db->fetch_object($resql);
+ $obj = $db->fetch_object($resql_models);
// If this generation module needs to scan a directory, then description field is filled
// with the constant that contains list of directories to scan (COMPANY_ADDON_PDF_ODT_PATH, ...).
diff --git a/htdocs/core/lib/modulebuilder.lib.php b/htdocs/core/lib/modulebuilder.lib.php
index 9d937d67627..2eb0aed6c07 100644
--- a/htdocs/core/lib/modulebuilder.lib.php
+++ b/htdocs/core/lib/modulebuilder.lib.php
@@ -638,3 +638,125 @@ function deletePropsFromDoc($file, $objectname)
$new_contents = preg_replace($search, '', $str);
file_put_contents($file, $new_contents);
}
+
+/**
+ * Search a string and return all lines needed from file
+ * @param string $file file for searching
+ * @param string $start start line if exist
+ * @param string $end end line if exist
+ * @return string return the content needed
+ */
+function getFromFile($file, $start, $end)
+{
+ $i = 1;
+ $keys = array();
+ $lines = file($file);
+ // Search for start and end lines
+ foreach ($lines as $i => $line) {
+ if (strpos($line, $start) !== false) {
+ // Copy lines until the end on array
+ while (($line = $lines[++$i]) !== false) {
+ if (strpos($line, $end) !== false) {
+ break;
+ }
+ $keys[] = $line;
+ }
+ break;
+ }
+ }
+ $content = implode("", $keys);
+ return $content;
+}
+
+/**
+ * Write all permissions of each object in AsciiDoc format
+ * @param string $file path of the class
+ * @param string $destfile file where write table of permissions
+ * @return int 1 if OK, -1 if KO
+ */
+function writePermsInAsciiDoc($file, $destfile)
+{
+ global $langs;
+ //search and get all permssion in stirng
+ $start = '/* BEGIN MODULEBUILDER PERMISSIONS */';
+ $end = '/* END MODULEBUILDER PERMISSIONS */';
+ $content = getFromFile($file, $start, $end);
+ if (empty($content)) {
+ return -1;
+ }
+ //prepare table
+ $string = "[options='header',grid=rows,width=60%,caption=Organisation]\n";
+ $string .= "|===\n";
+ // header for table
+ $header = array($langs->trans('Objects'),$langs->trans('Permission'));
+ foreach ($header as $h) {
+ $string .= "|".$h;
+ }
+ $string .= "\n";
+ //content table
+ $array = explode(";", $content);
+ $indexIgnored = 15;
+ $permissions = array_slice($array, $indexIgnored, null, true);
+ // delete occurrences "$r++" and ID
+ $permissions = str_replace('$r++', 1, $permissions);
+
+ $permsN = array();
+ foreach ($permissions as $i => $element) {
+ if ($element == 1) {
+ unset($permissions[$i]);
+ }
+ if (str_contains($element, '$this->numero')) {
+ unset($permissions[$i]);
+ }
+ if (str_contains($element, '$this->rights[$r][5]')) {
+ unset($permissions[$i]);
+ }
+ }
+ // cleaning the string on each element
+ foreach ($permissions as $key => $element) {
+ $element = str_replace(" '", '', $element);
+ $element = trim($element, "'");
+ $permsN[] = substr($element, strpos($element, "=")+1);
+ }
+ array_pop($permsN);
+
+ // Group permissions by Object and add it to string
+ $temp_array = [];
+ $final_array = [];
+ $countRights = count($permsN);
+ for ($i = 0; $i < $countRights ; $i++) {
+ // Add current element to temporary array
+ $temp_array[] = $permsN[$i];
+ // add them to the final array and empty the temporary array
+ if (count($temp_array) == 2) {
+ $final_array[] = $temp_array;
+ $temp_array = [];
+ }
+ }
+ // add it to the final array
+ if (count($temp_array) > 0) {
+ $final_array[] = $temp_array;
+ }
+
+ $result = array();
+ foreach ($final_array as $subarray) {
+ // found object
+ $key = $subarray[1];
+ // add sub array to object
+ $result[$key][] = $subarray;
+ }
+ foreach ($result as $i => $pems) {
+ $string .= "|*".$i."*|";
+ foreach ($pems as $tab) {
+ $string .= $tab[0]." , ";
+ }
+ $string .= "\n";
+ }
+ // end table
+ $string .= "\n|===\n";
+ $write = dolReplaceInFile($destfile, array('__DATA_PERMISSIONS__'=> $string));
+ if ($write<0) {
+ return -1;
+ }
+ return 1;
+}
diff --git a/htdocs/core/lib/payments.lib.php b/htdocs/core/lib/payments.lib.php
index 93c7a17de2f..ef89c65bdf1 100644
--- a/htdocs/core/lib/payments.lib.php
+++ b/htdocs/core/lib/payments.lib.php
@@ -417,109 +417,3 @@ function getOnlinePaymentUrl($mode, $type, $ref = '', $amount = '9.99', $freetag
return $out;
}
-
-
-
-/**
- * Show footer of company in HTML pages
- *
- * @param Societe $fromcompany Third party
- * @param Translate $langs Output language
- * @param int $addformmessage Add the payment form message
- * @param string $suffix Suffix to use on constants
- * @param Object $object Object related to payment
- * @return void
- */
-function htmlPrintOnlinePaymentFooter($fromcompany, $langs, $addformmessage = 0, $suffix = '', $object = null)
-{
- global $conf;
-
- $reg = array();
-
- // Juridical status
- $line1 = "";
- if ($fromcompany->forme_juridique_code) {
- $line1 .= ($line1 ? " - " : "").getFormeJuridiqueLabel($fromcompany->forme_juridique_code);
- }
- // Capital
- if ($fromcompany->capital) {
- $line1 .= ($line1 ? " - " : "").$langs->transnoentities("CapitalOf", $fromcompany->capital)." ".$langs->transnoentities("Currency".$conf->currency);
- }
- // Prof Id 1
- if ($fromcompany->idprof1 && ($fromcompany->country_code != 'FR' || !$fromcompany->idprof2)) {
- $field = $langs->transcountrynoentities("ProfId1", $fromcompany->country_code);
- if (preg_match('/\((.*)\)/i', $field, $reg)) {
- $field = $reg[1];
- }
- $line1 .= ($line1 ? " - " : "").$field.": ".$fromcompany->idprof1;
- }
- // Prof Id 2
- if ($fromcompany->idprof2) {
- $field = $langs->transcountrynoentities("ProfId2", $fromcompany->country_code);
- if (preg_match('/\((.*)\)/i', $field, $reg)) {
- $field = $reg[1];
- }
- $line1 .= ($line1 ? " - " : "").$field.": ".$fromcompany->idprof2;
- }
-
- // Second line of company infos
- $line2 = "";
- // Prof Id 3
- if ($fromcompany->idprof3) {
- $field = $langs->transcountrynoentities("ProfId3", $fromcompany->country_code);
- if (preg_match('/\((.*)\)/i', $field, $reg)) {
- $field = $reg[1];
- }
- $line2 .= ($line2 ? " - " : "").$field.": ".$fromcompany->idprof3;
- }
- // Prof Id 4
- if ($fromcompany->idprof4) {
- $field = $langs->transcountrynoentities("ProfId4", $fromcompany->country_code);
- if (preg_match('/\((.*)\)/i', $field, $reg)) {
- $field = $reg[1];
- }
- $line2 .= ($line2 ? " - " : "").$field.": ".$fromcompany->idprof4;
- }
- // IntraCommunautary VAT
- if ($fromcompany->tva_intra != '') {
- $line2 .= ($line2 ? " - " : "").$langs->transnoentities("VATIntraShort").": ".$fromcompany->tva_intra;
- }
-
- print ''."\n";
-
- print ''."\n";
- print ' ';
- if ($addformmessage) {
- print '';
- print ' ';
-
- $parammessageform = 'ONLINE_PAYMENT_MESSAGE_FORM_'.$suffix;
- if (!empty($conf->global->$parammessageform)) {
- print $langs->transnoentities($conf->global->$parammessageform);
- } elseif (!empty($conf->global->ONLINE_PAYMENT_MESSAGE_FORM)) {
- print $langs->transnoentities($conf->global->ONLINE_PAYMENT_MESSAGE_FORM);
- }
-
- // Add other message if VAT exists
- if (!empty($object->total_vat) || !empty($object->total_tva)) {
- $parammessageform = 'ONLINE_PAYMENT_MESSAGE_FORMIFVAT_'.$suffix;
- if (!empty($conf->global->$parammessageform)) {
- print $langs->transnoentities($conf->global->$parammessageform);
- } elseif (!empty($conf->global->ONLINE_PAYMENT_MESSAGE_FORMIFVAT)) {
- print $langs->transnoentities($conf->global->ONLINE_PAYMENT_MESSAGE_FORMIFVAT);
- }
- }
- }
-
- print ' '."\n";
- print $fromcompany->name.' ';
- print $line1;
- if (strlen($line1.$line2) > 50) {
- print ' ';
- } else {
- print ' - ';
- }
- print $line2;
- print ' ';
- print ' '."\n";
-}
diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php
index 468c4d82a9b..03e816a63f9 100644
--- a/htdocs/core/lib/pdf.lib.php
+++ b/htdocs/core/lib/pdf.lib.php
@@ -744,12 +744,13 @@ function pdf_pagehead(&$pdf, $outputlangs, $page_height)
* @param Translate $outputlangs Output language
* @param array $exclude Array of family keys we want to exclude. For example array('mycompany', 'object', 'date', 'user', ...)
* @param Object $object Object
- * @param int $onlykey 1=Do not calculate some heavy values of keys (performance enhancement when we need only the keys), 2=Values are truncated and html sanitized (to use for help tooltip)
+ * @param int $onlykey 1=Do not calculate some heavy values of keys (performance enhancement when we need only the keys), 2=Values are truncated and html sanitized (to use for help tooltip)
+ * @param array $include Array of family keys we want to include. For example array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...)
* @return array Array of substitutions
*/
-function pdf_getSubstitutionArray($outputlangs, $exclude = null, $object = null, $onlykey = 0)
+function pdf_getSubstitutionArray($outputlangs, $exclude = null, $object = null, $onlykey = 0, $include = null)
{
- $substitutionarray = getCommonSubstitutionArray($outputlangs, $onlykey, $exclude, $object);
+ $substitutionarray = getCommonSubstitutionArray($outputlangs, $onlykey, $exclude, $object, $include);
$substitutionarray['__FROM_NAME__'] = '__FROM_NAME__';
$substitutionarray['__FROM_EMAIL__'] = '__FROM_EMAIL__';
return $substitutionarray;
@@ -1666,7 +1667,7 @@ function pdf_getlinedesc($object, $i, $outputlangs, $hideref = 0, $hidedesc = 0,
// Show information for lot
if (!empty($dbatch)) {
// $object is a shipment.
- //var_dump($object->lines[$i]->details_entrepot); // array from llx_expeditiondet (we can have seral lines for one fk_origin_line)
+ //var_dump($object->lines[$i]->details_entrepot); // array from llx_expeditiondet (we can have several lines for one fk_origin_line)
//var_dump($object->lines[$i]->detail_batch); // array from llx_expeditiondet_batch (each line with a lot is linked to llx_expeditiondet)
include_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
diff --git a/htdocs/core/lib/project.lib.php b/htdocs/core/lib/project.lib.php
index 2a38906e4de..9baccc8e9fc 100644
--- a/htdocs/core/lib/project.lib.php
+++ b/htdocs/core/lib/project.lib.php
@@ -290,7 +290,7 @@ function project_prepare_head(Project $project, $moreparam = '')
} else {
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php';
- $upload_dir = $conf->project->dir_output."/".dol_sanitizeFileName($project->ref);
+ $upload_dir = $conf->project->multidir_output[$project->entity]."/".dol_sanitizeFileName($project->ref);
$nbFiles = count(dol_dir_list($upload_dir, 'files', 0, '', '(\.meta|_preview.*\.png)$'));
$nbLinks = Link::count($db, $project->element, $project->id);
$totalAttached = $nbFiles + $nbLinks;
@@ -418,7 +418,7 @@ function task_prepare_head($object)
}
$head[$h][0] = DOL_URL_ROOT.'/projet/tasks/document.php?id='.$object->id.(GETPOST('withproject') ? '&withproject=1' : '');
- $filesdir = $conf->project->dir_output."/".dol_sanitizeFileName($object->project->ref).'/'.dol_sanitizeFileName($object->ref);
+ $filesdir = $conf->project->multidir_output[$object->entity]."/".dol_sanitizeFileName($object->project->ref).'/'.dol_sanitizeFileName($object->ref);
include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
include_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php';
$nbFiles = count(dol_dir_list($filesdir, 'files', 0, '', '(\.meta|_preview.*\.png)$'));
diff --git a/htdocs/core/lib/security.lib.php b/htdocs/core/lib/security.lib.php
index 1f7b9404a7c..3c9ca5e34d9 100644
--- a/htdocs/core/lib/security.lib.php
+++ b/htdocs/core/lib/security.lib.php
@@ -270,9 +270,9 @@ function dol_verifyHash($chain, $hash, $type = '0')
if ($type == '0' && !empty($conf->global->MAIN_SECURITY_HASH_ALGO) && $conf->global->MAIN_SECURITY_HASH_ALGO == 'password_hash' && function_exists('password_verify')) {
if (! empty($hash[0]) && $hash[0] == '$') {
return password_verify($chain, $hash);
- } elseif (strlen($hash) == 32) {
+ } elseif (dol_strlen($hash) == 32) {
return dol_verifyHash($chain, $hash, '3'); // md5
- } elseif (strlen($hash) == 40) {
+ } elseif (dol_strlen($hash) == 40) {
return dol_verifyHash($chain, $hash, '2'); // sha1md5
}
@@ -332,7 +332,7 @@ function dolGetLdapPasswordHash($password, $type = 'md5')
* This method check permission on module then call checkUserAccessToObject() for permission on object (according to entity and socid of user).
*
* @param User $user User to check
- * @param string $features Features to check (it must be module $object->element. Can be a 'or' check with 'levela|levelb'.
+ * @param string $features Features to check (it must be module name or $object->element. Can be a 'or' check with 'levela|levelb'.
* Examples: 'societe', 'contact', 'produit&service', 'produit|service', ...)
* This is used to check permission $user->rights->features->...
* @param int|string|object $object Object or Object ID or list of Object ID if we want to check a particular record (optional) is linked to a owned thirdparty (optional).
@@ -340,7 +340,7 @@ function dolGetLdapPasswordHash($password, $type = 'md5')
* @param string $feature2 Feature to check, second level of permission (optional). Can be a 'or' check with 'sublevela|sublevelb'.
* This is used to check permission $user->rights->features->feature2...
* @param string $dbt_keyfield Field name for socid foreign key if not fk_soc. Not used if objectid is null (optional). Can use '' if NA.
- * @param string $dbt_select Field name for select if not "rowid". Not used if objectid is null (optional)
+ * @param string $dbt_select Field rowid name, for select into tableandshare if not "rowid". Not used if objectid is null (optional)
* @param int $isdraft 1=The object with id=$objectid is a draft
* @param int $mode Mode (0=default, 1=return without dieing)
* @return int If mode = 0 (default): Always 1, die process if not allowed. If mode = 1: Return 0 if access not allowed.
@@ -351,6 +351,7 @@ function restrictedArea(User $user, $features, $object = 0, $tableandshare = '',
global $db, $conf;
global $hookmanager;
+ // Define $objectid
if (is_object($object)) {
$objectid = $object->id;
} else {
@@ -363,12 +364,17 @@ function restrictedArea(User $user, $features, $object = 0, $tableandshare = '',
//dol_syslog("functions.lib:restrictedArea $feature, $objectid, $dbtablename, $feature2, $dbt_socfield, $dbt_select, $isdraft");
//print "user_id=".$user->id.", features=".$features.", feature2=".$feature2.", objectid=".$objectid;
//print ", dbtablename=".$tableandshare.", dbt_socfield=".$dbt_keyfield.", dbt_select=".$dbt_select;
- //print ", perm: ".$features."->".$feature2."=".($user->rights->$features->$feature2->lire)." ";
+ //print ", perm: ".$features."->".$feature2."=".($user->hasRight($features, $feature2, 'lire'))." ";
$parentfortableentity = '';
// Fix syntax of $features param
$originalfeatures = $features;
+ if ($features == 'agenda') {
+ $tableandshare = 'actioncomm&societe';
+ $feature2 = 'myactions|allactions';
+ $dbt_select = 'id';
+ }
if ($features == 'facturerec') {
$features = 'facture';
}
@@ -382,9 +388,7 @@ function restrictedArea(User $user, $features, $object = 0, $tableandshare = '',
$features = 'adherent';
$feature2 = 'cotisation';
}
- if ($features == 'websitepage') {
- $features = 'website';
- $tableandshare = 'website_page';
+ if ($features == 'website' && is_object($object) && $object->element == 'websitepage') {
$parentfortableentity = 'fk_website@website';
}
if ($features == 'project') {
@@ -393,6 +397,19 @@ function restrictedArea(User $user, $features, $object = 0, $tableandshare = '',
if ($features == 'product') {
$features = 'produit';
}
+ if ($features == 'productbatch') {
+ $features = 'produit';
+ }
+ if ($features == 'fournisseur') { // When vendor invoice and pruchase order are into module 'fournisseur'
+ $features = 'fournisseur';
+ if ($object->element == 'invoice_supplier') {
+ $feature2 = 'facture';
+ } elseif ($object->element == 'order_supplier') {
+ $feature2 = 'commande';
+ }
+ }
+
+ //print $features.' - '.$tableandshare.' - '.$feature2.' - '.$dbt_select."\n";
// Get more permissions checks from hooks
$parameters = array('features'=>$features, 'originalfeatures'=>$originalfeatures, 'objectid'=>$objectid, 'dbt_select'=>$dbt_select, 'idtype'=>$dbt_select, 'isdraft'=>$isdraft);
@@ -485,7 +502,7 @@ function restrictedArea(User $user, $features, $object = 0, $tableandshare = '',
$readok = 0;
$nbko++;
}
- } elseif (!empty($feature2)) { // This is for permissions on 2 levels
+ } elseif (!empty($feature2)) { // This is for permissions on 2 levels (module->object->read)
$tmpreadok = 1;
foreach ($feature2 as $subfeature) {
if ($subfeature == 'user' && $user->id == $objectid) {
@@ -504,7 +521,7 @@ function restrictedArea(User $user, $features, $object = 0, $tableandshare = '',
$readok = 0; // All tests are ko (we manage here the and, the or will be managed later using $nbko).
$nbko++;
}
- } elseif (!empty($feature) && ($feature != 'user' && $feature != 'usergroup')) { // This is permissions on 1 level
+ } elseif (!empty($feature) && ($feature != 'user' && $feature != 'usergroup')) { // This is permissions on 1 level (module->read)
if (empty($user->rights->$feature->lire)
&& empty($user->rights->$feature->read)
&& empty($user->rights->$feature->run)) {
@@ -531,7 +548,7 @@ function restrictedArea(User $user, $features, $object = 0, $tableandshare = '',
// Check write permission from module (we need to know write permission to create but also to delete drafts record or to upload files)
$createok = 1;
$nbko = 0;
- $wemustcheckpermissionforcreate = (GETPOST('sendit', 'alpha') || GETPOST('linkit', 'alpha') || in_array(GETPOST('action', 'aZ09'), array('create', 'update', 'add_element_resource', 'confirm_delete_linked_resource')) || GETPOST('roworder', 'alpha', 2));
+ $wemustcheckpermissionforcreate = (GETPOST('sendit', 'alpha') || GETPOST('linkit', 'alpha') || in_array(GETPOST('action', 'aZ09'), array('create', 'update', 'set', 'upload', 'add_element_resource', 'confirm_delete_linked_resource')) || GETPOST('roworder', 'alpha', 2));
$wemustcheckpermissionfordeletedraft = ((GETPOST("action", "aZ09") == 'confirm_delete' && GETPOST("confirm", "aZ09") == 'yes') || GETPOST("action", "aZ09") == 'delete');
if ($wemustcheckpermissionforcreate || $wemustcheckpermissionfordeletedraft) {
@@ -576,7 +593,7 @@ function restrictedArea(User $user, $features, $object = 0, $tableandshare = '',
$createok = 0;
$nbko++;
}
- } elseif (!empty($feature2)) { // This is for permissions on one level
+ } elseif (!empty($feature2)) { // This is for permissions on 2 levels (module->object->write)
foreach ($feature2 as $subfeature) {
if ($subfeature == 'user' && $user->id == $objectid && $user->rights->user->self->creer) {
continue; // User can edit its own card
@@ -599,7 +616,7 @@ function restrictedArea(User $user, $features, $object = 0, $tableandshare = '',
break;
}
}
- } elseif (!empty($feature)) { // This is for permissions on 2 levels ('creer' or 'write')
+ } elseif (!empty($feature)) { // This is for permissions on 1 levels (module->write)
//print ' feature='.$feature.' creer='.$user->rights->$feature->creer.' write='.$user->rights->$feature->write; exit;
if (empty($user->rights->$feature->creer)
&& empty($user->rights->$feature->write)
@@ -1114,6 +1131,7 @@ function httponly_accessforbidden($message = 1, $http_response_code = 403, $stri
function accessforbidden($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
{
global $conf, $db, $user, $langs, $hookmanager;
+ global $action, $object;
if (!is_object($langs)) {
include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
@@ -1139,12 +1157,13 @@ function accessforbidden($message = '', $printheader = 1, $printfooter = 1, $sho
print '
';
print '
';
if (empty($showonlymessage)) {
- global $action, $object;
if (empty($hookmanager)) {
+ include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
$hookmanager = new HookManager($db);
// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
$hookmanager->initHooks(array('main'));
}
+
$parameters = array('message'=>$message, 'params'=>$params);
$reshook = $hookmanager->executeHooks('getAccessForbiddenMessage', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
print $hookmanager->resPrint;
diff --git a/htdocs/core/lib/website.lib.php b/htdocs/core/lib/website.lib.php
index 6f3ac2cdf0f..52b9afab8a7 100644
--- a/htdocs/core/lib/website.lib.php
+++ b/htdocs/core/lib/website.lib.php
@@ -35,8 +35,10 @@ function dolStripPhpCode($str, $replacewith = '')
$newstr = '';
- //split on each opening tag
- $parts = explode('holiday->enabled', __HANDLER__, 'left', 5000__+MAX_llx_menu__, 'hrm', 'hrm', 15__+MAX_llx_menu__, '/holiday/list.php?mainmenu=hrm&leftmenu=hrm', 'CPTitreMenu', 0, 'holiday', '$user->rights->holiday->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->holiday->enabled', __HANDLER__, 'left', 5001__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/card.php?mainmenu=hrm&action=create', 'New', 1, 'holiday', '$user->rights->holiday->write', '', 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->holiday->enabled', __HANDLER__, 'left', 5001__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/card_group.php?mainmenu=hrm&action=create', 'NewHolidayForGroup', 1, 'holiday', '$user->rights->holiday->write', '', 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->holiday->enabled', __HANDLER__, 'left', 5002__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/list.php?mainmenu=hrm&leftmenu=hrm', 'List', 1, 'holiday', '$user->rights->holiday->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->holiday->enabled', __HANDLER__, 'left', 5003__+MAX_llx_menu__, 'hrm', '', 5002__+MAX_llx_menu__, '/holiday/list.php?mainmenu=hrm&search_statut=2&leftmenu=hrm', 'ListToApprove', 2, 'trips', '$user->rights->holiday->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->holiday->enabled', __HANDLER__, 'left', 5004__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/define_holiday.php?mainmenu=hrm', 'MenuConfCP', 1, 'holiday', '$user->rights->holiday->define_holiday', '', 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->holiday->enabled', __HANDLER__, 'left', 5005__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/view_log.php?mainmenu=hrm', 'MenuLogCP', 1, 'holiday', '$user->rights->holiday->define_holiday', '', 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->holiday->enabled', __HANDLER__, 'left', 5002__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/card_group.php?mainmenu=hrm&action=create', 'NewHolidayForGroup', 1, 'holiday', '$user->rights->holiday->write', '', 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->holiday->enabled', __HANDLER__, 'left', 5003__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/list.php?mainmenu=hrm&leftmenu=hrm', 'List', 1, 'holiday', '$user->rights->holiday->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->holiday->enabled', __HANDLER__, 'left', 5004__+MAX_llx_menu__, 'hrm', '', 5002__+MAX_llx_menu__, '/holiday/list.php?mainmenu=hrm&search_statut=2&leftmenu=hrm', 'ListToApprove', 2, 'trips', '$user->rights->holiday->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->holiday->enabled', __HANDLER__, 'left', 5005__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/define_holiday.php?mainmenu=hrm', 'MenuConfCP', 1, 'holiday', '$user->rights->holiday->define_holiday', '', 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->holiday->enabled', __HANDLER__, 'left', 5006__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/view_log.php?mainmenu=hrm', 'MenuLogCP', 1, 'holiday', '$user->rights->holiday->define_holiday', '', 0, 3, __ENTITY__);
-- HRM - Trips and expenses (old module)
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->deplacement->enabled', __HANDLER__, 'left', 2100__+MAX_llx_menu__, 'accountancy', 'tripsandexpenses', 15__+MAX_llx_menu__, '/compta/deplacement/index.php?mainmenu=accountancy&leftmenu=tripsandexpenses', 'TripsAndExpenses', 0, 'trips', '$user->rights->deplacement->lire', '', 0, 5, __ENTITY__);
diff --git a/htdocs/core/modules/DolibarrModules.class.php b/htdocs/core/modules/DolibarrModules.class.php
index 6c43c945b65..62dbd494082 100644
--- a/htdocs/core/modules/DolibarrModules.class.php
+++ b/htdocs/core/modules/DolibarrModules.class.php
@@ -1986,7 +1986,7 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it
public function insert_menus()
{
// phpcs:enable
- global $user;
+ global $conf, $user;
if (!is_array($this->menu) || empty($this->menu)) {
return 0;
@@ -1998,6 +1998,9 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it
$err = 0;
+ // Common module
+ $entity = ((!empty($this->always_enabled) || !empty($this->core_enabled)) ? 0 : $conf->entity);
+
$this->db->begin();
foreach ($this->menu as $key => $value) {
@@ -2049,6 +2052,7 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it
$menu->user = $this->menu[$key]['user'];
$menu->enabled = isset($this->menu[$key]['enabled']) ? $this->menu[$key]['enabled'] : 0;
$menu->position = $this->menu[$key]['position'];
+ $menu->entity = $entity;
if (!$err) {
$result = $menu->create($user); // Save menu entry into table llx_menu
@@ -2092,7 +2096,7 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it
$sql = "DELETE FROM ".MAIN_DB_PREFIX."menu";
$sql .= " WHERE module = '".$this->db->escape($module)."'";
- $sql .= " AND entity = ".$conf->entity;
+ $sql .= " AND entity IN (0, ".$conf->entity.")";
dol_syslog(get_class($this)."::delete_menus", LOG_DEBUG);
$resql = $this->db->query($sql);
diff --git a/htdocs/core/modules/expedition/doc/pdf_espadon.modules.php b/htdocs/core/modules/expedition/doc/pdf_espadon.modules.php
index 34699a6e608..f6c648f2ff0 100644
--- a/htdocs/core/modules/expedition/doc/pdf_espadon.modules.php
+++ b/htdocs/core/modules/expedition/doc/pdf_espadon.modules.php
@@ -368,11 +368,14 @@ class pdf_espadon extends ModelePdfExpedition
if (!empty($notetoshow) || !empty($object->tracking_number)) {
$tab_top -= 2;
+ $tab_topbeforetrackingnumber = $tab_top;
// Tracking number
if (!empty($object->tracking_number)) {
+ $height_trackingnumber = 4;
+
$pdf->SetFont('', 'B', $default_font_size - 2);
- $pdf->writeHTMLCell(60, 4, $this->posxdesc - 1, $tab_top - 1, $outputlangs->transnoentities("TrackingNumber") . " : " . $object->tracking_number, 0, 1, false, true, 'L');
+ $pdf->writeHTMLCell(60, $height_trackingnumber, $this->posxdesc - 1, $tab_top - 1, $outputlangs->transnoentities("TrackingNumber") . " : " . $object->tracking_number, 0, 1, false, true, 'L');
$tab_top_alt = $pdf->GetY();
$object->getUrlTrackingStatus($object->tracking_number);
@@ -390,19 +393,20 @@ class pdf_espadon extends ModelePdfExpedition
$label .= " : ";
$label .= $object->tracking_url;
}
- $pdf->SetFont('', 'B', $default_font_size - 2);
- $pdf->writeHTMLCell(60, 4, $this->posxdesc - 1, $tab_top_alt, $label, 0, 1, false, true, 'L');
- $tab_top = $pdf->GetY();
+ $height_trackingnumber += 4;
+ $pdf->SetFont('', 'B', $default_font_size - 2);
+ $pdf->writeHTMLCell(60, $height_trackingnumber, $this->posxdesc - 1, $tab_top_alt, $label, 0, 1, false, true, 'L');
}
}
+ $tab_top = $pdf->GetY();
}
// Notes
$pagenb = $pdf->getPage();
- if (!empty($notetoshow)) {
- $tab_top -= 2;
+ if (!empty($notetoshow) || !empty($object->tracking_number)) {
+ $tab_top -= 1;
$tab_width = $this->page_largeur - $this->marge_gauche - $this->marge_droite;
$pageposbeforenote = $pagenb;
@@ -465,11 +469,21 @@ class pdf_espadon extends ModelePdfExpedition
$pdf->SetDrawColor(128, 128, 128);
// Draw note frame
if ($i > $pageposbeforenote) {
- $height_note = $this->page_hauteur - ($tab_top_newpage + $heightforfooter);
- $pdf->Rect($this->marge_gauche, $tab_top_newpage - 1, $tab_width, $height_note + 1);
+ if (empty($height_trackingnumber)) {
+ $height_note = $this->page_hauteur - ($tab_top_newpage + $heightforfooter);
+ } else {
+ $height_note = $this->page_hauteur - ($tab_top_newpage + $heightforfooter) + $height_trackingnumber + 1;
+ $tab_top_newpage = $tab_topbeforetrackingnumber;
+ }
+ $pdf->Rect($this->marge_gauche, $tab_top_newpage - 1, $tab_width, $height_note + 2);
} else {
- $height_note = $this->page_hauteur - ($tab_top + $heightforfooter);
- $pdf->Rect($this->marge_gauche, $tab_top - 1, $tab_width, $height_note + 1);
+ if (empty($height_trackingnumber)) {
+ $height_note = $this->page_hauteur - ($tab_top + $heightforfooter);
+ } else {
+ $height_note = $this->page_hauteur - ($tab_top + $heightforfooter)+ $height_trackingnumber + 1;
+ $tab_top = $tab_topbeforetrackingnumber;
+ }
+ $pdf->Rect($this->marge_gauche, $tab_top - 1, $tab_width, $height_note + 2);
}
// Add footer
@@ -489,8 +503,13 @@ class pdf_espadon extends ModelePdfExpedition
{
$pdf->commitTransaction();
$posyafter = $pdf->GetY();
- $height_note = $posyafter - $tab_top;
- $pdf->Rect($this->marge_gauche, $tab_top - 1, $tab_width, $height_note + 1);
+ if (empty($height_trackingnumber)) {
+ $height_note = $posyafter - $tab_top + 1;
+ } else {
+ $height_note = $posyafter - $tab_top + $height_trackingnumber + 1;
+ $tab_top = $tab_topbeforetrackingnumber;
+ }
+ $pdf->Rect($this->marge_gauche, $tab_top - 1, $tab_width, $height_note + 2);
if ($posyafter > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + 20))) {
diff --git a/htdocs/core/modules/export/export_excel2007.modules.php b/htdocs/core/modules/export/export_excel2007.modules.php
index fba0dfa1c4a..ea7480cf15c 100644
--- a/htdocs/core/modules/export/export_excel2007.modules.php
+++ b/htdocs/core/modules/export/export_excel2007.modules.php
@@ -92,7 +92,7 @@ class ExportExcel2007 extends ModeleExports
if (empty($this->disabled)) {
require_once PHPEXCELNEW_PATH.'Spreadsheet.php';
$this->label_lib = 'PhpSpreadSheet';
- $this->version_lib = '1.6.0'; // No way to get info from library
+ $this->version_lib = '1.12.0'; // No way to get info from library
}
$this->row = 0;
@@ -464,6 +464,7 @@ class ExportExcel2007 extends ModeleExports
return '';
}
+ $letter = '';
while ($c != 0) {
$p = ($c - 1) % 26;
$c = intval(($c - $p) / 26);
diff --git a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php
index 174648dfa8c..50b22f1b439 100644
--- a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php
+++ b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php
@@ -1876,7 +1876,8 @@ class pdf_crabe extends ModelePDFFactures
$title = $outputlangs->transnoentities("InvoiceProForma");
}
if ($this->situationinvoice) {
- $title = $outputlangs->transnoentities("PDFInvoiceSituation");
+ $langs->loadLangs(array("other"));
+ $title = $outputlangs->transnoentities("PDFInvoiceSituation") . " " . $outputlangs->transnoentities("NumberingShort") . $object->situation_counter . " -";
}
if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && is_object($outputlangsbis)) {
$title .= ' - ';
@@ -1919,11 +1920,11 @@ class pdf_crabe extends ModelePDFFactures
$posy += 3;
$pdf->SetFont('', '', $default_font_size - 2);
- if ($object->ref_client) {
+ if ($object->ref_customer) {
$posy += 4;
$pdf->SetXY($posx, $posy);
$pdf->SetTextColor(0, 0, 60);
- $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefCustomer")." : ".$outputlangs->convToOutputCharset($object->ref_client), '', 'R');
+ $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefCustomer")." : ".$outputlangs->convToOutputCharset($object->ref_customer), '', 'R');
}
if (!empty($conf->global->PDF_SHOW_PROJECT_TITLE)) {
diff --git a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php
index 312115bb0c1..cbdb1608beb 100644
--- a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php
+++ b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php
@@ -2126,7 +2126,8 @@ class pdf_sponge extends ModelePDFFactures
$title = $outputlangs->transnoentities("InvoiceProForma");
}
if ($this->situationinvoice) {
- $title = $outputlangs->transnoentities("PDFInvoiceSituation");
+ $langs->loadLangs(array("other"));
+ $title = $outputlangs->transnoentities("PDFInvoiceSituation") . " " . $outputlangs->transnoentities("NumberingShort") . $object->situation_counter . " -";
}
if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && is_object($outputlangsbis)) {
$title .= ' - ';
@@ -2169,11 +2170,11 @@ class pdf_sponge extends ModelePDFFactures
$posy += 3;
$pdf->SetFont('', '', $default_font_size - 2);
- if ($object->ref_client) {
+ if ($object->ref_customer) {
$posy += 4;
$pdf->SetXY($posx, $posy);
$pdf->SetTextColor(0, 0, 60);
- $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefCustomer")." : ".dol_trunc($outputlangs->convToOutputCharset($object->ref_client), 65), '', 'R');
+ $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefCustomer")." : ".dol_trunc($outputlangs->convToOutputCharset($object->ref_customer), 65), '', 'R');
}
if (!empty($conf->global->PDF_SHOW_PROJECT_TITLE)) {
diff --git a/htdocs/core/modules/import/import_csv.modules.php b/htdocs/core/modules/import/import_csv.modules.php
index 1213cc4cf16..8ad5ef94b2f 100644
--- a/htdocs/core/modules/import/import_csv.modules.php
+++ b/htdocs/core/modules/import/import_csv.modules.php
@@ -96,6 +96,8 @@ class ImportCsv extends ModeleImports
public function __construct($db, $datatoimport)
{
global $conf, $langs;
+
+ parent::__construct();
$this->db = $db;
$this->separator = (GETPOST('separator') ?GETPOST('separator') : (empty($conf->global->IMPORT_CSV_SEPARATOR_TO_USE) ? ',' : $conf->global->IMPORT_CSV_SEPARATOR_TO_USE));
@@ -649,7 +651,7 @@ class ImportCsv extends ModeleImports
break;
}
$classinstance = new $class($this->db);
- $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $listfields, ($key - 1)));
+ $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $arrayfield, ($key - 1)));
$newval = $res; // We get new value computed.
} elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'numeric') {
$newval = price2num($newval);
@@ -809,7 +811,7 @@ class ImportCsv extends ModeleImports
break;
}
$classinstance = new $class($this->db);
- $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $listfields, ($key - 1)));
+ $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $arrayfield, ($key - 1)));
$fieldArr = explode('.', $fieldname);
if (count($fieldArr) > 0) {
$fieldname = $fieldArr[1];
@@ -870,6 +872,10 @@ class ImportCsv extends ModeleImports
$filters[] = $col.' = '.$data[$key];
}
}
+ if (!empty($tablewithentity_cache[$tablename])) {
+ $where[] = "entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
+ $filters[] = "entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
+ }
$sqlSelect .= " WHERE ".implode(' AND ', $where);
$resql = $this->db->query($sqlSelect);
@@ -906,6 +912,10 @@ class ImportCsv extends ModeleImports
}
$sqlSelect .= " WHERE ".$keyfield." = ".((int) $lastinsertid);
+ if (!empty($tablewithentity_cache[$tablename])) {
+ $sqlSelect .= " AND entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
+ }
+
$resql = $this->db->query($sqlSelect);
if ($resql) {
$res = $this->db->fetch_object($resql);
@@ -951,6 +961,10 @@ class ImportCsv extends ModeleImports
$sqlend = " WHERE " . implode(' AND ', $where);
}
+ if (!empty($tablewithentity_cache[$tablename])) {
+ $sqlend .= " AND entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
+ }
+
$sql = $sqlstart.$sqlend;
// Run update request
diff --git a/htdocs/core/modules/import/import_xlsx.modules.php b/htdocs/core/modules/import/import_xlsx.modules.php
index c7f6df31d03..4ced5a7dff4 100644
--- a/htdocs/core/modules/import/import_xlsx.modules.php
+++ b/htdocs/core/modules/import/import_xlsx.modules.php
@@ -106,6 +106,8 @@ class ImportXlsx extends ModeleImports
public function __construct($db, $datatoimport)
{
global $conf, $langs;
+
+ parent::__construct();
$this->db = $db;
// this is used as an extension from the example file code, so we have to put xlsx here !!!
@@ -694,7 +696,7 @@ class ImportXlsx extends ModeleImports
break;
}
$classinstance = new $class($this->db);
- $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $listfields, $key));
+ $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $arrayfield, $key));
$newval = $res; // We get new value computed.
} elseif ($objimport->array_import_convertvalue[0][$val]['rule'] == 'numeric') {
$newval = price2num($newval);
@@ -853,7 +855,7 @@ class ImportXlsx extends ModeleImports
break;
}
$classinstance = new $class($this->db);
- $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $listfields, $key));
+ $res = call_user_func_array(array($classinstance, $method), array(&$arrayrecord, $arrayfield, $key));
$fieldArr = explode('.', $fieldname);
if (count($fieldArr) > 0) {
$fieldname = $fieldArr[1];
@@ -916,6 +918,10 @@ class ImportXlsx extends ModeleImports
$filters[] = $col.' = '.$data[$key];
}
}
+ if (!empty($tablewithentity_cache[$tablename])) {
+ $where[] = "entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
+ $filters[] = "entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
+ }
$sqlSelect .= " WHERE " . implode(' AND ', $where);
$resql = $this->db->query($sqlSelect);
@@ -953,6 +959,10 @@ class ImportXlsx extends ModeleImports
}
$sqlSelect .= " WHERE ".$keyfield." = ".((int) $lastinsertid);
+ if (!empty($tablewithentity_cache[$tablename])) {
+ $sqlSelect .= " AND entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
+ }
+
$resql = $this->db->query($sqlSelect);
if ($resql) {
$res = $this->db->fetch_object($resql);
@@ -998,6 +1008,10 @@ class ImportXlsx extends ModeleImports
$sqlend = " WHERE " . implode(' AND ', $where);
}
+ if (!empty($tablewithentity_cache[$tablename])) {
+ $sqlend .= " AND entity IN (".getEntity($this->getElementFromTableWithPrefix($tablename)).")";
+ }
+
$sql = $sqlstart . $sqlend;
// Run update request
diff --git a/htdocs/core/modules/import/modules_import.php b/htdocs/core/modules/import/modules_import.php
index fa4a5ad3692..5fe408f6dd5 100644
--- a/htdocs/core/modules/import/modules_import.php
+++ b/htdocs/core/modules/import/modules_import.php
@@ -75,14 +75,53 @@ class ModeleImports
public $libversion = array();
+ /**
+ * @var array Element mapping from table name
+ */
+ public static $mapTableToElement = array(
+ 'actioncomm' => 'agenda',
+ 'adherent' => 'member',
+ 'adherent_type' => 'member_type',
+ //'bank_account' => 'bank_account',
+ 'categorie' => 'category',
+ //'commande' => 'commande',
+ //'commande_fournisseur' => 'commande_fournisseur',
+ 'contrat' => 'contract',
+ 'entrepot' => 'stock',
+ //'expensereport' => 'expensereport',
+ 'facture' => 'invoice',
+ //'facture_fourn' => 'facture_fourn',
+ 'fichinter' => 'intervention',
+ //'holiday' => 'holiday',
+ //'product' => 'product',
+ 'product_price' => 'productprice',
+ 'product_fournisseur_price' => 'productsupplierprice',
+ 'projet' => 'project',
+ //'propal' => 'propal',
+ //'societe' => 'societe',
+ 'socpeople' => 'contact',
+ //'supplier_proposal' => 'supplier_proposal',
+ //'ticket' => 'ticket',
+ );
/**
* Constructor
*/
public function __construct()
{
- }
+ global $hookmanager;
+ if (is_object($hookmanager)) {
+ $hookmanager->initHooks(array('import'));
+ $parameters = array();
+ $reshook = $hookmanager->executeHooks('constructModeleImports', $parameters, $this);
+ if ($reshook >= 0 && !empty($hookmanager->resArray)) {
+ foreach ($hookmanager->resArray as $mapList) {
+ self::$mapTableToElement[$mapList['table']] = $mapList['element'];
+ }
+ }
+ }
+ }
/**
* getDriverId
@@ -267,4 +306,22 @@ class ModeleImports
{
return $this->libversion[$key];
}
+
+ /**
+ * Get element from table name with prefix
+ *
+ * @param string $tableNameWithPrefix Table name with prefix
+ * @return string Element name or table element as default
+ */
+ public function getElementFromTableWithPrefix($tableNameWithPrefix)
+ {
+ $tableElement = preg_replace('/^'.preg_quote($this->db->prefix(), '/').'/', '', $tableNameWithPrefix);
+ $element = $tableElement;
+
+ if (isset(self::$mapTableToElement[$tableElement])) {
+ $element = self::$mapTableToElement[$tableElement];
+ }
+
+ return $element;
+ }
}
diff --git a/htdocs/core/modules/modEventOrganization.class.php b/htdocs/core/modules/modEventOrganization.class.php
index f037f26176c..1bec1c5c67e 100644
--- a/htdocs/core/modules/modEventOrganization.class.php
+++ b/htdocs/core/modules/modEventOrganization.class.php
@@ -375,6 +375,11 @@ class modEventOrganization extends DolibarrModules
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
$formmail = new FormMail($this->db);
+ include_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
+ if (!is_object($user)) {
+ $user = new User($this->db); // To avoid error during migration
+ }
+
$template = $formmail->getEMailTemplate($this->db, 'conferenceorbooth', $user, $langs, 0, 1, '(EventOrganizationEmailAskConf)');
if ($template->id > 0) {
dolibarr_set_const($this->db, 'EVENTORGANIZATION_TEMPLATE_EMAIL_ASK_CONF', $template->id, 'chaine', 0, '', $conf->entity);
diff --git a/htdocs/core/modules/modFacture.class.php b/htdocs/core/modules/modFacture.class.php
index e3d39ea5848..64fca2dde60 100644
--- a/htdocs/core/modules/modFacture.class.php
+++ b/htdocs/core/modules/modFacture.class.php
@@ -135,7 +135,7 @@ class modFacture extends DolibarrModules
'objectname'=>'Facture',
'method'=>'sendEmailsRemindersOnInvoiceDueDate',
'parameters'=>"10,all,EmailTemplateCode",
- 'comment'=>'Send an emails when the unpaid invoices reach a due date + n days = today. First param is the offset n of days, second parameter is "all" or a payment mode code, last parameter is the code of email template to use (an email template with EmailTemplateCode must exists. The version in the language of the thirdparty will be used in priority to update the PDF of the sent invoice).',
+ 'comment'=>'Send an emails when we reach the due date - n days of an invoice. First param is n, the number of days before due date to send the remind, second parameter is "all" or a payment mode code, last parameter is the code of email template to use (an email template with the EmailTemplateCode must exists. The version of the email template in the language of the thirdparty will be used in priority. Language of the thirdparty will be also used to update the PDF of the sent invoice).',
'frequency'=>1,
'unitfrequency'=>3600 * 24,
'priority'=>50,
diff --git a/htdocs/core/modules/modFournisseur.class.php b/htdocs/core/modules/modFournisseur.class.php
index b6286c35162..27c15bda16f 100644
--- a/htdocs/core/modules/modFournisseur.class.php
+++ b/htdocs/core/modules/modFournisseur.class.php
@@ -41,7 +41,7 @@ class modFournisseur extends DolibarrModules
*/
public function __construct($db)
{
- global $conf, $user;
+ global $conf, $langs, $user;
$this->db = $db;
$this->numero = 40;
@@ -309,6 +309,8 @@ class modFournisseur extends DolibarrModules
//--------
$r = 0;
+ $langs->loadLangs(array("suppliers", "multicurrency"));
+
$r++;
$this->export_code[$r] = $this->rights_class.'_'.$r;
$this->export_label[$r] = 'Vendor invoices and lines of invoices';
diff --git a/htdocs/core/modules/modProduct.class.php b/htdocs/core/modules/modProduct.class.php
index 96fdb779b0b..a6fc98ffc08 100644
--- a/htdocs/core/modules/modProduct.class.php
+++ b/htdocs/core/modules/modProduct.class.php
@@ -517,7 +517,7 @@ class modProduct extends DolibarrModules
'p.price_base_type' => "PriceBaseType", //price base: with-tax (TTC) or without (HT) tax. Displays accordingly in Product card
'p.tva_tx' => 'VATRate',
'p.datec' => 'DateCreation',
- 'p.cost_price' => "CostPrice",
+ 'p.cost_price' => "CostPrice"
);
$this->import_convertvalue_array[$r] = array(
@@ -748,6 +748,33 @@ class modProduct extends DolibarrModules
$this->import_updatekeys_array[$r] = array_merge($this->import_updatekeys_array[$r], array('p.barcode'=>'BarCode')); //only show/allow barcode as update key if Barcode module enabled
}
+ if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE)) {
+ // Import products limit and desired stock by product and warehouse
+ $r++;
+ $this->import_code[$r] = $this->rights_class.'_stock_by_warehouse';
+ $this->import_label[$r] = "ProductStockWarehouse"; // Translation key
+ $this->import_icon[$r] = $this->picto;
+ $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('pwp'=>MAIN_DB_PREFIX.'product_warehouse_properties');
+ $this->import_fields_array[$r] = array('pwp.fk_product'=>"Product*",
+ 'pwp.fk_entrepot'=>"Warehouse*", 'pwp.seuil_stock_alerte'=>"StockLimit",
+ 'pwp.desiredstock'=>"DesiredStock");
+ $this->import_regex_array[$r] = array(
+ 'pwp.fk_product' => 'rowid@'.MAIN_DB_PREFIX.'product',
+ 'pwp.fk_entrepot' => 'rowid@'.MAIN_DB_PREFIX.'entrepot',
+ );
+ $this->import_convertvalue_array[$r] = array(
+ 'pwp.fk_product'=>array('rule'=>'fetchidfromref', 'classfile'=>'/product/class/product.class.php', 'class'=>'Product', 'method'=>'fetch', 'element'=>'Product')
+ ,'pwp.fk_entrepot'=>array('rule'=>'fetchidfromref', 'classfile'=>'/product/stock/class/entrepot.class.php', 'class'=>'Entrepot', 'method'=>'fetch', 'element'=>'Entrepot')
+ );
+ $this->import_examplevalues_array[$r] = array('pwp.fk_product'=>"ref:PRODUCT_REF or id:123456",
+ 'pwp.fk_entrepot'=>"ref:WAREHOUSE_REF or id:123456",
+ 'pwp.seuil_stock_alerte'=>"100",
+ 'pwp.desiredstock'=>"110"
+ );
+ $this->import_updatekeys_array[$r] = array('pwp.fk_product'=>'Product', 'pwp.fk_entrepot'=>'Warehouse');
+ }
+
if ((isModEnabled("fournisseur") && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) || isModEnabled("supplier_order") || isModEnabled("supplier_invoice")) {
// Import suppliers prices (note: this code is duplicated in module Service)
$r++;
@@ -764,8 +791,9 @@ class modProduct extends DolibarrModules
'sp.quantity' => "QtyMin*",
'sp.tva_tx' => 'VATRate',
'sp.default_vat_code' => 'VATCode',
- 'sp.delivery_time_days' => 'DeliveryDelay',
- 'sp.supplier_reputation' => 'SupplierReputation'
+ 'sp.delivery_time_days' => 'NbDaysToDelivery',
+ 'sp.supplier_reputation' => 'SupplierReputation',
+ 'sp.status' => 'Status'
);
if (is_object($mysoc) && $usenpr) {
$this->import_fields_array[$r] = array_merge($this->import_fields_array[$r], array('sp.recuperableonly'=>'VATNPR'));
@@ -827,7 +855,8 @@ class modProduct extends DolibarrModules
'sp.remise_percent'=>'0',
'sp.default_vat_code' => '',
'sp.delivery_time_days' => '5',
- 'sp.supplier_reputation' => 'FAVORITE / NOTTHGOOD / DONOTORDER'
+ 'sp.supplier_reputation' => 'FAVORITE / NOTTHGOOD / DONOTORDER',
+ 'sp.status' => '1'
);
if (is_object($mysoc) && $usenpr) {
$this->import_examplevalues_array[$r] = array_merge($this->import_examplevalues_array[$r], array('sp.recuperableonly'=>''));
diff --git a/htdocs/core/modules/modService.class.php b/htdocs/core/modules/modService.class.php
index a316c746acb..b4a6d2f6025 100644
--- a/htdocs/core/modules/modService.class.php
+++ b/htdocs/core/modules/modService.class.php
@@ -722,7 +722,7 @@ class modService extends DolibarrModules
'sp.quantity' => "QtyMin*",
'sp.tva_tx' => 'VATRate',
'sp.default_vat_code' => 'VATCode',
- 'sp.delivery_time_days' => 'DeliveryDelay',
+ 'sp.delivery_time_days' => 'NbDaysToDelivery',
'sp.supplier_reputation' => 'SupplierReputation'
);
if (is_object($mysoc) && $usenpr) {
diff --git a/htdocs/core/modules/modSupplierProposal.class.php b/htdocs/core/modules/modSupplierProposal.class.php
index c775744c6e7..674ba46601a 100644
--- a/htdocs/core/modules/modSupplierProposal.class.php
+++ b/htdocs/core/modules/modSupplierProposal.class.php
@@ -36,7 +36,6 @@ include_once DOL_DOCUMENT_ROOT.'/core/modules/DolibarrModules.class.php';
*/
class modSupplierProposal extends DolibarrModules
{
-
/**
* Constructor. Define names, constants, directories, boxes, permissions
*
diff --git a/htdocs/core/modules/modWebServices.class.php b/htdocs/core/modules/modWebServices.class.php
index 6f2e8e21c3e..2e03c6759ee 100644
--- a/htdocs/core/modules/modWebServices.class.php
+++ b/htdocs/core/modules/modWebServices.class.php
@@ -45,8 +45,8 @@ class modWebServices extends DolibarrModules
// 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 the Dolibarr web services server";
- // Possible values for version are: 'development', 'experimental', 'dolibarr' or version
- $this->version = 'dolibarr';
+ // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
+ $this->version = 'dolibarr_deprecated';
// Key used in llx_const table to save module status enabled/disabled (where MYMODULE is value of property name of module in uppercase)
$this->const_name = 'MAIN_MODULE_'.strtoupper($this->name);
// Name of image file used for this module.
diff --git a/htdocs/core/modules/modWebhook.class.php b/htdocs/core/modules/modWebhook.class.php
index c8ddea7dc06..e9f3e7f5baa 100644
--- a/htdocs/core/modules/modWebhook.class.php
+++ b/htdocs/core/modules/modWebhook.class.php
@@ -260,17 +260,17 @@ class modWebhook extends DolibarrModules
// Add here entries to declare new permissions
/* BEGIN MODULEBUILDER PERMISSIONS */
$this->rights[$r][0] = $this->numero . sprintf("%02d", $r + 1); // Permission id (must not be already used)
- $this->rights[$r][1] = 'Read objects of Webhook'; // Permission label
+ $this->rights[$r][1] = 'Read Webhooks'; // Permission label
$this->rights[$r][4] = 'webhook_target';
$this->rights[$r][5] = 'read'; // In php code, permission will be checked by test if ($user->rights->webhook->webhook_target->read)
$r++;
$this->rights[$r][0] = $this->numero . sprintf("%02d", $r + 1); // Permission id (must not be already used)
- $this->rights[$r][1] = 'Create/Update objects of Webhook'; // Permission label
+ $this->rights[$r][1] = 'Create/Update Webhooks'; // Permission label
$this->rights[$r][4] = 'webhook_target';
$this->rights[$r][5] = 'write'; // In php code, permission will be checked by test if ($user->rights->webhook->webhook_target->write)
$r++;
$this->rights[$r][0] = $this->numero . sprintf("%02d", $r + 1); // Permission id (must not be already used)
- $this->rights[$r][1] = 'Delete objects of Webhook'; // Permission label
+ $this->rights[$r][1] = 'Delete Webhooks'; // Permission label
$this->rights[$r][4] = 'webhook_target';
$this->rights[$r][5] = 'delete'; // In php code, permission will be checked by test if ($user->rights->webhook->webhook_target->delete)
$r++;
diff --git a/htdocs/core/modules/printing/printgcp.modules.php b/htdocs/core/modules/printing/printgcp.modules.php
index c1b6ba6c86e..d62dbd94ab2 100644
--- a/htdocs/core/modules/printing/printgcp.modules.php
+++ b/htdocs/core/modules/printing/printgcp.modules.php
@@ -121,7 +121,7 @@ class printing_printgcp extends PrintingDriver
$this->google_id = getDolGlobalString('OAUTH_GOOGLE_ID');
$this->google_secret = getDolGlobalString('OAUTH_GOOGLE_SECRET');
// Token storage
- $storage = new DoliStorage($this->db, $this->conf, $keyforprovider);
+ $storage = new DoliStorage($this->db, $conf, $keyforprovider);
//$storage->clearToken($this->OAUTH_SERVICENAME_GOOGLE);
// Setup the credentials for the requests
$credentials = new Credentials(
@@ -255,12 +255,13 @@ class printing_printgcp extends PrintingDriver
*/
public function getlistAvailablePrinters()
{
+ global $conf;
$ret = array();
$keyforprovider = ''; // @FIXME
// Token storage
- $storage = new DoliStorage($this->db, $this->conf, $keyforprovider);
+ $storage = new DoliStorage($this->db, $conf, $keyforprovider);
// Setup the credentials for the requests
$credentials = new Credentials(
$this->google_id,
@@ -377,6 +378,7 @@ class printing_printgcp extends PrintingDriver
*/
public function sendPrintToPrinter($printerid, $printjobtitle, $filepath, $contenttype)
{
+ global $conf;
// Check if printer id
if (empty($printerid)) {
return array('status' =>0, 'errorcode' =>'', 'errormessage'=>'No provided printer ID');
@@ -401,7 +403,7 @@ class printing_printgcp extends PrintingDriver
$keyforprovider = ''; // @FIXME
// Dolibarr Token storage
- $storage = new DoliStorage($this->db, $this->conf, $keyforprovider);
+ $storage = new DoliStorage($this->db, $conf, $keyforprovider);
// Setup the credentials for the requests
$credentials = new Credentials(
$this->google_id,
@@ -453,7 +455,7 @@ class printing_printgcp extends PrintingDriver
$keyforprovider = ''; // @FIXME
// Token storage
- $storage = new DoliStorage($this->db, $this->conf, $keyforprovider);
+ $storage = new DoliStorage($this->db, $conf, $keyforprovider);
// Setup the credentials for the requests
$credentials = new Credentials(
$this->google_id,
diff --git a/htdocs/core/modules/project/doc/pdf_baleine.modules.php b/htdocs/core/modules/project/doc/pdf_baleine.modules.php
index 2bfd73757c9..06f6e400b75 100644
--- a/htdocs/core/modules/project/doc/pdf_baleine.modules.php
+++ b/htdocs/core/modules/project/doc/pdf_baleine.modules.php
@@ -195,11 +195,11 @@ class pdf_baleine extends ModelePDFProjects
// Load traductions files required by page
$outputlangs->loadLangs(array("main", "dict", "companies", "projects"));
- if ($conf->project->dir_output) {
+ if ($conf->project->multidir_output[$object->entity]) {
//$nblines = count($object->lines); // This is set later with array of tasks
$objectref = dol_sanitizeFileName($object->ref);
- $dir = $conf->project->dir_output;
+ $dir = $conf->project->multidir_output[$object->entity];
if (!preg_match('/specimen/i', $objectref)) {
$dir .= "/".$objectref;
}
diff --git a/htdocs/core/modules/project/doc/pdf_beluga.modules.php b/htdocs/core/modules/project/doc/pdf_beluga.modules.php
index c1365fde536..ba175dc7c52 100644
--- a/htdocs/core/modules/project/doc/pdf_beluga.modules.php
+++ b/htdocs/core/modules/project/doc/pdf_beluga.modules.php
@@ -242,11 +242,11 @@ class pdf_beluga extends ModelePDFProjects
// Load traductions files required by page
$outputlangs->loadLangs(array("main", "dict", "companies", "projects"));
- if ($conf->project->dir_output) {
+ if ($conf->project->multidir_output[$object->entity]) {
//$nblines = count($object->lines); // This is set later with array of tasks
$objectref = dol_sanitizeFileName($object->ref);
- $dir = $conf->project->dir_output;
+ $dir = $conf->project->multidir_output[$object->entity];
if (!preg_match('/specimen/i', $objectref)) {
$dir .= "/".$objectref;
}
diff --git a/htdocs/core/modules/project/doc/pdf_timespent.modules.php b/htdocs/core/modules/project/doc/pdf_timespent.modules.php
index c5aa80d5748..31a8127f733 100644
--- a/htdocs/core/modules/project/doc/pdf_timespent.modules.php
+++ b/htdocs/core/modules/project/doc/pdf_timespent.modules.php
@@ -195,11 +195,11 @@ class pdf_timespent extends ModelePDFProjects
// Load traductions files required by page
$outputlangs->loadLangs(array("main", "dict", "companies", "projects"));
- if ($conf->project->dir_output) {
+ if ($conf->project->multidir_output[$object->entity]) {
//$nblines = count($object->lines); // This is set later with array of tasks
$objectref = dol_sanitizeFileName($object->ref);
- $dir = $conf->project->dir_output;
+ $dir = $conf->project->multidir_output[$object->entity];
if (!preg_match('/specimen/i', $objectref)) {
$dir .= "/".$objectref;
}
diff --git a/htdocs/core/modules/syslog/mod_syslog_file.php b/htdocs/core/modules/syslog/mod_syslog_file.php
index 73ebc0d1d90..116e49487f9 100644
--- a/htdocs/core/modules/syslog/mod_syslog_file.php
+++ b/htdocs/core/modules/syslog/mod_syslog_file.php
@@ -125,7 +125,7 @@ class mod_syslog_file extends LogHandler implements LogHandlerInterface
}
}
- return $suffixinfilename ?preg_replace('/\.log$/i', $suffixinfilename.'.log', $tmp) : $tmp;
+ return $suffixinfilename ? preg_replace('/\.log$/i', $suffixinfilename.'.log', $tmp) : $tmp;
}
/**
diff --git a/htdocs/core/tpl/admin_extrafields_add.tpl.php b/htdocs/core/tpl/admin_extrafields_add.tpl.php
index 00dec927f9e..5404f831a65 100644
--- a/htdocs/core/tpl/admin_extrafields_add.tpl.php
+++ b/htdocs/core/tpl/admin_extrafields_add.tpl.php
@@ -209,7 +209,7 @@ $listofexamplesforlink = 'Societe:societe/class/societe.class.php
Contact:con
-
+
textwithpicto($langs->trans("CssOnEdit"), $langs->trans("HelpCssOnEditDesc")); ?>
@@ -220,7 +220,7 @@ $listofexamplesforlink = 'Societe:societe/class/societe.class.php
Contact:con
textwithpicto($langs->trans("HelpOnTooltip"), $langs->trans("HelpOnTooltipDesc")); ?>
-
trans("AllEntities"); ?> >
+
trans("AllEntities"); ?> >
diff --git a/htdocs/core/tpl/bloc_showhide.tpl.php b/htdocs/core/tpl/bloc_showhide.tpl.php
index ee67fa6b74a..047e09a1e93 100644
--- a/htdocs/core/tpl/bloc_showhide.tpl.php
+++ b/htdocs/core/tpl/bloc_showhide.tpl.php
@@ -52,9 +52,9 @@ print ' $("#hide-'.$blocname.'").show();'."\n";
print '});'."\n";
print 'function setShowHide(status) {'."\n";
-print ' var id = '.$object->id.";\n";
-print " var element = '".$object->element."';\n";
-print " var htmlelement = '".$blocname."';\n";
+print ' var id = '.((int) $object->id).";\n";
+print " var element = '".dol_escape_js($object->element)."';\n";
+print " var htmlelement = '".dol_escape_js($blocname)."';\n";
print ' var type = "showhide";'."\n";
print ' $.get("'.dol_buildpath('/core/ajax/extraparams.php', 1);
print '?id="+id+"&element="+element+"&htmlelement="+htmlelement+"&type="+type+"&value="+status);'."\n";
diff --git a/htdocs/core/tpl/extrafields_list_search_input.tpl.php b/htdocs/core/tpl/extrafields_list_search_input.tpl.php
index f57101b81e6..3f90715cbea 100644
--- a/htdocs/core/tpl/extrafields_list_search_input.tpl.php
+++ b/htdocs/core/tpl/extrafields_list_search_input.tpl.php
@@ -47,7 +47,7 @@ if (!empty($extrafieldsobjectkey)) { // $extrafieldsobject is the $object->table
if (in_array($typeofextrafield, array('link', 'sellist', 'text', 'html'))) {
$morecss = 'maxwidth200';
}
- echo $extrafields->showInputField($key, (empty($search_array_options[$search_options_pattern.$tmpkey]) ? '' : $search_array_options[$search_options_pattern.$tmpkey]), '', '', $search_options_pattern, $morecss, 0, $extrafieldsobjectkey, 1);
+ echo $extrafields->showInputField($key, (!isset($search_array_options[$search_options_pattern.$tmpkey]) ? '' : $search_array_options[$search_options_pattern.$tmpkey]), '', '', $search_options_pattern, $morecss, 0, $extrafieldsobjectkey, 1);
}
print '';
}
diff --git a/htdocs/core/tpl/extrafields_view.tpl.php b/htdocs/core/tpl/extrafields_view.tpl.php
index ec7578e0aa4..38b99fc5736 100644
--- a/htdocs/core/tpl/extrafields_view.tpl.php
+++ b/htdocs/core/tpl/extrafields_view.tpl.php
@@ -212,7 +212,7 @@ if (empty($reshook) && isset($extrafields->attributes[$object->table_element]['l
$html_id = !empty($object->id) ? $object->element.'_extras_'.$tmpkeyextra.'_'.$object->id : '';
- print '
'.$langs->trans("Description").' ';
- print nl2br($project->description);
+ print dol_htmlentitiesbr($project->description);
print ' ';
// Categories
@@ -933,7 +933,7 @@ while ($i < $imaxinloop) {
if (!empty($arrayfields['t.'.$key]['checked'])) {
print '
$key)) {
print ' title="'.dol_escape_htmltag($object->$key).'"';
}
print '>';
diff --git a/htdocs/eventorganization/conferenceorboothattendee_list.php b/htdocs/eventorganization/conferenceorboothattendee_list.php
index 8f55a4e558c..1582da86987 100644
--- a/htdocs/eventorganization/conferenceorboothattendee_list.php
+++ b/htdocs/eventorganization/conferenceorboothattendee_list.php
@@ -41,11 +41,8 @@ if (isModEnabled('categorie')) {
global $dolibarr_main_url_root;
-// for other modules
-//dol_include_once('/othermodule/class/otherobject.class.php');
-
// Load translation files required by the page
-$langs->loadLangs(array("eventorganization", "other", "projects"));
+$langs->loadLangs(array("eventorganization", "other", "projects", "bills"));
// Get Paramters
$action = GETPOST('action', 'aZ09') ?GETPOST('action', 'aZ09') : 'view'; // The action 'add', 'create', 'edit', 'update', 'view', ...
diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php
index a9d11ca1eac..dcc50eff7dc 100644
--- a/htdocs/expedition/card.php
+++ b/htdocs/expedition/card.php
@@ -614,6 +614,7 @@ if (empty($reshook)) {
$num_prod = count($lines);
for ($i = 0; $i < $num_prod; $i++) {
if ($lines[$i]->id == $line_id) { // we have found line to update
+ $update_done = false;
$line = new ExpeditionLigne($db);
$line->fk_expedition = $object->id;
@@ -654,6 +655,8 @@ if (empty($reshook)) {
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
+ } else {
+ $update_done=true;
}
} else {
setEventMessages($lotStock->error, $lotStock->errors, 'errors');
@@ -696,6 +699,8 @@ if (empty($reshook)) {
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
+ } else {
+ $update_done=true;
}
} else {
setEventMessages($line->error, $line->errors, 'errors');
@@ -713,6 +718,8 @@ if (empty($reshook)) {
if ($object->create_line_batch($line, $line->array_options) < 0) {
setEventMessages($object->error, $object->errors, 'errors');
$error++;
+ } else {
+ $update_done=true;
}
}
} else {
@@ -750,6 +757,8 @@ if (empty($reshook)) {
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
+ } else {
+ $update_done=true;
}
}
unset($_POST[$stockLocation]);
@@ -764,6 +773,8 @@ if (empty($reshook)) {
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
+ } else {
+ $update_done=true;
}
unset($_POST[$qty]);
}
@@ -776,10 +787,17 @@ if (empty($reshook)) {
if ($line->update($user) < 0) {
setEventMessages($line->error, $line->errors, 'errors');
$error++;
+ } else {
+ $update_done=true;
}
unset($_POST[$qty]);
}
}
+
+ if (empty($update_done)) {
+ $line->id = $lines[$i]->id;
+ $line->insertExtraFields();
+ }
}
}
diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php
index 7b1c8ae6df7..8f024665a77 100644
--- a/htdocs/expedition/class/expedition.class.php
+++ b/htdocs/expedition/class/expedition.class.php
@@ -754,77 +754,8 @@ class Expedition extends CommonObject
// If stock increment is done on sending (recommanded choice)
if (!$error && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT)) {
- require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
-
- $langs->load("agenda");
-
- // Loop on each product line to add a stock movement
- $sql = "SELECT cd.fk_product, cd.subprice,";
- $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
- $sql .= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock";
- $sql .= " FROM ".MAIN_DB_PREFIX."commandedet as cd,";
- $sql .= " ".MAIN_DB_PREFIX."expeditiondet as ed";
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid";
- $sql .= " WHERE ed.fk_expedition = ".((int) $this->id);
- $sql .= " AND cd.rowid = ed.fk_origin_line";
-
- dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if ($resql) {
- $cpt = $this->db->num_rows($resql);
- for ($i = 0; $i < $cpt; $i++) {
- $obj = $this->db->fetch_object($resql);
- if (empty($obj->edbrowid)) {
- $qty = $obj->qty;
- } else {
- $qty = $obj->edbqty;
- }
-
- if ($qty == 0 || ($qty < 0 && !getDolGlobalInt('SHIPMENT_ALLOW_NEGATIVE_QTY'))) {
- continue;
- }
- dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
-
- //var_dump($this->lines[$i]);
- $mouvS = new MouvementStock($this->db);
-
- $mouvS->setOrigin($this->element, $this->id);
-
- if (empty($obj->edbrowid)) {
- // line without batch detail
-
- // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record.
- $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr", $numref), '', '', '', '', 0, '', 1);
-
- if ($result < 0) {
- $error++;
- $this->error = $mouvS->error;
- $this->errors = array_merge($this->errors, $mouvS->errors);
- break;
- }
- } else {
- // line with batch detail
-
- // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record.
- // Note: ->fk_origin_stock = id into table llx_product_batch (may be renamed into llx_product_stock_batch in another version)
- $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr", $numref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock, '', 1);
- if ($result < 0) {
- $error++;
- $this->error = $mouvS->error;
- $this->errors = array_merge($this->errors, $mouvS->errors);
- break;
- }
- }
- }
-
- // If some stock lines are now 0, we can remove entry into llx_product_stock, but only if there is no child lines into llx_product_batch (detail of batch, because we can imagine
- // having a lot1/qty=X and lot2/qty=-X, so 0 but we must not loose repartition of different lot.
- $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_stock WHERE reel = 0 AND rowid NOT IN (SELECT fk_product_stock FROM ".MAIN_DB_PREFIX."product_batch as pb)";
- $resql = $this->db->query($sql);
- // We do not test error, it can fails if there is child in batch details
- } else {
- $this->db->rollback();
- $this->error = $this->db->error();
+ $result = $this->manageStockMvtOnEvt($user);
+ if ($result < 0) {
return -2;
}
}
@@ -1834,7 +1765,8 @@ class Expedition extends CommonObject
{
global $conf, $langs;
- $langs->load('shipping');
+ $langs->load('sendings');
+
$nofetch = !empty($params['nofetch']);
$datas = array();
@@ -1844,6 +1776,13 @@ class Expedition extends CommonObject
}
$datas['ref'] = ''.$langs->trans('Ref').': '.$this->ref;
$datas['refcustomer'] = ''.$langs->trans('RefCustomer').': '.($this->ref_customer ? $this->ref_customer : $this->ref_client);
+ if (!$nofetch) {
+ $langs->load('companies');
+ if (empty($this->thirdparty)) {
+ $this->fetch_thirdparty();
+ }
+ $datas['customer'] = ''.$langs->trans('Customer').': '.$this->thirdparty->getNomUrl(1, '', 0, 1);
+ }
return $datas;
}
@@ -1874,10 +1813,11 @@ class Expedition extends CommonObject
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = ' data-params='.json_encode($params);
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
$url = DOL_URL_ROOT.'/expedition/card.php?id='.$this->id;
@@ -1902,8 +1842,8 @@ class Expedition extends CommonObject
$label = $langs->trans("Shipment");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= $dataparams.' title="'.dol_escape_htmltag($label, 1).'"';
- $linkclose .= ' class="'.$classfortooltip.'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
+ $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
}
$linkstart = 'global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)) {
- require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
-
- $langs->load("agenda");
-
- // Loop on each product line to add a stock movement
- // TODO possibilite d'expedier a partir d'une propale ou autre origine ?
- $sql = "SELECT cd.fk_product, cd.subprice,";
- $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
- $sql .= " e.ref,";
- $sql .= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock";
- $sql .= " FROM ".MAIN_DB_PREFIX."commandedet as cd,";
- $sql .= " ".MAIN_DB_PREFIX."expeditiondet as ed";
- $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid";
- $sql .= " INNER JOIN ".MAIN_DB_PREFIX."expedition as e ON ed.fk_expedition = e.rowid";
- $sql .= " WHERE ed.fk_expedition = ".((int) $this->id);
- $sql .= " AND cd.rowid = ed.fk_origin_line";
-
- dol_syslog(get_class($this)."::valid select details", LOG_DEBUG);
- $resql = $this->db->query($sql);
- if ($resql) {
- $cpt = $this->db->num_rows($resql);
- for ($i = 0; $i < $cpt; $i++) {
- $obj = $this->db->fetch_object($resql);
- if (empty($obj->edbrowid)) {
- $qty = $obj->qty;
- } else {
- $qty = $obj->edbqty;
- }
- if ($qty <= 0) {
- continue;
- }
- dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid);
-
- $mouvS = new MouvementStock($this->db);
- $mouvS->origin = &$this;
- $mouvS->setOrigin($this->element, $this->id);
-
- if (empty($obj->edbrowid)) {
- // line without batch detail
-
- // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
- $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentClassifyClosedInDolibarr", $obj->ref));
- if ($result < 0) {
- $this->error = $mouvS->error;
- $this->errors = $mouvS->errors;
- $error++;
- break;
- }
- } else {
- // line with batch detail
-
- // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
- $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentClassifyClosedInDolibarr", $obj->ref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock);
- if ($result < 0) {
- $this->error = $mouvS->error;
- $this->errors = $mouvS->errors;
- $error++;
- break;
- }
- }
- }
- } else {
- $this->error = $this->db->lasterror();
+ $result = $this->manageStockMvtOnEvt($user);
+ if ($result<0) {
$error++;
}
}
@@ -2309,6 +2188,95 @@ class Expedition extends CommonObject
}
}
+ /**
+ * Manage Stock MVt onb Close or valid Shipment
+ * @param User $user Object user that modify
+ * @return int <0 if ko, >0 if ok
+ * @throws Exception
+ *
+ */
+ private function manageStockMvtOnEvt($user)
+ {
+ global $langs;
+
+ $error=0;
+
+ require_once DOL_DOCUMENT_ROOT . '/product/stock/class/mouvementstock.class.php';
+
+ $langs->load("agenda");
+
+ // Loop on each product line to add a stock movement
+ $sql = "SELECT cd.fk_product, cd.subprice,";
+ $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
+ $sql .= " e.ref,";
+ $sql .= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock";
+ $sql .= " ,cd.rowid as cdid, ed.rowid as edid";
+ $sql .= " FROM " . MAIN_DB_PREFIX . "commandedet as cd,";
+ $sql .= " " . MAIN_DB_PREFIX . "expeditiondet as ed";
+ $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid";
+ $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "expedition as e ON ed.fk_expedition = e.rowid";
+ $sql .= " WHERE ed.fk_expedition = " . ((int) $this->id);
+ $sql .= " AND cd.rowid = ed.fk_origin_line";
+
+ dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
+ $resql = $this->db->query($sql);
+ if ($resql) {
+ $cpt = $this->db->num_rows($resql);
+ for ($i = 0; $i < $cpt; $i++) {
+ $obj = $this->db->fetch_object($resql);
+ if (empty($obj->edbrowid)) {
+ $qty = $obj->qty;
+ } else {
+ $qty = $obj->edbqty;
+ }
+ if ($qty <= 0 || ($qty < 0 && !getDolGlobalInt('SHIPMENT_ALLOW_NEGATIVE_QTY'))) {
+ continue;
+ }
+ dol_syslog(get_class($this) . "::valid movement index " . $i . " ed.rowid=" . $obj->rowid . " edb.rowid=" . $obj->edbrowid);
+
+ $mouvS = new MouvementStock($this->db);
+ $mouvS->origin = &$this;
+ $mouvS->setOrigin($this->element, $this->id, $obj->cdid, $obj->edid);
+
+ if (empty($obj->edbrowid)) {
+ // line without batch detail
+
+ // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
+ $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentClassifyClosedInDolibarr", $obj->ref));
+ if ($result < 0) {
+ $this->error = $mouvS->error;
+ $this->errors = $mouvS->errors;
+ $error++;
+ break;
+ }
+ } else {
+ // line with batch detail
+
+ // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record
+ $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentClassifyClosedInDolibarr", $obj->ref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock);
+ if ($result < 0) {
+ $this->error = $mouvS->error;
+ $this->errors = $mouvS->errors;
+ $error++;
+ break;
+ }
+ }
+
+ // If some stock lines are now 0, we can remove entry into llx_product_stock, but only if there is no child lines into llx_product_batch (detail of batch, because we can imagine
+ // having a lot1/qty=X and lot2/qty=-X, so 0 but we must not loose repartition of different lot.
+ $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_stock WHERE reel = 0 AND rowid NOT IN (SELECT fk_product_stock FROM ".MAIN_DB_PREFIX."product_batch as pb)";
+ $resql = $this->db->query($sql);
+ // We do not test error, it can fails if there is child in batch details
+ }
+ } else {
+ $this->error = $this->db->lasterror();
+ $this->errors[] = $this->db->lasterror();
+ $error ++;
+ }
+
+ return $error;
+ }
+
/**
* Classify the shipping as invoiced (used when WORKFLOW_BILL_ON_SHIPMENT is on)
*
diff --git a/htdocs/expedition/index.php b/htdocs/expedition/index.php
index a644aeedbad..6cae17a835c 100644
--- a/htdocs/expedition/index.php
+++ b/htdocs/expedition/index.php
@@ -31,6 +31,7 @@ require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
$hookmanager = new HookManager($db);
+
$socid = GETPOST('socid', 'int');
// Initialize technical object to manage hooks. Note that conf->hooks_modules contains array
diff --git a/htdocs/expedition/stats/index.php b/htdocs/expedition/stats/index.php
index 9213e2ca849..c14d8c6d3b2 100644
--- a/htdocs/expedition/stats/index.php
+++ b/htdocs/expedition/stats/index.php
@@ -65,7 +65,7 @@ llxHeader();
print load_fiche_titre($langs->trans("StatisticsOfSendings"), '', 'dolly');
-
+$dir = (!empty($conf->expedition->multidir_temp[$conf->entity]) ? $conf->expedition->multidir_temp[$conf->entity] : $conf->service->multidir_temp[$conf->entity]);
dol_mkdir($dir);
$stats = new ExpeditionStats($db, $socid, '', ($userid > 0 ? $userid : 0));
@@ -84,6 +84,7 @@ if (empty($user->rights->societe->client->voir) || $user->socid) {
$px1 = new DolGraph();
$mesg = $px1->isGraphKo();
+$fileurlnb = '';
if (!$mesg) {
$px1->SetData($data);
$i = $startyear; $legend = array();
diff --git a/htdocs/expensereport/class/expensereport.class.php b/htdocs/expensereport/class/expensereport.class.php
index ebbc83302d5..3cf5d8a5003 100644
--- a/htdocs/expensereport/class/expensereport.class.php
+++ b/htdocs/expensereport/class/expensereport.class.php
@@ -1767,10 +1767,11 @@ class ExpenseReport extends CommonObject
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = " data-params='".json_encode($params)."'";
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
if ($option != 'nolink') {
// Add param to save lastsearch_values or not
@@ -1794,8 +1795,8 @@ class ExpenseReport extends CommonObject
$label = $langs->trans("ShowExpenseReport");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= $dataparams.' title="'.dol_escape_htmltag($label, 1).'"';
- $linkclose .= ' class="'.$classfortooltip.'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
+ $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
}
$linkstart = ' datepaid) {
+ if (!$this->datep) {
$this->error = 'ErrorBadValueForParameterCreatePaymentExpenseReport';
return -1;
}
@@ -170,7 +169,7 @@ class PaymentExpenseReport extends CommonObject
$sql = "INSERT INTO ".MAIN_DB_PREFIX."payment_expensereport (fk_expensereport, datec, datep, amount,";
$sql .= " fk_typepayment, num_payment, note, fk_user_creat, fk_bank)";
$sql .= " VALUES ($this->fk_expensereport, '".$this->db->idate($now)."',";
- $sql .= " '".$this->db->idate($this->datepaid)."',";
+ $sql .= " '".$this->db->idate($this->datep)."',";
$sql .= " ".price2num($totalamount).",";
$sql .= " ".((int) $this->fk_typepayment).", '".$this->db->escape($this->num_payment)."', '".$this->db->escape($this->note_public)."', ".((int) $user->id).",";
$sql .= " 0)"; // fk_bank is ID of transaction into ll_bank
@@ -531,7 +530,7 @@ class PaymentExpenseReport extends CommonObject
// Insert payment into llx_bank
$bank_line_id = $acc->addline(
- $this->datepaid,
+ $this->datep,
$this->fk_typepayment, // Payment mode id or code ("CHQ or VIR for example")
$label,
-$amount,
diff --git a/htdocs/expensereport/payment/payment.php b/htdocs/expensereport/payment/payment.php
index 6456bf80d7f..20b49b1533c 100644
--- a/htdocs/expensereport/payment/payment.php
+++ b/htdocs/expensereport/payment/payment.php
@@ -107,8 +107,8 @@ if ($action == 'add_payment') {
// Create a line of payments
$payment = new PaymentExpenseReport($db);
$payment->fk_expensereport = $expensereport->id;
- $payment->datepaid = $datepaid;
- $payment->amounts = $amounts; // Tableau de montant
+ $payment->datep = $datepaid;
+ $payment->amounts = $amounts; // Tableau de montant
$payment->total = $total;
$payment->fk_typepayment = GETPOST("fk_typepayment", 'int');
$payment->num_payment = GETPOST("num_payment", 'alphanothtml');
diff --git a/htdocs/exports/export.php b/htdocs/exports/export.php
index f35ae3a3121..1afd41c6027 100644
--- a/htdocs/exports/export.php
+++ b/htdocs/exports/export.php
@@ -1180,7 +1180,8 @@ if ($step == 5 && $datatoexport) {
print ' ';
// List of available export formats
- $htmltabloflibs = ' ';
+ $htmltabloflibs = ' ';
+ $htmltabloflibs .= '';
$htmltabloflibs .= '';
$htmltabloflibs .= ''.$langs->trans("AvailableFormats").' ';
$htmltabloflibs .= ''.$langs->trans("LibraryUsed").' ';
@@ -1204,7 +1205,7 @@ if ($step == 5 && $datatoexport) {
$htmltabloflibs .= ''.$objmodelexport->getLibVersionForKey($key).' ';
$htmltabloflibs .= ' '."\n";
}
- $htmltabloflibs .= '
';
+ $htmltabloflibs .= '
';
print ''.$form->textwithpicto($langs->trans("NowClickToGenerateToBuildExportFile"), $htmltabloflibs, 1, 'help', '', 0, 2, 'helphonformat').' ';
//print $htmltabloflibs;
diff --git a/htdocs/fichinter/card.php b/htdocs/fichinter/card.php
index 31ae0ccaf65..a4244195bfa 100644
--- a/htdocs/fichinter/card.php
+++ b/htdocs/fichinter/card.php
@@ -607,7 +607,7 @@ if (empty($reshook)) {
// Extrafields
$extrafields->fetch_name_optionals_label($object->table_element_line);
$array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
- $objectline->array_options = $array_options;
+ $objectline->array_options = array_merge($objectline->array_options, $array_options);
$result = $objectline->update($user);
if ($result < 0) {
diff --git a/htdocs/fichinter/class/fichinter.class.php b/htdocs/fichinter/class/fichinter.class.php
index 45a78d2914d..2456c71e985 100644
--- a/htdocs/fichinter/class/fichinter.class.php
+++ b/htdocs/fichinter/class/fichinter.class.php
@@ -815,10 +815,11 @@ class Fichinter extends CommonObject
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = " data-params='".json_encode($params)."'";
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
$url = DOL_URL_ROOT.'/fichinter/card.php?id='.$this->id;
@@ -839,8 +840,8 @@ class Fichinter extends CommonObject
$label = $langs->trans("ShowIntervention");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= $dataparams.' title="'.dol_escape_htmltag($label, 1).'"';
- $linkclose .= ' class="'.$classfortooltip.'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
+ $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
}
$linkstart = 'db->query($sql);
- if ($result) {
- $objp = $this->db->fetch_object($result);
+ $resql = $this->db->query($sql);
+ if ($resql) {
+ $objp = $this->db->fetch_object($resql);
$this->rowid = $objp->rowid;
- $this->id = $objp->rowid;
+ $this->id = $objp->rowid;
$this->fk_fichinter = $objp->fk_fichinter;
$this->date = $this->db->jdate($objp->date);
$this->datei = $this->db->jdate($objp->date); // For backward compatibility
@@ -1606,7 +1608,10 @@ class FichinterLigne extends CommonObjectLine
$this->duration = $objp->duree;
$this->rang = $objp->rang;
- $this->db->free($result);
+ $this->db->free($resql);
+
+ $this->fetch_optionals();
+
return 1;
} else {
$this->error = $this->db->error().' sql='.$sql;
@@ -1623,8 +1628,6 @@ class FichinterLigne extends CommonObjectLine
*/
public function insert($user, $notrigger = 0)
{
- global $langs, $conf;
-
$error = 0;
dol_syslog("FichinterLigne::insert rang=".$this->rang);
@@ -1714,8 +1717,6 @@ class FichinterLigne extends CommonObjectLine
*/
public function update($user, $notrigger = 0)
{
- global $langs, $conf;
-
$error = 0;
if (empty($this->date) && !empty($this->datei)) { // For backward compatibility
diff --git a/htdocs/fourn/ajax/getSupplierPrices.php b/htdocs/fourn/ajax/getSupplierPrices.php
index 66001ccc5a6..c08ed49dd83 100644
--- a/htdocs/fourn/ajax/getSupplierPrices.php
+++ b/htdocs/fourn/ajax/getSupplierPrices.php
@@ -67,6 +67,9 @@ if ($idprod > 0) {
$productSupplierArray = $producttmp->list_product_fournisseur_price($idprod, $sorttouse); // We list all price per supplier, and then firstly with the lower quantity. So we can choose first one with enough quantity into list.
if (is_array($productSupplierArray)) {
foreach ($productSupplierArray as $productSupplier) {
+ if (getDolGlobalInt("DISABLE_BAD_REPUTATION_PRODUCT_PRICE") && $productSupplier->supplier_reputation == "DONOTORDER")
+ continue;
+
$price = $productSupplier->fourn_price * (1 - $productSupplier->fourn_remise_percent / 100);
$unitprice = $productSupplier->fourn_unitprice * (1 - $productSupplier->fourn_remise_percent / 100);
diff --git a/htdocs/fourn/class/fournisseur.commande.class.php b/htdocs/fourn/class/fournisseur.commande.class.php
index 1e3f74389d5..e3f8e3c5f81 100644
--- a/htdocs/fourn/class/fournisseur.commande.class.php
+++ b/htdocs/fourn/class/fournisseur.commande.class.php
@@ -260,25 +260,25 @@ class CommandeFournisseur extends CommonOrder
'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'Ref ext', 'enabled'=>1, 'visible'=>0, 'position'=>35),
'ref_supplier' =>array('type'=>'varchar(255)', 'label'=>'RefOrderSupplierShort', 'enabled'=>1, 'visible'=>1, 'position'=>40, 'searchall'=>1),
'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label'=>'Project', 'enabled'=>"isModEnabled('project')", 'visible'=>-1, 'position'=>45),
- 'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>60),
- 'date_approve' =>array('type'=>'datetime', 'label'=>'DateApprove', 'enabled'=>1, 'visible'=>-1, 'position'=>62),
- 'date_approve2' =>array('type'=>'datetime', 'label'=>'DateApprove2', 'enabled'=>1, 'visible'=>3, 'position'=>64),
+ 'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>710),
+ 'date_approve' =>array('type'=>'datetime', 'label'=>'DateApprove', 'enabled'=>1, 'visible'=>-1, 'position'=>720),
+ 'date_approve2' =>array('type'=>'datetime', 'label'=>'DateApprove2', 'enabled'=>1, 'visible'=>3, 'position'=>725),
'date_commande' =>array('type'=>'date', 'label'=>'OrderDateShort', 'enabled'=>1, 'visible'=>1, 'position'=>70),
'date_livraison' =>array('type'=>'datetime', 'label'=>'DeliveryDate', 'enabled'=>'empty($conf->global->ORDER_DISABLE_DELIVERY_DATE)', 'visible'=>1, 'position'=>74),
- 'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>3, 'position'=>75),
+ 'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>3, 'position'=>41),
'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>3, 'notnull'=>-1, 'position'=>80),
- 'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>3, 'position'=>85),
- 'fk_user_approve' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval', 'enabled'=>1, 'visible'=>3, 'position'=>90),
- 'fk_user_approve2' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval2', 'enabled'=>1, 'visible'=>3, 'position'=>95),
+ 'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>3, 'position'=>711),
+ 'fk_user_approve' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval', 'enabled'=>1, 'visible'=>3, 'position'=>721),
+ 'fk_user_approve2' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserApproval2', 'enabled'=>1, 'visible'=>3, 'position'=>726),
'source' =>array('type'=>'smallint(6)', 'label'=>'Source', 'enabled'=>1, 'visible'=>3, 'notnull'=>1, 'position'=>100),
- 'billed' =>array('type'=>'smallint(6)', 'label'=>'Billed', 'enabled'=>1, 'visible'=>1, 'position'=>110),
- 'total_tva' =>array('type'=>'double(24,8)', 'label'=>'Tva', 'enabled'=>1, 'visible'=>1, 'position'=>130, 'isameasure'=>1),
- 'localtax1' =>array('type'=>'double(24,8)', 'label'=>'Localtax1', 'enabled'=>1, 'visible'=>3, 'position'=>135, 'isameasure'=>1),
- 'localtax2' =>array('type'=>'double(24,8)', 'label'=>'Localtax2', 'enabled'=>1, 'visible'=>3, 'position'=>140, 'isameasure'=>1),
- 'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>1, 'position'=>145, 'isameasure'=>1),
- 'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>150, 'isameasure'=>1),
- 'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>155, 'searchall'=>1),
- 'note_private' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>160, 'searchall'=>1),
+ 'billed' =>array('type'=>'smallint(6)', 'label'=>'Billed', 'enabled'=>1, 'visible'=>1, 'position'=>710),
+ 'total_ht' =>array('type'=>'double(24,8)', 'label'=>'AmountHT', 'enabled'=>1, 'visible'=>1, 'position'=>130, 'isameasure'=>1),
+ 'total_tva' =>array('type'=>'double(24,8)', 'label'=>'AmountVAT', 'enabled'=>1, 'visible'=>1, 'position'=>135, 'isameasure'=>1),
+ 'localtax1' =>array('type'=>'double(24,8)', 'label'=>'LT1', 'enabled'=>1, 'visible'=>3, 'position'=>140, 'isameasure'=>1),
+ 'localtax2' =>array('type'=>'double(24,8)', 'label'=>'LT2', 'enabled'=>1, 'visible'=>3, 'position'=>145, 'isameasure'=>1),
+ 'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'AmountTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>150, 'isameasure'=>1),
+ 'note_public' =>array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>750, 'searchall'=>1),
+ 'note_private' =>array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>760, 'searchall'=>1),
'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPDF', 'enabled'=>1, 'visible'=>0, 'position'=>165),
'fk_input_method' =>array('type'=>'integer', 'label'=>'OrderMode', 'enabled'=>1, 'visible'=>3, 'position'=>170),
'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>3, 'position'=>175),
@@ -294,7 +294,7 @@ class CommandeFournisseur extends CommonOrder
'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>235),
'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-1, 'position'=>240),
'date_creation' =>array('type'=>'datetime', 'label'=>'Date creation', 'enabled'=>1, 'visible'=>-1, 'position'=>500),
- 'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'isModEnabled("societe")', 'visible'=>1, 'notnull'=>1, 'position'=>46),
+ 'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'isModEnabled("societe")', 'visible'=>1, 'notnull'=>1, 'position'=>50),
'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>1000, 'index'=>1),
'tms'=>array('type'=>'datetime', 'label'=>"DateModificationShort", 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>501),
'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>0, 'position'=>700),
@@ -873,6 +873,8 @@ class CommandeFournisseur extends CommonOrder
$langs->loadLangs(['bills', 'orders']);
$datas = [];
+ $nofetch = !empty($params['nofetch']);
+
if ($user->hasRight("fournisseur", "commande", "read")) {
$datas['picto'] = ''.$langs->trans("SupplierOrder").' ';
if (isset($this->statut)) {
@@ -884,6 +886,13 @@ class CommandeFournisseur extends CommonOrder
if (!empty($this->ref_supplier)) {
$datas['refsupplier'] = ''.$langs->trans('RefSupplier').': '.$this->ref_supplier;
}
+ if (!$nofetch) {
+ $langs->load('companies');
+ if (empty($this->thirdparty)) {
+ $this->fetch_thirdparty();
+ }
+ $datas['supplier'] = ''.$langs->trans('Supplier').': '.$this->thirdparty->getNomUrl(1, '', 0, 1);
+ }
if (!empty($this->total_ht)) {
$datas['totalht'] = ''.$langs->trans('AmountHT').': '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
}
@@ -922,17 +931,18 @@ class CommandeFournisseur extends CommonOrder
'id' => $this->id,
'objecttype' => $this->element,
'option' => $option,
+ 'nofetch' => 1
];
$classfortooltip = 'classfortooltip';
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = " data-params='".json_encode($params)."'";
- // $label = $langs->trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
-
$url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
if ($option !== 'nolink') {
@@ -952,8 +962,8 @@ class CommandeFournisseur extends CommonOrder
$label = $langs->trans("ShowOrder");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= $dataparams.' title="'.dol_escape_htmltag($label, 1).'"';
- $linkclose .= ' class="'.$classfortooltip.'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
+ $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
}
$linkstart = ' trans('Loading');
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
-
$ref = $this->ref;
if (empty($ref)) {
$ref = $this->id;
@@ -2846,8 +2846,8 @@ class FactureFournisseur extends CommonInvoice
$label = $langs->trans("ShowSupplierInvoice");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
- $linkclose .= $dataparams.' title="'.dol_escape_htmltag($label, 1).'"';
- $linkclose .= ' class="'.$classfortooltip.'"';
+ $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
+ $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
}
$linkstart = ' getNomUrl(1) : $this->ref).'';
$return .= ' ';
- if (property_exists($this, 'socid')) {
- $return .= ' | '.$this->socid.' ';
+ if (!empty($arraydata['thirdparty'])) {
+ $return .= ''.$arraydata['thirdparty'].' ';
}
- if (property_exists($this, 'date_echeance') && property_exists($this, 'date')) {
- if (!empty($this->date_echeance)) {
- $return .= ''.dol_print_date($this->date_echeance).' ';
- } else {
- $return .= ''.dol_print_date($this->date).' ';
- }
+ if (property_exists($this, 'date')) {
+ $return .= ''.dol_print_date($this->date, 'day').' ';
}
if (property_exists($this, 'total_ht')) {
- $return .= ''.$langs->trans("AmountHT").' : '.price($this->total_ht).' ';
+ $return .= ' '.price($this->total_ht);
+ $return .= ' '.$langs->trans("HT");
+ $return .= ' ';
}
if (method_exists($this, 'getLibStatut')) {
$alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']);
diff --git a/htdocs/fourn/class/fournisseur.product.class.php b/htdocs/fourn/class/fournisseur.product.class.php
index f31d6a38e7e..743d8d7b33c 100644
--- a/htdocs/fourn/class/fournisseur.product.class.php
+++ b/htdocs/fourn/class/fournisseur.product.class.php
@@ -135,6 +135,9 @@ class ProductFournisseur extends Product
public $packaging;
+ const STATUS_OPEN = 1;
+ const STATUS_CANCELED = 0;
+
/**
* Constructor
@@ -685,7 +688,7 @@ class ProductFournisseur extends Product
$sql .= " pfp.rowid as product_fourn_pri_id, pfp.entity, pfp.ref_fourn, pfp.desc_fourn, pfp.fk_product as product_fourn_id, pfp.fk_supplier_price_expression,";
$sql .= " pfp.price, pfp.quantity, pfp.unitprice, pfp.remise_percent, pfp.remise, pfp.tva_tx, pfp.fk_availability, pfp.charges, pfp.info_bits, pfp.delivery_time_days, pfp.supplier_reputation,";
$sql .= " pfp.multicurrency_price, pfp.multicurrency_unitprice, pfp.multicurrency_tx, pfp.fk_multicurrency, pfp.multicurrency_code, pfp.datec, pfp.tms,";
- $sql .= " pfp.barcode, pfp.fk_barcode_type, pfp.packaging";
+ $sql .= " pfp.barcode, pfp.fk_barcode_type, pfp.packaging, pfp.status as pfstatus";
$sql .= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp, ".MAIN_DB_PREFIX."product as p, ".MAIN_DB_PREFIX."societe as s";
$sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")";
$sql .= " AND pfp.fk_soc = s.rowid AND pfp.fk_product = p.rowid";
@@ -742,6 +745,7 @@ class ProductFournisseur extends Product
$prodfourn->fourn_multicurrency_code = $record["multicurrency_code"];
$prodfourn->packaging = $record["packaging"];
+ $prodfourn->status = $record["pfstatus"];
if (isModEnabled('barcode')) {
$prodfourn->supplier_barcode = $record["barcode"];
@@ -1249,7 +1253,7 @@ class ProductFournisseur extends Product
$label .= $this->displayPriceProductFournisseurLog($logPrices);
}
- $url = dol_buildpath('/product/fournisseurs.php', 1).'?id='.$this->id.'&action=add_price&token='.newToken().'&socid='.$this->fourn_id.'&rowid='.$this->product_fourn_price_id;
+ $url = DOL_URL_ROOT.'/product/fournisseurs.php?id='.((int) $this->id).'&action=create_price&token='.newToken().'&socid='.((int) $this->fourn_id).'&rowid='.((int) $this->product_fourn_price_id);
if ($option != 'nolink') {
// Add param to save lastsearch_values or not
@@ -1302,6 +1306,46 @@ class ProductFournisseur extends Product
return $result;
}
+ /**
+ * Return the label of the status
+ *
+ * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
+ * @param int $type Type of product
+ * @return string Label of status
+ */
+ public function getLibStatut($mode = 0, $type = 0) // must be compatible with getLibStatut of inherited Product
+ {
+ return $this->LibStatut($this->status, $mode);
+ }
+
+ // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+ /**
+ * Return the status
+ *
+ * @param int $status Id status
+ * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
+ * @param int $type Type of product
+ * @return string Label of status
+ */
+ public function LibStatut($status, $mode = 0, $type = 0)
+ {
+ // phpcs:enable
+ if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
+ global $langs;
+ //$langs->load("mymodule@mymodule");
+ $this->labelStatus[self::STATUS_OPEN] = $langs->transnoentitiesnoconv('Enabled');
+ $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled');
+ }
+
+ $statusType = 'status4';
+ //if ($status == self::STATUS_VALIDATED) $statusType = 'status1';
+ if ($status == self::STATUS_CANCELED) {
+ $statusType = 'status6';
+ }
+
+ return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
+ }
+
/**
* Private function to log price history
*
diff --git a/htdocs/fourn/class/paiementfourn.class.php b/htdocs/fourn/class/paiementfourn.class.php
index 21dae0e85b2..b587922b71d 100644
--- a/htdocs/fourn/class/paiementfourn.class.php
+++ b/htdocs/fourn/class/paiementfourn.class.php
@@ -7,6 +7,7 @@
* Copyright (C) 2014 Marcos García
* Copyright (C) 2018 Nicolas ZABOURI
* Copyright (C) 2018 Frédéric France
+ * Copyright (C) 2023 Joachim Kueter
*
* 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
@@ -341,7 +342,18 @@ class PaiementFourn extends Paiement
}
}
} else {
- dol_syslog("Remain to pay for invoice ".$facid." not null. We do nothing.");
+ // hook to have an option to automatically close a closable invoice with less payment than the total amount (e.g. agreed cash discount terms)
+ global $hookmanager;
+ $hookmanager->initHooks(array('payment_supplierdao'));
+ $parameters = array('facid' => $facid, 'invoice' => $invoice, 'remaintopay' => $remaintopay);
+ $action = 'CLOSEPAIDSUPPLIERINVOICE';
+ $reshook = $hookmanager->executeHooks('createPayment', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
+ if ($reshook < 0) {
+ $this->error = $hookmanager->error;
+ $error++;
+ } elseif ($reshook == 0) {
+ dol_syslog("Remain to pay for invoice " . $facid . " not null. We do nothing more.");
+ }
}
}
@@ -350,6 +362,7 @@ class PaiementFourn extends Paiement
$newlang = '';
$outputlangs = $langs;
if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
+ $invoice->fetch_thirdparty();
$newlang = $invoice->thirdparty->default_lang;
}
if (!empty($newlang)) {
diff --git a/htdocs/fourn/commande/dispatch.php b/htdocs/fourn/commande/dispatch.php
index f5a983ac5bb..50508f171df 100644
--- a/htdocs/fourn/commande/dispatch.php
+++ b/htdocs/fourn/commande/dispatch.php
@@ -486,6 +486,7 @@ if ($action == 'updateline' && $permissiontoreceive) {
}
}
+
/*
* View
*/
@@ -616,6 +617,10 @@ if ($id > 0 || !empty($ref)) {
print ''.$langs->trans("OrderStatusNotReadyToDispatch").' ';
}
+
+ print ' ';
+
+
if ($object->statut == CommandeFournisseur::STATUS_ORDERSENT
|| $object->statut == CommandeFournisseur::STATUS_RECEIVED_PARTIALLY
|| $object->statut == CommandeFournisseur::STATUS_RECEIVED_COMPLETELY) {
@@ -731,7 +736,7 @@ if ($id > 0 || !empty($ref)) {
print ''.$langs->trans("QtyOrdered").' ';
print ''.$langs->trans("QtyDispatchedShort").' ';
print ' '.$langs->trans("QtyToDispatchShort");
- print ''.$langs->trans("Reset").' ';
+ print ''.img_picto($langs->trans("Reset"), 'eraser', 'class="pictofixedwidth opacitymedium"').$langs->trans("Reset").' ';
print '
';
if (!empty($conf->global->SUPPLIER_ORDER_CAN_UPDATE_BUYINGPRICE_DURING_RECEIPT)) {
@@ -1069,6 +1074,7 @@ if ($id > 0 || !empty($ref)) {
$dispatchBt = empty($conf->reception->enabled) ? $langs->trans("Receive") : $langs->trans("CreateReception");
print '
';
+ print '
id.'">';
print '
array('label'=>"AliasNameShort", 'position'=>47, 'checked'=>0),
- 's.town'=>array('label'=>"Town", 'enabled'=>1, 'position'=>47, 'checked'=>1),
- 's.zip'=>array('label'=>"Zip", 'enabled'=>1, 'position'=>47, 'checked'=>1),
- 'state.nom'=>array('label'=>"StateShort", 'enabled'=>1, 'position'=>48),
- 'country.code_iso'=>array('label'=>"Country", 'enabled'=>1, 'position'=>49),
- 'typent.code'=>array('label'=>"ThirdPartyType", 'enabled'=>$checkedtypetiers, 'position'=>50),
- 'u.login'=>array('label'=>"AuthorRequest", 'enabled'=>1, 'position'=>51),
- 'cf.note_public'=>array('label'=>'NotePublic', 'checked'=>0, 'enabled'=>(!getDolGlobalInt('MAIN_LIST_HIDE_PUBLIC_NOTES')), 'position'=>100),
- 'cf.note_private'=>array('label'=>'NotePrivate', 'checked'=>0, 'enabled'=>(!getDolGlobalInt('MAIN_LIST_HIDE_PRIVATE_NOTES')), 'position'=>110),
+ 'u.login'=>array('label'=>"AuthorRequest", 'enabled'=>1, 'position'=>41),
+ 's.name_alias'=>array('label'=>"AliasNameShort", 'position'=>51, 'checked'=>0),
+ 's.town'=>array('label'=>"Town", 'enabled'=>1, 'position'=>55, 'checked'=>1),
+ 's.zip'=>array('label'=>"Zip", 'enabled'=>1, 'position'=>56, 'checked'=>1),
+ 'state.nom'=>array('label'=>"StateShort", 'enabled'=>1, 'position'=>57),
+ 'country.code_iso'=>array('label'=>"Country", 'enabled'=>1, 'position'=>58),
+ 'typent.code'=>array('label'=>"ThirdPartyType", 'enabled'=>$checkedtypetiers, 'position'=>59),
+ 'cf.total_localtax1'=>array('label'=>$langs->transcountry("AmountLT1", $mysoc->country_code), 'checked'=>0, 'enabled'=>($mysoc->localtax1_assuj == "1"), 'position'=>140),
+ 'cf.total_localtax2'=>array('label'=>$langs->transcountry("AmountLT2", $mysoc->country_code), 'checked'=>0, 'enabled'=>($mysoc->localtax2_assuj == "1"), 'position'=>145),
+ 'cf.note_public'=>array('label'=>'NotePublic', 'checked'=>0, 'enabled'=>(!getDolGlobalInt('MAIN_LIST_HIDE_PUBLIC_NOTES')), 'position'=>750),
+ 'cf.note_private'=>array('label'=>'NotePrivate', 'checked'=>0, 'enabled'=>(!getDolGlobalInt('MAIN_LIST_HIDE_PRIVATE_NOTES')), 'position'=>760),
);
foreach ($object->fields as $key => $val) {
// If $val['visible']==0, then we never show the field
@@ -766,6 +768,7 @@ $sql .= ' s.rowid as socid, s.nom as name, s.name_alias as alias, s.town, s.zip,
$sql .= " typent.code as typent_code,";
$sql .= " state.code_departement as state_code, state.nom as state_name,";
$sql .= " cf.rowid, cf.ref, cf.ref_supplier, cf.fk_statut, cf.billed, cf.total_ht, cf.total_tva, cf.total_ttc, cf.fk_user_author, cf.date_commande as date_commande, cf.date_livraison as date_livraison,cf.date_valid, cf.date_approve,";
+$sql .= ' cf.localtax1 as total_localtax1, cf.localtax2 as total_localtax2,';
$sql .= ' cf.fk_multicurrency, cf.multicurrency_code, cf.multicurrency_tx, cf.multicurrency_total_ht, cf.multicurrency_total_tva, cf.multicurrency_total_ttc,';
$sql .= ' cf.date_creation as date_creation, cf.tms as date_update,';
$sql .= ' cf.note_public, cf.note_private,';
@@ -1774,7 +1777,7 @@ if ($resql) {
$totalarray['nbfield']++;
}
}
- //alias
+ // Alias
if (!empty($arrayfields['s.name_alias']['checked'])) {
print '
';
print $obj->alias;
diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php
index 5408b8f980a..054ae69e3ad 100644
--- a/htdocs/fourn/facture/card.php
+++ b/htdocs/fourn/facture/card.php
@@ -2800,7 +2800,7 @@ if ($action == 'create') {
$head = facturefourn_prepare_head($object);
$titre = $langs->trans('SupplierInvoice');
- print dol_get_fiche_head($head, 'card', $titre, -1, 'supplier_invoice');
+ print dol_get_fiche_head($head, 'card', $titre, -1, 'supplier_invoice', 0, '', '', 0, '', 1);
$formconfirm = '';
diff --git a/htdocs/fourn/facture/list.php b/htdocs/fourn/facture/list.php
index 5302d5de3b2..878cca686e8 100644
--- a/htdocs/fourn/facture/list.php
+++ b/htdocs/fourn/facture/list.php
@@ -1513,7 +1513,7 @@ while ($i < $imaxinloop) {
}
}
- $arraydata = array('alreadypaid' => $paiement);
+ $arraydata = array('alreadypaid' => $paiement, 'thirdparty' => $thirdparty->getNomUrl(1, '', 12));
print $facturestatic->getKanbanView('', $arraydata);
if ($i == ($imaxinloop - 1)) {
print '';
diff --git a/htdocs/fourn/js/lib_dispatch.js.php b/htdocs/fourn/js/lib_dispatch.js.php
index 01af2ee5d91..ff822543ddb 100644
--- a/htdocs/fourn/js/lib_dispatch.js.php
+++ b/htdocs/fourn/js/lib_dispatch.js.php
@@ -93,8 +93,8 @@ function addDispatchLine(index, type, mode)
}
console.log("qtyDispatched="+qtyDispatched+" qtyOrdered="+qtyOrdered);
- if (qtyOrdered <= 1) {
- window.alert("Quantity can't be split");
+ if (qtyDispatched >= qtyOrdered || qtyOrdered <= 1) {
+ window.alert("Remain quantity to dispatch is too low to be split");
} else if (qtyDispatched < qtyOrdered) {
//replace tr suffix nbr
$row.html($row.html().replace(/_0_/g,"_"+nbrTrs+"_"));
diff --git a/htdocs/holiday/card.php b/htdocs/holiday/card.php
index 3e800760606..d8a25d06842 100644
--- a/htdocs/holiday/card.php
+++ b/htdocs/holiday/card.php
@@ -1003,27 +1003,6 @@ if ((empty($id) && empty($ref)) || $action == 'create' || $action == 'add') {
print ' '."\n";
print ' '."\n";
- if (empty($conf->global->HOLIDAY_HIDE_BALANCE)) {
- print dol_get_fiche_head('', '', '', -1);
-
- $out = '';
- $nb_holiday = 0;
- $typeleaves = $object->getTypes(1, 1);
- foreach ($typeleaves as $key => $val) {
- $nb_type = $object->getCPforUser($user->id, $val['rowid']);
- $nb_holiday += $nb_type;
-
- $out .= ' - '.($langs->trans($val['code']) != $val['code'] ? $langs->trans($val['code']) : $val['label']).': '.($nb_type ? price2num($nb_type) : 0).' ';
- //$out .= ' - '.$val['label'].': '.($nb_type ?price2num($nb_type) : 0).' ';
- }
- print $langs->trans('SoldeCPUser', round($nb_holiday, 5)).' ';
- print $out;
-
- print dol_get_fiche_end();
- } elseif (!is_numeric($conf->global->HOLIDAY_HIDE_BALANCE)) {
- print $langs->trans($conf->global->HOLIDAY_HIDE_BALANCE).' ';
- }
-
print dol_get_fiche_head();
//print ''.$langs->trans('DelayToRequestCP',$object->getConfCP('delayForRequest')).' ';
@@ -1033,14 +1012,57 @@ if ((empty($id) && empty($ref)) || $action == 'create' || $action == 'add') {
// User for leave request
print ' ';
- print ''.$langs->trans("User").' ';
- print '';
+ print ' '.$langs->trans("User").' ';
+ print '';
if ($cancreate && !$cancreateall) {
- print img_picto('', 'user').$form->select_dolusers(($fuserid ? $fuserid : $user->id), 'fuserid', 0, '', 0, 'hierarchyme', '', '0,'.$conf->entity, 0, 0, $morefilter, 0, '', 'minwidth200 maxwidth500');
+ print img_picto('', 'user').$form->select_dolusers(($fuserid ? $fuserid : $user->id), 'fuserid', 0, '', 0, 'hierarchyme', '', '0,'.$conf->entity, 0, 0, $morefilter, 0, '', 'minwidth200 maxwidth500 inline-block');
//print ' ';
} else {
- print img_picto('', 'user').$form->select_dolusers($fuserid ? $fuserid : $user->id, 'fuserid', 0, '', 0, '', '', '0,'.$conf->entity, 0, 0, $morefilter, 0, '', 'minwidth200 maxwidth500');
+ print img_picto('', 'user').$form->select_dolusers($fuserid ? $fuserid : $user->id, 'fuserid', 0, '', 0, '', '', '0,'.$conf->entity, 0, 0, $morefilter, 0, '', 'minwidth200 maxwidth500 inline-block');
}
+ print '
';
+
+ if (empty($conf->global->HOLIDAY_HIDE_BALANCE)) {
+ print '';
+
+ $out = '';
+ $nb_holiday = 0;
+ $typeleaves = $object->getTypes(1, 1);
+ foreach ($typeleaves as $key => $val) {
+ $nb_type = $object->getCPforUser(($fuserid ? $fuserid : $user->id), $val['rowid']);
+ $nb_holiday += $nb_type;
+
+ $out .= ' - '.($langs->trans($val['code']) != $val['code'] ? $langs->trans($val['code']) : $val['label']).': '.($nb_type ? price2num($nb_type) : 0).' ';
+ //$out .= ' - '.$val['label'].': '.($nb_type ?price2num($nb_type) : 0).' ';
+ }
+ print ' ';
+
+ $htmltooltip = $langs->trans("Detail").' ';
+ $htmltooltip .= $out;
+
+ print $form->textwithtooltip($langs->trans('SoldeCPUser', round($nb_holiday, 5)).' '.img_picto('', 'help'), $htmltooltip);
+
+ print '
';
+ if (!empty($conf->use_javascript_ajax)) {
+ print '';
+ }
+ } elseif (!is_numeric($conf->global->HOLIDAY_HIDE_BALANCE)) {
+ print '';
+ print $langs->trans($conf->global->HOLIDAY_HIDE_BALANCE);
+ print '
';
+ }
+
print ' ';
print ' ';
diff --git a/htdocs/holiday/class/holiday.class.php b/htdocs/holiday/class/holiday.class.php
index f3c7130e7f8..507d784092a 100644
--- a/htdocs/holiday/class/holiday.class.php
+++ b/htdocs/holiday/class/holiday.class.php
@@ -1363,9 +1363,11 @@ class Holiday extends CommonObject
$dataparams = '';
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
$classfortooltip = 'classforajaxtooltip';
- $dataparams = " data-params='".json_encode($params)."'";
+ $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
+ $label = '';
+ } else {
+ $label = implode($this->getTooltipContentArray($params));
}
- $label = implode($this->getTooltipContentArray($params));
$url = DOL_URL_ROOT.'/holiday/card.php?id='.$this->id;
@@ -1380,7 +1382,9 @@ class Holiday extends CommonObject
$url .= '&save_lastsearch_values=1';
}
//}
- $linkstart = '
';
+ $linkstart = ' ';
$linkend = ' ';
$result .= $linkstart;
diff --git a/htdocs/hrm/index.php b/htdocs/hrm/index.php
index 4fabcaa3840..b8ea9116418 100644
--- a/htdocs/hrm/index.php
+++ b/htdocs/hrm/index.php
@@ -54,6 +54,7 @@ if (isModEnabled('holiday')) {
// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
$hookmanager = new HookManager($db);
+
$hookmanager->initHooks('hrmindex');
// Load translation files required by the page
diff --git a/htdocs/hrm/job_list.php b/htdocs/hrm/job_list.php
index dad366d14f1..3616c696d54 100644
--- a/htdocs/hrm/job_list.php
+++ b/htdocs/hrm/job_list.php
@@ -650,8 +650,8 @@ while ($i < $imaxinloop) {
//if (in_array($key, array('fk_soc', 'fk_user', 'fk_warehouse'))) $cssforfield = 'tdoverflowmax100';
if (!empty($arrayfields['t.'.$key]['checked'])) {
- print '
$key)) {
print ' title="'.dol_escape_htmltag($object->$key).'"';
}
print '>';
diff --git a/htdocs/hrm/position_list.php b/htdocs/hrm/position_list.php
index 4644382416e..3f605a84d4a 100644
--- a/htdocs/hrm/position_list.php
+++ b/htdocs/hrm/position_list.php
@@ -678,8 +678,8 @@ while ($i < $imaxinloop) {
//if (in_array($key, array('fk_soc', 'fk_user', 'fk_warehouse'))) $cssforfield = 'tdoverflowmax100';
if (!empty($arrayfields['t.'.$key]['checked'])) {
- print ' $key)) {
print ' title="'.dol_escape_htmltag($object->$key).'"';
}
print '>';
diff --git a/htdocs/hrm/skill_list.php b/htdocs/hrm/skill_list.php
index 07084815b13..b11af391ae2 100644
--- a/htdocs/hrm/skill_list.php
+++ b/htdocs/hrm/skill_list.php
@@ -673,7 +673,7 @@ while ($i < $imaxinloop) {
if (!empty($arrayfields['t.'.$key]['checked'])) {
print ' $key)) {
print ' title="'.dol_escape_htmltag($object->$key).'"';
}
print '>';
diff --git a/htdocs/imports/import.php b/htdocs/imports/import.php
index d46bfb2605c..963581e1350 100644
--- a/htdocs/imports/import.php
+++ b/htdocs/imports/import.php
@@ -1082,9 +1082,9 @@ if ($step == 4 && $datatoimport) {
$valforsourcefieldnb[$lefti] = $key;
$lefti++;
- if ($lefti > count($fieldstarget)) {
+ /*if ($lefti > count($fieldstarget)) {
break; // Other fields are in the not imported area
- }
+ }*/
}
//var_dump($valforsourcefieldnb);
@@ -1138,9 +1138,9 @@ if ($step == 4 && $datatoimport) {
print '';
foreach ($fieldssource as $code => $line) { // $fieldssource is an array code=column num, line=content on first line for column in source file.
- if ($i == $minpos) {
+ /*if ($i == $minpos) {
break;
- }
+ }*/
print '';
$entity = (!empty($objimport->array_import_entities[0][$code]) ? $objimport->array_import_entities[0][$code] : $objimport->array_import_icon[0]);
diff --git a/htdocs/includes/OAuth/Common/Storage/DoliStorage.php b/htdocs/includes/OAuth/Common/Storage/DoliStorage.php
index e9cfb8d5dcc..61a4e53061a 100644
--- a/htdocs/includes/OAuth/Common/Storage/DoliStorage.php
+++ b/htdocs/includes/OAuth/Common/Storage/DoliStorage.php
@@ -238,8 +238,16 @@ class DoliStorage implements TokenStorageInterface
*/
public function clearAllTokens()
{
- // TODO
- $this->conf->remove($this->key);
+ // TODO Remove token using a loop on each $service
+ /*
+ $servicepluskeyforprovider = $service;
+ if (!empty($this->keyforprovider)) {
+ // We clean the keyforprovider after the - to be sure it is not present
+ $servicepluskeyforprovider = preg_replace('/\-'.preg_quote($this->keyforprovider, '/').'$/', '', $servicepluskeyforprovider);
+ // Now we add the keyforprovider
+ $servicepluskeyforprovider .= '-'.$this->keyforprovider;
+ }
+ */
// allow chaining
return $this;
@@ -334,7 +342,6 @@ class DoliStorage implements TokenStorageInterface
{
// TODO
// get previously saved tokens
- //$states = $this->conf->get($this->stateKey);
if (is_array($this->states) && array_key_exists($service, $this->states)) {
unset($this->states[$service]);
@@ -353,7 +360,6 @@ class DoliStorage implements TokenStorageInterface
public function clearAllAuthorizationStates()
{
// TODO
- //$this->conf->remove($this->stateKey);
// allow chaining
return $this;
diff --git a/htdocs/includes/jquery/js/jquery.js b/htdocs/includes/jquery/js/jquery.js
index fc6c299b73e..7f35c11bdf3 100644
--- a/htdocs/includes/jquery/js/jquery.js
+++ b/htdocs/includes/jquery/js/jquery.js
@@ -1,5 +1,5 @@
/*!
- * jQuery JavaScript Library v3.6.0
+ * jQuery JavaScript Library v3.6.4
* https://jquery.com/
*
* Includes Sizzle.js
@@ -9,7 +9,7 @@
* Released under the MIT license
* https://jquery.org/license
*
- * Date: 2021-03-02T17:08Z
+ * Date: 2023-03-08T15:28Z
*/
( function( global, factory ) {
@@ -23,7 +23,7 @@
// (such as Node.js), expose a factory as module.exports.
// This accentuates the need for the creation of a real `window`.
// e.g. var jQuery = require("jquery")(window);
- // See ticket #14549 for more info.
+ // See ticket trac-14549 for more info.
module.exports = global.document ?
factory( global, true ) :
function( w ) {
@@ -151,7 +151,7 @@ function toType( obj ) {
var
- version = "3.6.0",
+ version = "3.6.4",
// Define a local copy of jQuery
jQuery = function( selector, context ) {
@@ -522,14 +522,14 @@ function isArrayLike( obj ) {
}
var Sizzle =
/*!
- * Sizzle CSS Selector Engine v2.3.6
+ * Sizzle CSS Selector Engine v2.3.10
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://js.foundation/
*
- * Date: 2021-02-16
+ * Date: 2023-02-14
*/
( function( window ) {
var i,
@@ -633,7 +633,7 @@ var i,
whitespace + "+$", "g" ),
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
- rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace +
+ rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace +
"*" ),
rdescend = new RegExp( whitespace + "|>" ),
@@ -850,7 +850,7 @@ function Sizzle( selector, context, results, seed ) {
// as such selectors are not recognized by querySelectorAll.
// Thanks to Andrew Dupont for this technique.
if ( nodeType === 1 &&
- ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) {
+ ( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) {
// Expand context for sibling selectors
newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
@@ -1174,6 +1174,24 @@ setDocument = Sizzle.setDocument = function( node ) {
!el.querySelectorAll( ":scope fieldset div" ).length;
} );
+ // Support: Chrome 105 - 110+, Safari 15.4 - 16.3+
+ // Make sure the the `:has()` argument is parsed unforgivingly.
+ // We include `*` in the test to detect buggy implementations that are
+ // _selectively_ forgiving (specifically when the list includes at least
+ // one valid selector).
+ // Note that we treat complete lack of support for `:has()` as if it were
+ // spec-compliant support, which is fine because use of `:has()` in such
+ // environments will fail in the qSA path and fall back to jQuery traversal
+ // anyway.
+ support.cssHas = assert( function() {
+ try {
+ document.querySelector( ":has(*,:jqfake)" );
+ return false;
+ } catch ( e ) {
+ return true;
+ }
+ } );
+
/* Attributes
---------------------------------------------------------------------- */
@@ -1440,6 +1458,17 @@ setDocument = Sizzle.setDocument = function( node ) {
} );
}
+ if ( !support.cssHas ) {
+
+ // Support: Chrome 105 - 110+, Safari 15.4 - 16.3+
+ // Our regular `try-catch` mechanism fails to detect natively-unsupported
+ // pseudo-classes inside `:has()` (such as `:has(:contains("Foo"))`)
+ // in browsers that parse the `:has()` argument as a forgiving selector list.
+ // https://drafts.csswg.org/selectors/#relational now requires the argument
+ // to be parsed unforgivingly, but browsers have not yet fully adjusted.
+ rbuggyQSA.push( ":has" );
+ }
+
rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) );
rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) );
@@ -1452,7 +1481,14 @@ setDocument = Sizzle.setDocument = function( node ) {
// As in, an element does not contain itself
contains = hasCompare || rnative.test( docElem.contains ) ?
function( a, b ) {
- var adown = a.nodeType === 9 ? a.documentElement : a,
+
+ // Support: IE <9 only
+ // IE doesn't have `contains` on `document` so we need to check for
+ // `documentElement` presence.
+ // We need to fall back to `a` when `documentElement` is missing
+ // as `ownerDocument` of elements within ` ` may have
+ // a null one - a default behavior of all modern browsers.
+ var adown = a.nodeType === 9 && a.documentElement || a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && (
adown.contains ?
@@ -2242,7 +2278,7 @@ Expr = Sizzle.selectors = {
return elem.nodeName.toLowerCase() === "input" &&
elem.type === "text" &&
- // Support: IE<8
+ // Support: IE <10 only
// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
( ( attr = elem.getAttribute( "type" ) ) == null ||
attr.toLowerCase() === "text" );
@@ -2342,7 +2378,7 @@ tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
matched = false;
// Combinators
- if ( ( match = rcombinators.exec( soFar ) ) ) {
+ if ( ( match = rleadingCombinator.exec( soFar ) ) ) {
matched = match.shift();
tokens.push( {
value: matched,
@@ -3129,8 +3165,8 @@ jQuery.fn.extend( {
var rootjQuery,
// A simple way to check for HTML strings
- // Prioritize #id over to avoid XSS via location.hash (#9521)
- // Strict HTML recognition (#11290: must start with <)
+ // Prioritize #id over to avoid XSS via location.hash (trac-9521)
+ // Strict HTML recognition (trac-11290: must start with <)
// Shortcut simple #id case for speed
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
@@ -4087,7 +4123,7 @@ jQuery.extend( {
isReady: false,
// A counter to track how many items to wait for before
- // the ready event fires. See #6781
+ // the ready event fires. See trac-6781
readyWait: 1,
// Handle when the DOM is ready
@@ -4215,7 +4251,7 @@ function fcamelCase( _all, letter ) {
// Convert dashed to camelCase; used by the css and data modules
// Support: IE <=9 - 11, Edge 12 - 15
-// Microsoft forgot to hump their vendor prefix (#9572)
+// Microsoft forgot to hump their vendor prefix (trac-9572)
function camelCase( string ) {
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
}
@@ -4251,7 +4287,7 @@ Data.prototype = {
value = {};
// We can accept data for non-element nodes in modern browsers,
- // but we should not, see #8335.
+ // but we should not, see trac-8335.
// Always return an empty object.
if ( acceptData( owner ) ) {
@@ -4490,7 +4526,7 @@ jQuery.fn.extend( {
while ( i-- ) {
// Support: IE 11 only
- // The attrs elements can be null (#14894)
+ // The attrs elements can be null (trac-14894)
if ( attrs[ i ] ) {
name = attrs[ i ].name;
if ( name.indexOf( "data-" ) === 0 ) {
@@ -4913,9 +4949,9 @@ var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
input = document.createElement( "input" );
// Support: Android 4.0 - 4.3 only
- // Check state lost if the name is set (#11217)
+ // Check state lost if the name is set (trac-11217)
// Support: Windows Web Apps (WWA)
- // `name` and `type` must use .setAttribute for WWA (#14901)
+ // `name` and `type` must use .setAttribute for WWA (trac-14901)
input.setAttribute( "type", "radio" );
input.setAttribute( "checked", "checked" );
input.setAttribute( "name", "t" );
@@ -4939,7 +4975,7 @@ var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
} )();
-// We have to close these tags to support XHTML (#13200)
+// We have to close these tags to support XHTML (trac-13200)
var wrapMap = {
// XHTML parsers do not magically insert elements in the
@@ -4965,7 +5001,7 @@ if ( !support.option ) {
function getAll( context, tag ) {
// Support: IE <=9 - 11 only
- // Use typeof to avoid zero-argument method invocation on host objects (#15151)
+ // Use typeof to avoid zero-argument method invocation on host objects (trac-15151)
var ret;
if ( typeof context.getElementsByTagName !== "undefined" ) {
@@ -5048,7 +5084,7 @@ function buildFragment( elems, context, scripts, selection, ignored ) {
// Remember the top-level container
tmp = fragment.firstChild;
- // Ensure the created nodes are orphaned (#12392)
+ // Ensure the created nodes are orphaned (trac-12392)
tmp.textContent = "";
}
}
@@ -5469,15 +5505,15 @@ jQuery.event = {
for ( ; cur !== this; cur = cur.parentNode || this ) {
- // Don't check non-elements (#13208)
- // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+ // Don't check non-elements (trac-13208)
+ // Don't process clicks on disabled elements (trac-6911, trac-8165, trac-11382, trac-11764)
if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
matchedHandlers = [];
matchedSelectors = {};
for ( i = 0; i < delegateCount; i++ ) {
handleObj = handlers[ i ];
- // Don't conflict with Object.prototype properties (#13203)
+ // Don't conflict with Object.prototype properties (trac-13203)
sel = handleObj.selector + " ";
if ( matchedSelectors[ sel ] === undefined ) {
@@ -5731,7 +5767,7 @@ jQuery.Event = function( src, props ) {
// Create target properties
// Support: Safari <=6 - 7 only
- // Target should not be a text node (#504, #13143)
+ // Target should not be a text node (trac-504, trac-13143)
this.target = ( src.target && src.target.nodeType === 3 ) ?
src.target.parentNode :
src.target;
@@ -5854,10 +5890,10 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp
return true;
},
- // Suppress native focus or blur as it's already being fired
- // in leverageNative.
- _default: function() {
- return true;
+ // Suppress native focus or blur if we're currently inside
+ // a leveraged native-event stack
+ _default: function( event ) {
+ return dataPriv.get( event.target, type );
},
delegateType: delegateType
@@ -5956,7 +5992,8 @@ var
// checked="checked" or checked
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
- rcleanScript = /^\s*\s*$/g;
+
+ rcleanScript = /^\s*\s*$/g;
// Prefer a tbody over its parent table for containing new rows
function manipulationTarget( elem, content ) {
@@ -6070,7 +6107,7 @@ function domManip( collection, args, callback, ignored ) {
// Use the original fragment for the last item
// instead of the first because it can end up
- // being emptied incorrectly in certain situations (#8070).
+ // being emptied incorrectly in certain situations (trac-8070).
for ( ; i < l; i++ ) {
node = fragment;
@@ -6111,6 +6148,12 @@ function domManip( collection, args, callback, ignored ) {
}, doc );
}
} else {
+
+ // Unwrap a CDATA section containing script contents. This shouldn't be
+ // needed as in XML documents they're already not visible when
+ // inspecting element contents and in HTML documents they have no
+ // meaning but we're preserving that logic for backwards compatibility.
+ // This will be removed completely in 4.0. See gh-4904.
DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc );
}
}
@@ -6393,9 +6436,12 @@ jQuery.each( {
} );
var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
+var rcustomProp = /^--/;
+
+
var getStyles = function( elem ) {
- // Support: IE <=11 only, Firefox <=30 (#15098, #14150)
+ // Support: IE <=11 only, Firefox <=30 (trac-15098, trac-14150)
// IE throws on elements created in popups
// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
var view = elem.ownerDocument.defaultView;
@@ -6430,6 +6476,15 @@ var swap = function( elem, options, callback ) {
var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
+var whitespace = "[\\x20\\t\\r\\n\\f]";
+
+
+var rtrimCSS = new RegExp(
+ "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$",
+ "g"
+);
+
+
( function() {
@@ -6495,7 +6550,7 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
}
// Support: IE <=9 - 11 only
- // Style of cloned element affects source element cloned (#8908)
+ // Style of cloned element affects source element cloned (trac-8908)
div.style.backgroundClip = "content-box";
div.cloneNode( true ).style.backgroundClip = "";
support.clearCloneStyle = div.style.backgroundClip === "content-box";
@@ -6575,6 +6630,7 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
function curCSS( elem, name, computed ) {
var width, minWidth, maxWidth, ret,
+ isCustomProp = rcustomProp.test( name ),
// Support: Firefox 51+
// Retrieving style before computed somehow
@@ -6585,11 +6641,42 @@ function curCSS( elem, name, computed ) {
computed = computed || getStyles( elem );
// getPropertyValue is needed for:
- // .css('filter') (IE 9 only, #12537)
- // .css('--customProperty) (#3144)
+ // .css('filter') (IE 9 only, trac-12537)
+ // .css('--customProperty) (gh-3144)
if ( computed ) {
+
+ // Support: IE <=9 - 11+
+ // IE only supports `"float"` in `getPropertyValue`; in computed styles
+ // it's only available as `"cssFloat"`. We no longer modify properties
+ // sent to `.css()` apart from camelCasing, so we need to check both.
+ // Normally, this would create difference in behavior: if
+ // `getPropertyValue` returns an empty string, the value returned
+ // by `.css()` would be `undefined`. This is usually the case for
+ // disconnected elements. However, in IE even disconnected elements
+ // with no styles return `"none"` for `getPropertyValue( "float" )`
ret = computed.getPropertyValue( name ) || computed[ name ];
+ if ( isCustomProp && ret ) {
+
+ // Support: Firefox 105+, Chrome <=105+
+ // Spec requires trimming whitespace for custom properties (gh-4926).
+ // Firefox only trims leading whitespace. Chrome just collapses
+ // both leading & trailing whitespace to a single space.
+ //
+ // Fall back to `undefined` if empty string returned.
+ // This collapses a missing definition with property defined
+ // and set to an empty string but there's no standard API
+ // allowing us to differentiate them without a performance penalty
+ // and returning `undefined` aligns with older jQuery.
+ //
+ // rtrimCSS treats U+000D CARRIAGE RETURN and U+000C FORM FEED
+ // as whitespace while CSS does not, but this is not a problem
+ // because CSS preprocessing replaces them with U+000A LINE FEED
+ // (which *is* CSS whitespace)
+ // https://www.w3.org/TR/css-syntax-3/#input-preprocessing
+ ret = ret.replace( rtrimCSS, "$1" ) || undefined;
+ }
+
if ( ret === "" && !isAttached( elem ) ) {
ret = jQuery.style( elem, name );
}
@@ -6685,7 +6772,6 @@ var
// except "table", "table-cell", or "table-caption"
// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
rdisplayswap = /^(none|table(?!-c[ea]).+)/,
- rcustomProp = /^--/,
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
cssNormalTransform = {
letterSpacing: "0",
@@ -6921,15 +7007,15 @@ jQuery.extend( {
if ( value !== undefined ) {
type = typeof value;
- // Convert "+=" or "-=" to relative numbers (#7345)
+ // Convert "+=" or "-=" to relative numbers (trac-7345)
if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
value = adjustCSS( elem, name, ret );
- // Fixes bug #9237
+ // Fixes bug trac-9237
type = "number";
}
- // Make sure that null and NaN values aren't set (#7116)
+ // Make sure that null and NaN values aren't set (trac-7116)
if ( value == null || value !== value ) {
return;
}
@@ -7553,7 +7639,7 @@ function Animation( elem, properties, options ) {
remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
// Support: Android 2.3 only
- // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
+ // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (trac-12497)
temp = remaining / animation.duration || 0,
percent = 1 - temp,
index = 0,
@@ -7943,7 +8029,6 @@ jQuery.fx.speeds = {
// Based off of the plugin by Clint Helfers, with permission.
-// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
jQuery.fn.delay = function( time, type ) {
time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
type = type || "fx";
@@ -8168,8 +8253,7 @@ jQuery.extend( {
// Support: IE <=9 - 11 only
// elem.tabIndex doesn't always return the
// correct value when it hasn't been explicitly set
- // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
- // Use proper attribute retrieval(#12072)
+ // Use proper attribute retrieval (trac-12072)
var tabindex = jQuery.find.attr( elem, "tabindex" );
if ( tabindex ) {
@@ -8273,8 +8357,7 @@ function classesToArray( value ) {
jQuery.fn.extend( {
addClass: function( value ) {
- var classes, elem, cur, curValue, clazz, j, finalValue,
- i = 0;
+ var classNames, cur, curValue, className, i, finalValue;
if ( isFunction( value ) ) {
return this.each( function( j ) {
@@ -8282,36 +8365,35 @@ jQuery.fn.extend( {
} );
}
- classes = classesToArray( value );
+ classNames = classesToArray( value );
- if ( classes.length ) {
- while ( ( elem = this[ i++ ] ) ) {
- curValue = getClass( elem );
- cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
+ if ( classNames.length ) {
+ return this.each( function() {
+ curValue = getClass( this );
+ cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
if ( cur ) {
- j = 0;
- while ( ( clazz = classes[ j++ ] ) ) {
- if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
- cur += clazz + " ";
+ for ( i = 0; i < classNames.length; i++ ) {
+ className = classNames[ i ];
+ if ( cur.indexOf( " " + className + " " ) < 0 ) {
+ cur += className + " ";
}
}
// Only assign if different to avoid unneeded rendering.
finalValue = stripAndCollapse( cur );
if ( curValue !== finalValue ) {
- elem.setAttribute( "class", finalValue );
+ this.setAttribute( "class", finalValue );
}
}
- }
+ } );
}
return this;
},
removeClass: function( value ) {
- var classes, elem, cur, curValue, clazz, j, finalValue,
- i = 0;
+ var classNames, cur, curValue, className, i, finalValue;
if ( isFunction( value ) ) {
return this.each( function( j ) {
@@ -8323,45 +8405,42 @@ jQuery.fn.extend( {
return this.attr( "class", "" );
}
- classes = classesToArray( value );
+ classNames = classesToArray( value );
- if ( classes.length ) {
- while ( ( elem = this[ i++ ] ) ) {
- curValue = getClass( elem );
+ if ( classNames.length ) {
+ return this.each( function() {
+ curValue = getClass( this );
// This expression is here for better compressibility (see addClass)
- cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
+ cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
if ( cur ) {
- j = 0;
- while ( ( clazz = classes[ j++ ] ) ) {
+ for ( i = 0; i < classNames.length; i++ ) {
+ className = classNames[ i ];
// Remove *all* instances
- while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
- cur = cur.replace( " " + clazz + " ", " " );
+ while ( cur.indexOf( " " + className + " " ) > -1 ) {
+ cur = cur.replace( " " + className + " ", " " );
}
}
// Only assign if different to avoid unneeded rendering.
finalValue = stripAndCollapse( cur );
if ( curValue !== finalValue ) {
- elem.setAttribute( "class", finalValue );
+ this.setAttribute( "class", finalValue );
}
}
- }
+ } );
}
return this;
},
toggleClass: function( value, stateVal ) {
- var type = typeof value,
+ var classNames, className, i, self,
+ type = typeof value,
isValidValue = type === "string" || Array.isArray( value );
- if ( typeof stateVal === "boolean" && isValidValue ) {
- return stateVal ? this.addClass( value ) : this.removeClass( value );
- }
-
if ( isFunction( value ) ) {
return this.each( function( i ) {
jQuery( this ).toggleClass(
@@ -8371,17 +8450,20 @@ jQuery.fn.extend( {
} );
}
- return this.each( function() {
- var className, i, self, classNames;
+ if ( typeof stateVal === "boolean" && isValidValue ) {
+ return stateVal ? this.addClass( value ) : this.removeClass( value );
+ }
+ classNames = classesToArray( value );
+
+ return this.each( function() {
if ( isValidValue ) {
// Toggle individual class names
- i = 0;
self = jQuery( this );
- classNames = classesToArray( value );
- while ( ( className = classNames[ i++ ] ) ) {
+ for ( i = 0; i < classNames.length; i++ ) {
+ className = classNames[ i ];
// Check each className given, space separated list
if ( self.hasClass( className ) ) {
@@ -8515,7 +8597,7 @@ jQuery.extend( {
val :
// Support: IE <=10 - 11 only
- // option.text throws exceptions (#14686, #14858)
+ // option.text throws exceptions (trac-14686, trac-14858)
// Strip and collapse whitespace
// https://html.spec.whatwg.org/#strip-and-collapse-whitespace
stripAndCollapse( jQuery.text( elem ) );
@@ -8542,7 +8624,7 @@ jQuery.extend( {
option = options[ i ];
// Support: IE <=9 only
- // IE8-9 doesn't update selected after form reset (#2551)
+ // IE8-9 doesn't update selected after form reset (trac-2551)
if ( ( option.selected || i === index ) &&
// Don't return options that are disabled or in a disabled optgroup
@@ -8685,8 +8767,8 @@ jQuery.extend( jQuery.event, {
return;
}
- // Determine event propagation path in advance, per W3C events spec (#9951)
- // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ // Determine event propagation path in advance, per W3C events spec (trac-9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (trac-9724)
if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {
bubbleType = special.delegateType || type;
@@ -8738,7 +8820,7 @@ jQuery.extend( jQuery.event, {
acceptData( elem ) ) {
// Call a native DOM method on the target with the same name as the event.
- // Don't do default actions on window, that's where global variables be (#6170)
+ // Don't do default actions on window, that's where global variables be (trac-6170)
if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {
// Don't re-trigger an onFOO event when we call its FOO() method
@@ -9012,7 +9094,7 @@ var
rantiCache = /([?&])_=[^&]*/,
rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
- // #7653, #8125, #8152: local protocol detection
+ // trac-7653, trac-8125, trac-8152: local protocol detection
rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
rnoContent = /^(?:GET|HEAD)$/,
rprotocol = /^\/\//,
@@ -9035,7 +9117,7 @@ var
*/
transports = {},
- // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+ // Avoid comment-prolog char sequence (trac-10098); must appease lint and evade compression
allTypes = "*/".concat( "*" ),
// Anchor tag for parsing the document origin
@@ -9106,7 +9188,7 @@ function inspectPrefiltersOrTransports( structure, options, originalOptions, jqX
// A special extend for ajax options
// that takes "flat" options (not to be deep extended)
-// Fixes #9887
+// Fixes trac-9887
function ajaxExtend( target, src ) {
var key, deep,
flatOptions = jQuery.ajaxSettings.flatOptions || {};
@@ -9517,12 +9599,12 @@ jQuery.extend( {
deferred.promise( jqXHR );
// Add protocol if not provided (prefilters might expect it)
- // Handle falsy url in the settings object (#10093: consistency with old signature)
+ // Handle falsy url in the settings object (trac-10093: consistency with old signature)
// We also use the url parameter if available
s.url = ( ( url || s.url || location.href ) + "" )
.replace( rprotocol, location.protocol + "//" );
- // Alias method option to type as per ticket #12004
+ // Alias method option to type as per ticket trac-12004
s.type = options.method || options.type || s.method || s.type;
// Extract dataTypes list
@@ -9565,7 +9647,7 @@ jQuery.extend( {
}
// We can fire global events as of now if asked to
- // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
+ // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (trac-15118)
fireGlobals = jQuery.event && s.global;
// Watch for a new set of requests
@@ -9594,7 +9676,7 @@ jQuery.extend( {
if ( s.data && ( s.processData || typeof s.data === "string" ) ) {
cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;
- // #9682: remove data so that it's not used in an eventual retry
+ // trac-9682: remove data so that it's not used in an eventual retry
delete s.data;
}
@@ -9867,7 +9949,7 @@ jQuery._evalUrl = function( url, options, doc ) {
return jQuery.ajax( {
url: url,
- // Make this explicit, since user can override this through ajaxSetup (#11264)
+ // Make this explicit, since user can override this through ajaxSetup (trac-11264)
type: "GET",
dataType: "script",
cache: true,
@@ -9976,7 +10058,7 @@ var xhrSuccessStatus = {
0: 200,
// Support: IE <=9 only
- // #1450: sometimes IE returns 1223 when it should be 204
+ // trac-1450: sometimes IE returns 1223 when it should be 204
1223: 204
},
xhrSupported = jQuery.ajaxSettings.xhr();
@@ -10048,7 +10130,7 @@ jQuery.ajaxTransport( function( options ) {
} else {
complete(
- // File: protocol always yields status 0; see #8605, #14207
+ // File: protocol always yields status 0; see trac-8605, trac-14207
xhr.status,
xhr.statusText
);
@@ -10109,7 +10191,7 @@ jQuery.ajaxTransport( function( options ) {
xhr.send( options.hasContent && options.data || null );
} catch ( e ) {
- // #14683: Only rethrow if this hasn't been notified as an error yet
+ // trac-14683: Only rethrow if this hasn't been notified as an error yet
if ( callback ) {
throw e;
}
@@ -10753,7 +10835,9 @@ jQuery.each(
// Support: Android <=4.0 only
// Make sure we trim BOM and NBSP
-var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
+// Require that the "whitespace run" starts from a non-whitespace
+// to avoid O(N^2) behavior when the engine would try matching "\s+$" at each space position.
+var rtrim = /^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g;
// Bind a function to a context, optionally partially applying any
// arguments.
@@ -10820,7 +10904,7 @@ jQuery.isNumeric = function( obj ) {
jQuery.trim = function( text ) {
return text == null ?
"" :
- ( text + "" ).replace( rtrim, "" );
+ ( text + "" ).replace( rtrim, "$1" );
};
@@ -10868,8 +10952,8 @@ jQuery.noConflict = function( deep ) {
};
// Expose jQuery and $ identifiers, even in AMD
-// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
-// and CommonJS for browser emulators (#13566)
+// (trac-7102#comment:10, https://github.com/jquery/jquery/pull/557)
+// and CommonJS for browser emulators (trac-13566)
if ( typeof noGlobal === "undefined" ) {
window.jQuery = window.$ = jQuery;
}
diff --git a/htdocs/includes/jquery/js/jquery.min.js b/htdocs/includes/jquery/js/jquery.min.js
index c4c6022f298..0de648ed3b5 100644
--- a/htdocs/includes/jquery/js/jquery.min.js
+++ b/htdocs/includes/jquery/js/jquery.min.js
@@ -1,2 +1,2 @@
-/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */
-!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML=" ",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML=" ";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML=" ",y.option=!!ce.lastChild;var ge={thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&v(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!y||!y.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ve(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ye(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.cssHas=ce(function(){try{return C.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],y=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML=" ",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||y.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||y.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||y.push(".#.+[+~]"),e.querySelectorAll("\\\f"),y.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML=" ";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),d.cssHas||y.push(":has"),y=y.length&&new RegExp(y.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType&&e.documentElement||e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&v(p,e)?-1:t==C||t.ownerDocument==p&&v(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!y||!y.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),v.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",v.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML=" ",v.option=!!ce.lastChild;var ge={thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var me=/<|?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0
+ * @author Serban Ghita (since 2012)
* @author Nick Ilyin
- * Original author: Victor Stanciu
+ * @author: Victor Stanciu (original author)
*
- * @version 2.8.39
+ * @version 2.8.41
*
* Auto-generated isXXXX() magic methods.
* php -a examples/dump_magic_methods.php
@@ -216,1471 +216,1477 @@
*/
class Mobile_Detect
{
- /**
- * Mobile detection type.
- *
- * @deprecated since version 2.6.9
- */
- const DETECTION_TYPE_MOBILE = 'mobile';
-
- /**
- * Extended detection type.
- *
- * @deprecated since version 2.6.9
- */
- const DETECTION_TYPE_EXTENDED = 'extended';
-
- /**
- * A frequently used regular expression to extract version #s.
- *
- * @deprecated since version 2.6.9
- */
- const VER = '([\w._\+]+)';
-
- /**
- * Top-level device.
- */
- const MOBILE_GRADE_A = 'A';
-
- /**
- * Mid-level device.
- */
- const MOBILE_GRADE_B = 'B';
-
- /**
- * Low-level device.
- */
- const MOBILE_GRADE_C = 'C';
-
- /**
- * Stores the version number of the current release.
- */
- const VERSION = '2.8.39';
-
- /**
- * A type for the version() method indicating a string return value.
- */
- const VERSION_TYPE_STRING = 'text';
-
- /**
- * A type for the version() method indicating a float return value.
- */
- const VERSION_TYPE_FLOAT = 'float';
-
- /**
- * A cache for resolved matches
- * @var array
- */
- protected $cache = array();
-
- /**
- * The User-Agent HTTP header is stored in here.
- * @var string
- */
- protected $userAgent = null;
-
- /**
- * HTTP headers in the PHP-flavor. So HTTP_USER_AGENT and SERVER_SOFTWARE.
- * @var array
- */
- protected $httpHeaders = array();
-
- /**
- * CloudFront headers. E.g. CloudFront-Is-Desktop-Viewer, CloudFront-Is-Mobile-Viewer & CloudFront-Is-Tablet-Viewer.
- * @var array
- */
- protected $cloudfrontHeaders = array();
-
- /**
- * The matching Regex.
- * This is good for debug.
- * @var string
- */
- protected $matchingRegex = null;
-
- /**
- * The matches extracted from the regex expression.
- * This is good for debug.
- *
- * @var string
- */
- protected $matchesArray = null;
-
- /**
- * The detection type, using self::DETECTION_TYPE_MOBILE or self::DETECTION_TYPE_EXTENDED.
- *
- * @deprecated since version 2.6.9
- *
- * @var string
- */
- protected $detectionType = self::DETECTION_TYPE_MOBILE;
-
- /**
- * HTTP headers that trigger the 'isMobile' detection
- * to be true.
- *
- * @var array
- */
- protected static $mobileHeaders = array(
-
- 'HTTP_ACCEPT' => array('matches' => array(
- // Opera Mini; @reference: http://dev.opera.com/articles/view/opera-binary-markup-language/
- 'application/x-obml2d',
- // BlackBerry devices.
- 'application/vnd.rim.html',
- 'text/vnd.wap.wml',
- 'application/vnd.wap.xhtml+xml'
- )),
- 'HTTP_X_WAP_PROFILE' => null,
- 'HTTP_X_WAP_CLIENTID' => null,
- 'HTTP_WAP_CONNECTION' => null,
- 'HTTP_PROFILE' => null,
- // Reported by Opera on Nokia devices (eg. C3).
- 'HTTP_X_OPERAMINI_PHONE_UA' => null,
- 'HTTP_X_NOKIA_GATEWAY_ID' => null,
- 'HTTP_X_ORANGE_ID' => null,
- 'HTTP_X_VODAFONE_3GPDPCONTEXT' => null,
- 'HTTP_X_HUAWEI_USERID' => null,
- // Reported by Windows Smartphones.
- 'HTTP_UA_OS' => null,
- // Reported by Verizon, Vodafone proxy system.
- 'HTTP_X_MOBILE_GATEWAY' => null,
- // Seen this on HTC Sensation. SensationXE_Beats_Z715e.
- 'HTTP_X_ATT_DEVICEID' => null,
- // Seen this on a HTC.
- 'HTTP_UA_CPU' => array('matches' => array('ARM')),
- );
-
- /**
- * List of mobile devices (phones).
- *
- * @var array
- */
- protected static $phoneDevices = array(
- 'iPhone' => '\biPhone\b|\biPod\b', // |\biTunes
- 'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+|\b(BBA100|BBB100|BBD100|BBE100|BBF100|STH100)\b-[0-9]+',
- 'Pixel' => '; \bPixel\b',
- 'HTC' => 'HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200|ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\bEVO\b|T-Mobile G1|Z520m|Android [0-9.]+; Pixel',
- 'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 5X|Nexus 6',
- // @todo: Is 'Dell Streak' a tablet or a phone? ;)
- 'Dell' => 'Dell[;]? (Streak|Aero|Venue|Venue Pro|Flash|Smoke|Mini 3iX)|XCD28|XCD35|\b001DL\b|\b101DL\b|\bGS01\b',
- 'Motorola' => 'Motorola|DROIDX|DROID BIONIC|\bDroid\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\bMoto E\b|XT1068|XT1092|XT1052',
- 'Samsung' => '\bSamsung\b|SM-G950F|SM-G955F|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K|SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558|GT-I9205|SM-G9350|SM-J120F|SM-G920F|SM-G920V|SM-G930F|SM-N910C|SM-A310F|GT-I9190|SM-J500FN|SM-G903F|SM-J330F|SM-G610F|SM-G981B|SM-G892A|SM-A530F|SM-G988N',
- 'LG' => '\bLG\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323|M257)|LM-G710',
- 'Sony' => 'SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533|SOV34|601SO|F8332',
- 'Asus' => 'Asus.*Galaxy|PadFone.*Mobile|ASUS_Z01QD',
- 'Xiaomi' => '^(?!.*\bx11\b).*xiaomi.*$|POCOPHONE F1|\bMI\b 8|\bMi\b 10|Redmi Note 9S|Redmi 5A|Redmi Note 5A Prime|Redmi Note 7 Pro|N2G47H|M2001J2G|M2001J2I|M1805E10A|M2004J11G|M1902F1G|M2002J9G|M2004J19G|M2003J6A1G|M2012K11C',
- 'NokiaLumia' => 'Lumia [0-9]{3,4}',
- // http://www.micromaxinfo.com/mobiles/smartphones
- // Added because the codes might conflict with Acer Tablets.
- 'Micromax' => 'Micromax.*\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\b',
- // @todo Complete the regex.
- 'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino ;
- 'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature', // Just for fun ;)
- // http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA (PANTECH)
- // Most of the VEGA devices are legacy. PANTECH seem to be newer devices based on Android.
- 'Pantech' => 'PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L|IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S|IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995|IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790',
- // http://www.fly-phone.com/devices/smartphones/ ; Included only smartphones.
- 'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250',
- // http://fr.wikomobile.com
- 'Wiko' => 'KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA(?!nna)|LENNY|BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM',
- 'iMobile' => 'i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)',
- // Added simvalley mobile just for fun. They have some interesting devices.
- // http://www.simvalley.fr/telephonie---gps-_22_telephonie-mobile_telephones_.html
- 'SimValley' => '\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\b',
- // Wolfgang - a brand that is sold by Aldi supermarkets.
- // http://www.wolfgangmobile.com/
- 'Wolfgang' => 'AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q',
- 'Alcatel' => 'Alcatel',
- 'Nintendo' => 'Nintendo (3DS|Switch)',
- // http://en.wikipedia.org/wiki/Amoi
- 'Amoi' => 'Amoi',
- // http://en.wikipedia.org/wiki/INQ
- 'INQ' => 'INQ',
- 'OnePlus' => 'ONEPLUS',
- // @Tapatalk is a mobile app; http://support.tapatalk.com/threads/smf-2-0-2-os-and-browser-detection-plugin-and-tapatalk.15565/#post-79039
- 'GenericPhone' => 'Tapatalk|PDA;|SAGEM|\bmmp\b|pocket|\bpsp\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\bwap\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser',
- );
-
- /**
- * List of tablet devices.
- *
- * @var array
- */
- protected static $tabletDevices = array(
- // @todo: check for mobile friendly emails topic.
- 'iPad' => 'iPad|iPad.*Mobile',
- // Removed |^.*Android.*Nexus(?!(?:Mobile).)*$
- // @see #442
- // @todo Merge NexusTablet into GoogleTablet.
- 'NexusTablet' => 'Android.*Nexus[\s]+(7|9|10)',
- // https://en.wikipedia.org/wiki/Pixel_C
- 'GoogleTablet' => 'Android.*Pixel C',
- 'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-T116BU|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y?|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587|SM-P350|SM-P555M|SM-P355M|SM-T113NU|SM-T815Y|SM-T585|SM-T285|SM-T825|SM-W708|SM-T835|SM-T830|SM-T837V|SM-T720|SM-T510|SM-T387V|SM-P610|SM-T290|SM-T515|SM-T590|SM-T595|SM-T725|SM-T817P|SM-P585N0|SM-T395|SM-T295|SM-T865|SM-P610N|SM-P615|SM-T970|SM-T380|SM-T5950|SM-T905|SM-T231|SM-T500|SM-T860|SM-T536', // SCH-P709|SCH-P729|SM-T2558|GT-I9205 - Samsung Mega - treat them like a regular phone.
- // http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
- 'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\b|Android.*Silk/[0-9.]+ like Chrome/[0-9.]+ (?!Mobile)',
- // Only the Surface tablets with Windows RT are considered mobile.
- // http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
- 'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)',
- // http://shopping1.hp.com/is-bin/INTERSHOP.enfinity/WFS/WW-USSMBPublicStore-Site/en_US/-/USD/ViewStandardCatalog-Browse?CatalogCategoryID=JfIQ7EN5lqMAAAEyDcJUDwMT
- 'HPTablet' => 'HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10',
- // Watch out for PadFone, see #132.
- // http://www.asus.com/de/Tablets_Mobile/Memo_Pad_Products/
- 'AsusTablet' => '^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\bK00F\b|\bK00C\b|\bK00E\b|\bK00L\b|TX201LA|ME176C|ME102A|\bM80TA\b|ME372CL|ME560CG|ME372CG|ME302KL| K01A | K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\bME70C\b|ME581C|ME581CL|ME8510C|ME181C|P01Y|PO1MA|P01Z|\bP027\b|\bP024\b|\bP00C\b',
- 'BlackBerryTablet' => 'PlayBook|RIM Tablet',
- 'HTCtablet' => 'HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410',
- 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
- 'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2',
- // http://www.acer.ro/ac/ro/RO/content/drivers
- // http://www.packardbell.co.uk/pb/en/GB/content/download (Packard Bell is part of Acer)
- // http://us.acer.com/ac/en/US/content/group/tablets
- // http://www.acer.de/ac/de/DE/content/models/tablets/
- // Can conflict with Micromax and Motorola phones codes.
- 'AcerTablet' => 'Android.*; \b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\b|W3-810|\bA3-A10\b|\bA3-A11\b|\bA3-A20\b|\bA3-A30|A3-A40',
- // http://eu.computers.toshiba-europe.com/innovation/family/Tablets/1098744/banner_id/tablet_footerlink/
- // http://us.toshiba.com/tablets/tablet-finder
- // http://www.toshiba.co.jp/regza/tablet/
- 'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
- // http://www.nttdocomo.co.jp/english/service/developer/smart_phone/technical_info/spec/index.html
- // http://www.lg.com/us/tablets
- 'LGTablet' => '\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\b',
- 'FujitsuTablet' => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b',
- // Prestigio Tablets http://www.prestigio.com/support
- 'PrestigioTablet' => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002',
- // http://support.lenovo.com/en_GB/downloads/default.page?#
- 'LenovoTablet' => 'Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-850M|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)|TB-X103F|TB-X304X|TB-X304F|TB-X304L|TB-X505F|TB-X505L|TB-X505X|TB-X605F|TB-X605L|TB-8703F|TB-8703X|TB-8703N|TB-8704N|TB-8704F|TB-8704X|TB-8704V|TB-7304F|TB-7304I|TB-7304X|Tab2A7-10F|Tab2A7-20F|TB2-X30L|YT3-X50L|YT3-X50F|YT3-X50M|YT-X705F|YT-X703F|YT-X703L|YT-X705L|YT-X705X|TB2-X30F|TB2-X30L|TB2-X30M|A2107A-F|A2107A-H|TB3-730F|TB3-730M|TB3-730X|TB-7504F|TB-7504X|TB-X704F|TB-X104F|TB3-X70F|TB-X705F|TB-8504F|TB3-X70L|TB3-710F|TB-X704L|TB-J606F|TB-X606F|TB-X306X',
- // http://www.dell.com/support/home/us/en/04/Products/tab_mob/tablets
- 'DellTablet' => 'Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7',
- 'XiaomiTablet' => '21051182G',
- // http://www.yarvik.com/en/matrix/tablets/
- 'YarvikTablet' => 'Android.*\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\b',
- 'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
- 'ArnovaTablet' => '97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2',
- // http://www.intenso.de/kategorie_en.php?kategorie=33
- // @todo: http://www.nbhkdz.com/read/b8e64202f92a2df129126bff.html - investigate
- 'IntensoTablet' => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004',
- // IRU.ru Tablets http://www.iru.ru/catalog/soho/planetable/
- 'IRUTablet' => 'M702pro',
- 'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b',
- // http://www.e-boda.ro/tablete-pc.html
- 'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)',
- // http://www.allview.ro/produse/droseries/lista-tablete-pc/
- 'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)',
- // http://wiki.archosfans.com/index.php?title=Main_Page
- // @note Rewrite the regex format after we add more UAs.
- 'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R|Archos5|\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\b',
- // http://www.ainol.com/plugin.php?identifier=ainol&module=product
- 'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark',
- 'NokiaLumiaTablet' => 'Lumia 2520',
- // @todo: inspect http://esupport.sony.com/US/p/select-system.pl?DIRECTOR=DRIVER
- // Readers http://www.atsuhiro-me.net/ebook/sony-reader/sony-reader-web-browser
- // http://www.sony.jp/support/tablet/
- 'SonyTablet' => 'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP641|SGP612|SOT31|SGP771|SGP611|SGP612|SGP712',
- // http://www.support.philips.com/support/catalog/worldproducts.jsp?userLanguage=en&userCountry=cn&categoryid=3G_LTE_TABLET_SU_CN_CARE&title=3G%20tablets%20/%20LTE%20range&_dyncharset=UTF-8
- 'PhilipsTablet' => '\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\b',
- // db + http://www.cube-tablet.com/buy-products.html
- 'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
- // http://www.cobyusa.com/?p=pcat&pcat_id=3001
- 'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
- // http://www.match.net.cn/products.asp
- 'MIDTablet' => 'M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10',
- // http://www.msi.com/support
- // @todo Research the Windows Tablets.
- 'MSITablet' => 'MSI \b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\b',
- // @todo http://www.kyoceramobile.com/support/drivers/
- // 'KyoceraTablet' => null,
- // @todo http://intexuae.com/index.php/category/mobile-devices/tablets-products/
- // 'IntextTablet' => null,
- // http://pdadb.net/index.php?m=pdalist&list=SMiT (NoName Chinese Tablets)
- // http://www.imp3.net/14/show.php?itemid=20454
- 'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
- // http://www.rock-chips.com/index.php?do=prod&pid=2
- 'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
- // http://www.fly-phone.com/devices/tablets/ ; http://www.fly-phone.com/service/
- 'FlyTablet' => 'IQ310|Fly Vision',
- // http://www.bqreaders.com/gb/tablets-prices-sale.html
- 'bqTablet' => 'Android.*(bq)?.*\b(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris ([E|M]10|M8))\b|Maxwell.*Lite|Maxwell.*Plus',
- // http://www.huaweidevice.com/worldwide/productFamily.do?method=index&directoryId=5011&treeId=3290
- // http://www.huaweidevice.com/worldwide/downloadCenter.do?method=index&directoryId=3372&treeId=0&tb=1&type=software (including legacy tablets)
- 'HuaweiTablet' => 'MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim|M2-A01L|BAH-L09|BAH-W09|AGS-L09|CMR-AL19|KOB2-L09',
- // Nec or Medias Tab
- 'NecTablet' => '\bN-06D|\bN-08D',
- // Pantech Tablets: http://www.pantechusa.com/phones/
- 'PantechTablet' => 'Pantech.*P4100',
- // Broncho Tablets: http://www.broncho.cn/ (hard to find)
- 'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)',
- // http://versusuk.com/support.html
- 'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b',
- // http://www.zync.in/index.php/our-products/tablet-phablets
- 'ZyncTablet' => 'z1000|Z99 2G|z930|z990|z909|Z919|z900', // Removed "z999" because of https://github.com/serbanghita/Mobile-Detect/issues/717
- // http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/
- 'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
- // https://www.nabitablet.com/
- 'NabiTablet' => 'Android.*\bNabi',
- 'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
- // French Danew Tablets http://www.danew.com/produits-tablette.php
- 'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
- // Texet Tablets and Readers http://www.texet.ru/tablet/
- 'TexetTablet' => 'NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE',
- // Avoid detecting 'PLAYSTATION 3' as mobile.
- 'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
- // http://www.trekstor.de/surftabs.html
- 'TrekstorTablet' => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab',
- // http://www.pyleaudio.com/Products.aspx?%2fproducts%2fPersonal-Electronics%2fTablets
- 'PyleAudioTablet' => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b',
- // http://www.advandigital.com/index.php?link=content-product&jns=JP001
- // because of the short codenames we have to include whitespaces to reduce the possible conflicts.
- 'AdvanTablet' => 'Android.* \b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\b ',
- // http://www.danytech.com/category/tablet-pc
- 'DanyTechTablet' => 'Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1',
- // http://www.galapad.net/product.html ; https://github.com/serbanghita/Mobile-Detect/issues/761
- 'GalapadTablet' => 'Android [0-9.]+; [a-z-]+; \bG1\b',
- // http://www.micromaxinfo.com/tablet/funbook
- 'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b',
- // http://www.karbonnmobiles.com/products_tablet.php
- 'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b',
- // http://www.myallfine.com/Products.asp
- 'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide',
- // http://www.proscanvideo.com/products-search.asp?itemClass=TABLET&itemnmbr=
- 'PROSCANTablet' => '\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\b',
- // http://www.yonesnav.com/products/products.php
- 'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026',
- // http://www.cjshowroom.com/eproducts.aspx?classcode=004001001
- // China manufacturer makes tablets for different small brands (eg. http://www.zeepad.net/index.html)
- 'ChangJiaTablet' => 'TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503',
- // http://www.gloryunion.cn/products.asp
- // http://www.allwinnertech.com/en/apply/mobile.html
- // http://www.ptcl.com.pk/pd_content.php?pd_id=284 (EVOTAB)
- // @todo: Softwiner tablets?
- // aka. Cute or Cool tablets. Not sure yet, must research to avoid collisions.
- 'GUTablet' => 'TX-A1301|TX-M9002|Q702|kf026', // A12R|D75A|D77|D79|R83|A95|A106C|R15|A75|A76|D71|D72|R71|R73|R77|D82|R85|D92|A97|D92|R91|A10F|A77F|W71F|A78F|W78F|W81F|A97F|W91F|W97F|R16G|C72|C73E|K72|K73|R96G
- // http://www.pointofview-online.com/showroom.php?shop_mode=product_listing&category_id=118
- 'PointOfViewTablet' => 'TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10',
- // http://www.overmax.pl/pl/katalog-produktow,p8/tablety,c14/
- // @todo: add more tests.
- 'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)|Qualcore 1027',
- // http://hclmetablet.com/India/index.php
- 'HCLTablet' => 'HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync',
- // http://www.edigital.hu/Tablet_es_e-book_olvaso/Tablet-c18385.html
- 'DPSTablet' => 'DPS Dream 9|DPS Dual 7',
- // http://www.visture.com/index.asp
- 'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10',
- // http://www.mijncresta.nl/tablet
- 'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989',
- // MediaTek - http://www.mediatek.com/_en/01_products/02_proSys.php?cata_sn=1&cata1_sn=1&cata2_sn=309
- 'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b',
- // Concorde tab
- 'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan',
- // GoClever Tablets - http://www.goclever.com/uk/products,c1/tablet,c5/
- 'GoCleverTablet' => 'GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042',
- // Modecom Tablets - http://www.modecom.eu/tablets/portal/
- 'ModecomTablet' => 'FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003',
- // Vonino Tablets
- 'VoninoTablet' => '\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\bQ8\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\b',
- // ECS Tablets - http://www.ecs.com.tw/ECSWebSite/Product/Product_Tablet_List.aspx?CategoryID=14&MenuID=107&childid=M_107&LanID=0
- 'ECSTablet' => 'V07OT2|TM105A|S10OT1|TR10CS1',
- // Storex Tablets - http://storex.fr/espace_client/support.html
- // @note: no need to add all the tablet codes since they are guided by the first regex.
- 'StorexTablet' => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab',
- // Generic Vodafone tablets.
- 'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497|VFD 1400',
- // French tablets - Essentiel B http://www.boulanger.fr/tablette_tactile_e-book/tablette_tactile_essentiel_b/cl_68908.htm?multiChoiceToDelete=brand&mc_brand=essentielb
- // Aka: http://www.essentielb.fr/
- 'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2',
- // Ross & Moor - http://ross-moor.ru/
- 'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711',
- // i-mobile http://product.i-mobilephone.com/Mobile_Device
- 'iMobileTablet' => 'i-mobile i-note',
- // http://www.tolino.de/de/vergleichen/
- 'TolinoTablet' => 'tolino tab [0-9.]+|tolino shine',
- // AudioSonic - a Kmart brand
- // http://www.kmart.com.au/webapp/wcs/stores/servlet/Search?langId=-1&storeId=10701&catalogId=10001&categoryId=193001&pageSize=72¤tPage=1&searchCategory=193001%2b4294965664&sortBy=p_MaxPrice%7c1
- 'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b',
- // AMPE Tablets - http://www.ampe.com.my/product-category/tablets/
- // @todo: add them gradually to avoid conflicts.
- 'AMPETablet' => 'Android.* A78 ',
- // Skk Mobile - http://skkmobile.com.ph/product_tablets.php
- 'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)',
- // Tecno Mobile (only tablet) - http://www.tecno-mobile.com/index.php/product?filterby=smart&list_order=all&page=1
- 'TecnoTablet' => 'TECNO P9|TECNO DP8D',
- // JXD (consoles & tablets) - http://jxd.hk/products.asp?selectclassid=009008&clsid=3
- 'JXDTablet' => 'Android.* \b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\b',
- // i-Joy tablets - http://www.i-joy.es/en/cat/products/tablets/
- 'iJoyTablet' => 'Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)',
- // http://www.intracon.eu/tablet
- 'FX2Tablet' => 'FX2 PAD7|FX2 PAD10',
- // http://www.xoro.de/produkte/
- // @note: Might be the same brand with 'Simply tablets'
- 'XoroTablet' => 'KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151',
- // http://www1.viewsonic.com/products/computing/tablets/
- 'ViewsonicTablet' => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a',
- // https://www.verizonwireless.com/tablets/verizon/
- 'VerizonTablet' => 'QTAQZ3|QTAIR7|QTAQTZ3|QTASUN1|QTASUN2|QTAXIA1',
- // http://www.odys.de/web/internet-tablet_en.html
- 'OdysTablet' => 'LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\bXELIO\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10',
- // http://www.captiva-power.de/products.html#tablets-en
- 'CaptivaTablet' => 'CAPTIVA PAD',
- // IconBIT - http://www.iconbit.com/products/tablets/
- 'IconbitTablet' => 'NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S',
- // http://www.teclast.com/topic.php?channelID=70&topicID=140&pid=63
- 'TeclastTablet' => 'T98 4G|\bP80\b|\bX90HD\b|X98 Air|X98 Air 3G|\bX89\b|P80 3G|\bX80h\b|P98 Air|\bX89HD\b|P98 3G|\bP90HD\b|P89 3G|X98 3G|\bP70h\b|P79HD 3G|G18d 3G|\bP79HD\b|\bP89s\b|\bA88\b|\bP10HD\b|\bP19HD\b|G18 3G|\bP78HD\b|\bA78\b|\bP75\b|G17s 3G|G17h 3G|\bP85t\b|\bP90\b|\bP11\b|\bP98t\b|\bP98HD\b|\bG18d\b|\bP85s\b|\bP11HD\b|\bP88s\b|\bA80HD\b|\bA80se\b|\bA10h\b|\bP89\b|\bP78s\b|\bG18\b|\bP85\b|\bA70h\b|\bA70\b|\bG17\b|\bP18\b|\bA80s\b|\bA11s\b|\bP88HD\b|\bA80h\b|\bP76s\b|\bP76h\b|\bP98\b|\bA10HD\b|\bP78\b|\bP88\b|\bA11\b|\bA10t\b|\bP76a\b|\bP76t\b|\bP76e\b|\bP85HD\b|\bP85a\b|\bP86\b|\bP75HD\b|\bP76v\b|\bA12\b|\bP75a\b|\bA15\b|\bP76Ti\b|\bP81HD\b|\bA10\b|\bT760VE\b|\bT720HD\b|\bP76\b|\bP73\b|\bP71\b|\bP72\b|\bT720SE\b|\bC520Ti\b|\bT760\b|\bT720VE\b|T720-3GE|T720-WiFi',
- // Onda - http://www.onda-tablet.com/buy-android-onda.html?dir=desc&limit=all&order=price
- 'OndaTablet' => '\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\b[\s]+|V10 \b4G\b',
- 'JaytechTablet' => 'TPC-PA762',
- 'BlaupunktTablet' => 'Endeavour 800NG|Endeavour 1010',
- // http://www.digma.ru/support/download/
- // @todo: Ebooks also (if requested)
- 'DigmaTablet' => '\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\b',
- // http://www.evolioshop.com/ro/tablete-pc.html
- // http://www.evolio.ro/support/downloads_static.html?cat=2
- // @todo: Research some more
- 'EvolioTablet' => 'ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\bEvotab\b|\bNeura\b',
- // @todo http://www.lavamobiles.com/tablets-data-cards
- 'LavaTablet' => 'QPAD E704|\bIvoryS\b|E-TAB IVORY|\bE-TAB\b',
- // http://www.breezetablet.com/
- 'AocTablet' => 'MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712',
- // http://www.mpmaneurope.com/en/products/internet-tablets-14/android-tablets-14/
- 'MpmanTablet' => 'MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\bMPG7\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010',
- // https://www.celkonmobiles.com/?_a=categoryphones&sid=2
- 'CelkonTablet' => 'CT695|CT888|CT[\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\bCT-1\b',
- // http://www.wolderelectronics.com/productos/manuales-y-guias-rapidas/categoria-2-miTab
- 'WolderTablet' => 'miTab \b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\b',
- 'MediacomTablet' => 'M-MPI10C3G|M-SP10EG|M-SP10EGP|M-SP10HXAH|M-SP7HXAH|M-SP10HXBH|M-SP8HXAH|M-SP8MXA',
- // http://www.mi.com/en
- 'MiTablet' => '\bMI PAD\b|\bHM NOTE 1W\b',
- // http://www.nbru.cn/index.html
- 'NibiruTablet' => 'Nibiru M1|Nibiru Jupiter One',
- // http://navroad.com/products/produkty/tablety/
- // http://navroad.com/products/produkty/tablety/
- 'NexoTablet' => 'NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI',
- // http://leader-online.com/new_site/product-category/tablets/
- // http://www.leader-online.net.au/List/Tablet
- 'LeaderTablet' => 'TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100',
- // http://www.datawind.com/ubislate/
- 'UbislateTablet' => 'UbiSlate[\s]?7C',
- // http://www.pocketbook-int.com/ru/support
- 'PocketBookTablet' => 'Pocketbook',
- // http://www.kocaso.com/product_tablet.html
- 'KocasoTablet' => '\b(TB-1207)\b',
- // http://global.hisense.com/product/asia/tablet/Sero7/201412/t20141215_91832.htm
- 'HisenseTablet' => '\b(F5281|E2371)\b',
- // http://www.tesco.com/direct/hudl/
- 'Hudl' => 'Hudl HT7S3|Hudl 2',
- // http://www.telstra.com.au/home-phone/thub-2/
- 'TelstraTablet' => 'T-Hub2',
- 'GenericTablet' => 'Android.*\b97D\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|rk30sdk|\bEVOTAB\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\bM6pro\b|CT1020W|arc 10HD|\bTP750\b|\bQTAQZ3\b|WVT101|TM1088|KT107'
- );
-
- /**
- * List of mobile Operating Systems.
- *
- * @var array
- */
- protected static $operatingSystems = array(
- 'AndroidOS' => 'Android',
- 'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os',
- 'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino',
- 'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b',
- // @reference: http://en.wikipedia.org/wiki/Windows_Mobile
- 'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Windows Mobile|Windows Phone [0-9.]+|WCE;',
- // @reference: http://en.wikipedia.org/wiki/Windows_Phone
- // http://wifeng.cn/?r=blog&a=view&id=106
- // http://nicksnettravels.builttoroam.com/post/2011/01/10/Bogus-Windows-Phone-7-User-Agent-String.aspx
- // http://msdn.microsoft.com/library/ms537503.aspx
- // https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
- 'WindowsPhoneOS' => 'Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;',
- 'iOS' => '\biPhone.*Mobile|\biPod|\biPad|AppleCoreMedia',
- // https://en.wikipedia.org/wiki/IPadOS
- 'iPadOS' => 'CPU OS 13',
- // @reference https://en.m.wikipedia.org/wiki/Sailfish_OS
- // https://sailfishos.org/
- 'SailfishOS' => 'Sailfish',
- // http://en.wikipedia.org/wiki/MeeGo
- // @todo: research MeeGo in UAs
- 'MeeGoOS' => 'MeeGo',
- // http://en.wikipedia.org/wiki/Maemo
- // @todo: research Maemo in UAs
- 'MaemoOS' => 'Maemo',
- 'JavaOS' => 'J2ME/|\bMIDP\b|\bCLDC\b', // '|Java/' produces bug #135
- 'webOS' => 'webOS|hpwOS',
- 'badaOS' => '\bBada\b',
- 'BREWOS' => 'BREW',
- );
-
- /**
- * List of mobile User Agents.
- *
- * IMPORTANT: This is a list of only mobile browsers.
- * Mobile Detect 2.x supports only mobile browsers,
- * it was never designed to detect all browsers.
- * The change will come in 2017 in the 3.x release for PHP7.
- *
- * @var array
- */
- protected static $browsers = array(
- //'Vivaldi' => 'Vivaldi',
- // @reference: https://developers.google.com/chrome/mobile/docs/user-agent
- 'Chrome' => '\bCrMo\b|CriOS.*Mobile|Android.*Chrome/[.0-9]* Mobile',
- 'Dolfin' => '\bDolfin\b',
- 'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+$|Coast/[0-9.]+',
- 'Skyfire' => 'Skyfire',
- // Added "Edge on iOS" https://github.com/serbanghita/Mobile-Detect/issues/764
- 'Edge' => 'EdgiOS.*Mobile|Mobile Safari/[.0-9]* Edge',
- 'IE' => 'IEMobile|MSIEMobile', // |Trident/[.0-9]+
- 'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS.*Mobile',
- 'Bolt' => 'bolt',
- 'TeaShark' => 'teashark',
- 'Blazer' => 'Blazer',
- // @reference: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/OptimizingforSafarioniPhone/OptimizingforSafarioniPhone.html#//apple_ref/doc/uid/TP40006517-SW3
- // Excluded "Edge on iOS" https://github.com/serbanghita/Mobile-Detect/issues/764
- 'Safari' => 'Version((?!\bEdgiOS\b).)*Mobile.*Safari|Safari.*Mobile|MobileSafari',
- // http://en.wikipedia.org/wiki/Midori_(web_browser)
- //'Midori' => 'midori',
- //'Tizen' => 'Tizen',
- 'WeChat' => '\bMicroMessenger\b',
- 'UCBrowser' => 'UC.*Browser|UCWEB',
- 'baiduboxapp' => 'baiduboxapp',
- 'baidubrowser' => 'baidubrowser',
- // https://github.com/serbanghita/Mobile-Detect/issues/7
- 'DiigoBrowser' => 'DiigoBrowser',
- // http://www.puffinbrowser.com/index.php
- // https://github.com/serbanghita/Mobile-Detect/issues/752
- // 'Puffin' => 'Puffin',
- // http://mercury-browser.com/index.html
- 'Mercury' => '\bMercury\b',
- // http://en.wikipedia.org/wiki/Obigo_Browser
- 'ObigoBrowser' => 'Obigo',
- // http://en.wikipedia.org/wiki/NetFront
- 'NetFront' => 'NF-Browser',
- // @reference: http://en.wikipedia.org/wiki/Minimo
- // http://en.wikipedia.org/wiki/Vision_Mobile_Browser
- 'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger',
- // @reference: https://en.wikipedia.org/wiki/Pale_Moon_(web_browser)
- 'PaleMoon' => 'Android.*PaleMoon|Mobile.*PaleMoon',
- );
-
- /**
- * Utilities.
- *
- * @var array
- */
- protected static $utilities = array(
- // Experimental. When a mobile device wants to switch to 'Desktop Mode'.
- // http://scottcate.com/technology/windows-phone-8-ie10-desktop-or-mobile/
- // https://github.com/serbanghita/Mobile-Detect/issues/57#issuecomment-15024011
- // https://developers.facebook.com/docs/sharing/webmasters/crawler/
- 'Bot' => 'Googlebot|facebookexternalhit|Google-AMPHTML|s~amp-validator|AdsBot-Google|Google Keyword Suggestion|Facebot|YandexBot|YandexMobileBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|Exabot|MJ12bot|YandexImages|TurnitinBot|Pingdom|contentkingapp|AspiegelBot',
- 'MobileBot' => 'Googlebot-Mobile|AdsBot-Google-Mobile|YahooSeeker/M1A1-R2D2',
- 'DesktopMode' => 'WPDesktop',
- 'TV' => 'SonyDTV|HbbTV', // experimental
- 'WebKit' => '(webkit)[ /]([\w.]+)',
- // @todo: Include JXD consoles.
- 'Console' => '\b(Nintendo|Nintendo WiiU|Nintendo 3DS|Nintendo Switch|PLAYSTATION|Xbox)\b',
- 'Watch' => 'SM-V700',
- );
-
- /**
- * All possible HTTP headers that represent the
- * User-Agent string.
- *
- * @var array
- */
- protected static $uaHttpHeaders = array(
- // The default User-Agent string.
- 'HTTP_USER_AGENT',
- // Header can occur on devices using Opera Mini.
- 'HTTP_X_OPERAMINI_PHONE_UA',
- // Vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/
- 'HTTP_X_DEVICE_USER_AGENT',
- 'HTTP_X_ORIGINAL_USER_AGENT',
- 'HTTP_X_SKYFIRE_PHONE',
- 'HTTP_X_BOLT_PHONE_UA',
- 'HTTP_DEVICE_STOCK_UA',
- 'HTTP_X_UCBROWSER_DEVICE_UA'
- );
-
- /**
- * The individual segments that could exist in a User-Agent string. VER refers to the regular
- * expression defined in the constant self::VER.
- *
- * @var array
- */
- protected static $properties = array(
-
- // Build
- 'Mobile' => 'Mobile/[VER]',
- 'Build' => 'Build/[VER]',
- 'Version' => 'Version/[VER]',
- 'VendorID' => 'VendorID/[VER]',
-
- // Devices
- 'iPad' => 'iPad.*CPU[a-z ]+[VER]',
- 'iPhone' => 'iPhone.*CPU[a-z ]+[VER]',
- 'iPod' => 'iPod.*CPU[a-z ]+[VER]',
- //'BlackBerry' => array('BlackBerry[VER]', 'BlackBerry [VER];'),
- 'Kindle' => 'Kindle/[VER]',
-
- // Browser
- 'Chrome' => array('Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]'),
- 'Coast' => array('Coast/[VER]'),
- 'Dolfin' => 'Dolfin/[VER]',
- // @reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
- 'Firefox' => array('Firefox/[VER]', 'FxiOS/[VER]'),
- 'Fennec' => 'Fennec/[VER]',
- // http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
- // https://msdn.microsoft.com/en-us/library/ie/hh869301(v=vs.85).aspx
- 'Edge' => 'Edge/[VER]',
- 'IE' => array('IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];', 'Trident/[0-9.]+;.*rv:[VER]'),
- // http://en.wikipedia.org/wiki/NetFront
- 'NetFront' => 'NetFront/[VER]',
- 'NokiaBrowser' => 'NokiaBrowser/[VER]',
- 'Opera' => array( ' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]' ),
- 'Opera Mini' => 'Opera Mini/[VER]',
- 'Opera Mobi' => 'Version/[VER]',
- 'UCBrowser' => array( 'UCWEB[VER]', 'UC.*Browser/[VER]' ),
- 'MQQBrowser' => 'MQQBrowser/[VER]',
- 'MicroMessenger' => 'MicroMessenger/[VER]',
- 'baiduboxapp' => 'baiduboxapp/[VER]',
- 'baidubrowser' => 'baidubrowser/[VER]',
- 'SamsungBrowser' => 'SamsungBrowser/[VER]',
- 'Iron' => 'Iron/[VER]',
- // @note: Safari 7534.48.3 is actually Version 5.1.
- // @note: On BlackBerry the Version is overwriten by the OS.
- 'Safari' => array( 'Version/[VER]', 'Safari/[VER]' ),
- 'Skyfire' => 'Skyfire/[VER]',
- 'Tizen' => 'Tizen/[VER]',
- 'Webkit' => 'webkit[ /][VER]',
- 'PaleMoon' => 'PaleMoon/[VER]',
- 'SailfishBrowser' => 'SailfishBrowser/[VER]',
-
- // Engine
- 'Gecko' => 'Gecko/[VER]',
- 'Trident' => 'Trident/[VER]',
- 'Presto' => 'Presto/[VER]',
- 'Goanna' => 'Goanna/[VER]',
-
- // OS
- 'iOS' => ' \bi?OS\b [VER][ ;]{1}',
- 'Android' => 'Android [VER]',
- 'Sailfish' => 'Sailfish [VER]',
- 'BlackBerry' => array('BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'),
- 'BREW' => 'BREW [VER]',
- 'Java' => 'Java/[VER]',
- // @reference: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/08/29/introducing-the-ie9-on-windows-phone-mango-user-agent-string.aspx
- // @reference: http://en.wikipedia.org/wiki/Windows_NT#Releases
- 'Windows Phone OS' => array( 'Windows Phone OS [VER]', 'Windows Phone [VER]'),
- 'Windows Phone' => 'Windows Phone [VER]',
- 'Windows CE' => 'Windows CE/[VER]',
- // http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/6be392da-4d2f-41b4-8354-8dcee20c85cd
- 'Windows NT' => 'Windows NT [VER]',
- 'Symbian' => array('SymbianOS/[VER]', 'Symbian/[VER]'),
- 'webOS' => array('webOS/[VER]', 'hpwOS/[VER];'),
- );
-
- /**
- * Construct an instance of this class.
- *
- * @param array $headers Specify the headers as injection. Should be PHP _SERVER flavored.
- * If left empty, will use the global _SERVER['HTTP_*'] vars instead.
- * @param string $userAgent Inject the User-Agent header. If null, will use HTTP_USER_AGENT
- * from the $headers array instead.
- */
- public function __construct(
- array $headers = null,
- $userAgent = null
- ) {
- $this->setHttpHeaders($headers);
- $this->setUserAgent($userAgent);
- }
-
- /**
- * Get the current script version.
- * This is useful for the demo.php file,
- * so people can check on what version they are testing
- * for mobile devices.
- *
- * @return string The version number in semantic version format.
- */
- public static function getScriptVersion()
- {
- return self::VERSION;
- }
-
- /**
- * Set the HTTP Headers. Must be PHP-flavored. This method will reset existing headers.
- *
- * @param array $httpHeaders The headers to set. If null, then using PHP's _SERVER to extract
- * the headers. The default null is left for backwards compatibility.
- */
- public function setHttpHeaders($httpHeaders = null)
- {
- // use global _SERVER if $httpHeaders aren't defined
- if (!is_array($httpHeaders) || !count($httpHeaders)) {
- $httpHeaders = $_SERVER;
- }
-
- // clear existing headers
- $this->httpHeaders = array();
-
- // Only save HTTP headers. In PHP land, that means only _SERVER vars that
- // start with HTTP_.
- foreach ($httpHeaders as $key => $value) {
- if (substr($key, 0, 5) === 'HTTP_') {
- $this->httpHeaders[$key] = $value;
- }
- }
-
- // In case we're dealing with CloudFront, we need to know.
- $this->setCfHeaders($httpHeaders);
- }
-
- /**
- * Retrieves the HTTP headers.
- *
- * @return array
- */
- public function getHttpHeaders()
- {
- return $this->httpHeaders;
- }
-
- /**
- * Retrieves a particular header. If it doesn't exist, no exception/error is caused.
- * Simply null is returned.
- *
- * @param string $header The name of the header to retrieve. Can be HTTP compliant such as
- * "User-Agent" or "X-Device-User-Agent" or can be php-esque with the
- * all-caps, HTTP_ prefixed, underscore seperated awesomeness.
- *
- * @return string|null The value of the header.
- */
- public function getHttpHeader($header)
- {
- // are we using PHP-flavored headers?
- if (strpos($header, '_') === false) {
- $header = str_replace('-', '_', $header);
- $header = strtoupper($header);
- }
-
- // test the alternate, too
- $altHeader = 'HTTP_' . $header;
-
- //Test both the regular and the HTTP_ prefix
- if (isset($this->httpHeaders[$header])) {
- return $this->httpHeaders[$header];
- } elseif (isset($this->httpHeaders[$altHeader])) {
- return $this->httpHeaders[$altHeader];
- }
-
- return null;
- }
-
- public function getMobileHeaders()
- {
- return self::$mobileHeaders;
- }
-
- /**
- * Get all possible HTTP headers that
- * can contain the User-Agent string.
- *
- * @return array List of HTTP headers.
- */
- public function getUaHttpHeaders()
- {
- return self::$uaHttpHeaders;
- }
-
-
- /**
- * Set CloudFront headers
- * http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-device
- *
- * @param array $cfHeaders List of HTTP headers
- *
- * @return boolean If there were CloudFront headers to be set
- */
- public function setCfHeaders($cfHeaders = null)
- {
- // use global _SERVER if $cfHeaders aren't defined
- if (!is_array($cfHeaders) || !count($cfHeaders)) {
- $cfHeaders = $_SERVER;
- }
-
- // clear existing headers
- $this->cloudfrontHeaders = array();
-
- // Only save CLOUDFRONT headers. In PHP land, that means only _SERVER vars that
- // start with cloudfront-.
- $response = false;
- foreach ($cfHeaders as $key => $value) {
- if (substr(strtolower($key), 0, 16) === 'http_cloudfront_') {
- $this->cloudfrontHeaders[strtoupper($key)] = $value;
- $response = true;
- }
- }
-
- return $response;
- }
-
- /**
- * Retrieves the cloudfront headers.
- *
- * @return array
- */
- public function getCfHeaders()
- {
- return $this->cloudfrontHeaders;
- }
-
- /**
- * @param string $userAgent
- * @return string
- */
- private function prepareUserAgent($userAgent)
- {
- $userAgent = trim($userAgent);
- $userAgent = substr($userAgent, 0, 500);
- return $userAgent;
- }
-
- /**
- * Set the User-Agent to be used.
- *
- * @param string $userAgent The user agent string to set.
- *
- * @return string|null
- */
- public function setUserAgent($userAgent = null)
- {
- // Invalidate cache due to #375
- $this->cache = array();
-
- if (false === empty($userAgent)) {
- return $this->userAgent = $this->prepareUserAgent($userAgent);
- } else {
- $this->userAgent = null;
- foreach ($this->getUaHttpHeaders() as $altHeader) {
- if (false === empty($this->httpHeaders[$altHeader])) { // @todo: should use getHttpHeader(), but it would be slow. (Serban)
- $this->userAgent .= $this->httpHeaders[$altHeader] . " ";
- }
- }
-
- if (!empty($this->userAgent)) {
- return $this->userAgent = $this->prepareUserAgent($this->userAgent);
- }
- }
-
- if (count($this->getCfHeaders()) > 0) {
- return $this->userAgent = 'Amazon CloudFront';
- }
- return $this->userAgent = null;
- }
-
- /**
- * Retrieve the User-Agent.
- *
- * @return string|null The user agent if it's set.
- */
- public function getUserAgent()
- {
- return $this->userAgent;
- }
-
- /**
- * Set the detection type. Must be one of self::DETECTION_TYPE_MOBILE or
- * self::DETECTION_TYPE_EXTENDED. Otherwise, nothing is set.
- *
- * @deprecated since version 2.6.9
- *
- * @param string $type The type. Must be a self::DETECTION_TYPE_* constant. The default
- * parameter is null which will default to self::DETECTION_TYPE_MOBILE.
- */
- public function setDetectionType($type = null)
- {
- if ($type === null) {
- $type = self::DETECTION_TYPE_MOBILE;
- }
-
- if ($type !== self::DETECTION_TYPE_MOBILE && $type !== self::DETECTION_TYPE_EXTENDED) {
- return;
- }
-
- $this->detectionType = $type;
- }
-
- public function getMatchingRegex()
- {
- return $this->matchingRegex;
- }
-
- public function getMatchesArray()
- {
- return $this->matchesArray;
- }
-
- /**
- * Retrieve the list of known phone devices.
- *
- * @return array List of phone devices.
- */
- public static function getPhoneDevices()
- {
- return self::$phoneDevices;
- }
-
- /**
- * Retrieve the list of known tablet devices.
- *
- * @return array List of tablet devices.
- */
- public static function getTabletDevices()
- {
- return self::$tabletDevices;
- }
-
- /**
- * Alias for getBrowsers() method.
- *
- * @return array List of user agents.
- */
- public static function getUserAgents()
- {
- return self::getBrowsers();
- }
-
- /**
- * Retrieve the list of known browsers. Specifically, the user agents.
- *
- * @return array List of browsers / user agents.
- */
- public static function getBrowsers()
- {
- return self::$browsers;
- }
-
- /**
- * Retrieve the list of known utilities.
- *
- * @return array List of utilities.
- */
- public static function getUtilities()
- {
- return self::$utilities;
- }
-
- /**
- * Method gets the mobile detection rules. This method is used for the magic methods $detect->is*().
- *
- * @deprecated since version 2.6.9
- *
- * @return array All the rules (but not extended).
- */
- public static function getMobileDetectionRules()
- {
- static $rules;
-
- if (!$rules) {
- $rules = array_merge(
- self::$phoneDevices,
- self::$tabletDevices,
- self::$operatingSystems,
- self::$browsers
- );
- }
-
- return $rules;
- }
-
- /**
- * Method gets the mobile detection rules + utilities.
- * The reason this is separate is because utilities rules
- * don't necessary imply mobile. This method is used inside
- * the new $detect->is('stuff') method.
- *
- * @deprecated since version 2.6.9
- *
- * @return array All the rules + extended.
- */
- public function getMobileDetectionRulesExtended()
- {
- static $rules;
-
- if (!$rules) {
- // Merge all rules together.
- $rules = array_merge(
- self::$phoneDevices,
- self::$tabletDevices,
- self::$operatingSystems,
- self::$browsers,
- self::$utilities
- );
- }
-
- return $rules;
- }
-
- /**
- * Retrieve the current set of rules.
- *
- * @deprecated since version 2.6.9
- *
- * @return array
- */
- public function getRules()
- {
- if ($this->detectionType == self::DETECTION_TYPE_EXTENDED) {
- return self::getMobileDetectionRulesExtended();
- } else {
- return self::getMobileDetectionRules();
- }
- }
-
- /**
- * Retrieve the list of mobile operating systems.
- *
- * @return array The list of mobile operating systems.
- */
- public static function getOperatingSystems()
- {
- return self::$operatingSystems;
- }
-
- /**
- * Check the HTTP headers for signs of mobile.
- * This is the fastest mobile check possible; it's used
- * inside isMobile() method.
- *
- * @return bool
- */
- public function checkHttpHeadersForMobile()
- {
-
- foreach ($this->getMobileHeaders() as $mobileHeader => $matchType) {
- if (isset($this->httpHeaders[$mobileHeader])) {
- if (isset($matchType['matches']) && is_array($matchType['matches'])) {
- foreach ($matchType['matches'] as $_match) {
- if (strpos($this->httpHeaders[$mobileHeader], $_match) !== false) {
- return true;
- }
- }
-
- return false;
- } else {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Magic overloading method.
- *
- * @method boolean is[...]()
- * @param string $name
- * @param array $arguments
- * @return mixed
- * @throws BadMethodCallException when the method doesn't exist and doesn't start with 'is'
- */
- public function __call($name, $arguments)
- {
- // make sure the name starts with 'is', otherwise
- if (substr($name, 0, 2) !== 'is') {
- throw new BadMethodCallException("No such method exists: $name");
- }
-
- $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
-
- $key = substr($name, 2);
-
- return $this->matchUAAgainstKey($key);
- }
-
- /**
- * Find a detection rule that matches the current User-agent.
- *
- * @param null $userAgent deprecated
- * @return boolean
- */
- protected function matchDetectionRulesAgainstUA($userAgent = null)
- {
- // Begin general search.
- foreach ($this->getRules() as $_regex) {
- if (empty($_regex)) {
- continue;
- }
-
- if ($this->match($_regex, $userAgent)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Search for a certain key in the rules array.
- * If the key is found then try to match the corresponding
- * regex against the User-Agent.
- *
- * @param string $key
- *
- * @return boolean
- */
- protected function matchUAAgainstKey($key)
- {
- // Make the keys lowercase so we can match: isIphone(), isiPhone(), isiphone(), etc.
- $key = strtolower($key);
- if (false === isset($this->cache[$key])) {
- // change the keys to lower case
- $_rules = array_change_key_case($this->getRules());
-
- if (false === empty($_rules[$key])) {
- $this->cache[$key] = $this->match($_rules[$key]);
- }
-
- if (false === isset($this->cache[$key])) {
- $this->cache[$key] = false;
- }
- }
-
- return $this->cache[$key];
- }
-
- /**
- * Check if the device is mobile.
- * Returns true if any type of mobile device detected, including special ones
- * @param null $userAgent deprecated
- * @param null $httpHeaders deprecated
- * @return bool
- */
- public function isMobile($userAgent = null, $httpHeaders = null)
- {
-
- if ($httpHeaders) {
- $this->setHttpHeaders($httpHeaders);
- }
-
- if ($userAgent) {
- $this->setUserAgent($userAgent);
- }
-
- // Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
- if ($this->getUserAgent() === 'Amazon CloudFront') {
- $cfHeaders = $this->getCfHeaders();
- if (array_key_exists('HTTP_CLOUDFRONT_IS_MOBILE_VIEWER', $cfHeaders) && $cfHeaders['HTTP_CLOUDFRONT_IS_MOBILE_VIEWER'] === 'true') {
- return true;
- }
- }
-
- $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
-
- if ($this->checkHttpHeadersForMobile()) {
- return true;
- } else {
- return $this->matchDetectionRulesAgainstUA();
- }
- }
-
- /**
- * Check if the device is a tablet.
- * Return true if any type of tablet device is detected.
- *
- * @param string $userAgent deprecated
- * @param array $httpHeaders deprecated
- * @return bool
- */
- public function isTablet($userAgent = null, $httpHeaders = null)
- {
- // Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
- if ($this->getUserAgent() === 'Amazon CloudFront') {
- $cfHeaders = $this->getCfHeaders();
- if (array_key_exists('HTTP_CLOUDFRONT_IS_TABLET_VIEWER', $cfHeaders) && $cfHeaders['HTTP_CLOUDFRONT_IS_TABLET_VIEWER'] === 'true') {
- return true;
- }
- }
-
- $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
-
- foreach (self::$tabletDevices as $_regex) {
- if ($this->match($_regex, $userAgent)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * This method checks for a certain property in the
- * userAgent.
- * @todo: The httpHeaders part is not yet used.
- *
- * @param string $key
- * @param string $userAgent deprecated
- * @param string $httpHeaders deprecated
- * @return bool|int|null
- */
- public function is($key, $userAgent = null, $httpHeaders = null)
- {
- // Set the UA and HTTP headers only if needed (eg. batch mode).
- if ($httpHeaders) {
- $this->setHttpHeaders($httpHeaders);
- }
-
- if ($userAgent) {
- $this->setUserAgent($userAgent);
- }
-
- $this->setDetectionType(self::DETECTION_TYPE_EXTENDED);
-
- return $this->matchUAAgainstKey($key);
- }
-
- /**
- * Some detection rules are relative (not standard),
- * because of the diversity of devices, vendors and
- * their conventions in representing the User-Agent or
- * the HTTP headers.
- *
- * This method will be used to check custom regexes against
- * the User-Agent string.
- *
- * @param $regex
- * @param string $userAgent
- * @return bool
- *
- * @todo: search in the HTTP headers too.
- */
- public function match($regex, $userAgent = null)
- {
- if (!\is_string($userAgent) && !\is_string($this->userAgent)) {
- return false;
- }
-
- $match = (bool) preg_match(sprintf('#%s#is', $regex), (false === empty($userAgent) ? $userAgent : $this->userAgent), $matches);
- // If positive match is found, store the results for debug.
- if ($match) {
- $this->matchingRegex = $regex;
- $this->matchesArray = $matches;
- }
-
- return $match;
- }
-
- /**
- * Get the properties array.
- *
- * @return array
- */
- public static function getProperties()
- {
- return self::$properties;
- }
-
- /**
- * Prepare the version number.
- *
- * @todo Remove the error supression from str_replace() call.
- *
- * @param string $ver The string version, like "2.6.21.2152";
- *
- * @return float
- */
- public function prepareVersionNo($ver)
- {
- $ver = str_replace(array('_', ' ', '/'), '.', $ver);
- $arrVer = explode('.', $ver, 2);
-
- if (isset($arrVer[1])) {
- $arrVer[1] = @str_replace('.', '', $arrVer[1]); // @todo: treat strings versions.
- }
-
- return (float) implode('.', $arrVer);
- }
-
- /**
- * Check the version of the given property in the User-Agent.
- * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
- *
- * @param string $propertyName The name of the property. See self::getProperties() array
- * keys for all possible properties.
- * @param string $type Either self::VERSION_TYPE_STRING to get a string value or
- * self::VERSION_TYPE_FLOAT indicating a float value. This parameter
- * is optional and defaults to self::VERSION_TYPE_STRING. Passing an
- * invalid parameter will default to the this type as well.
- *
- * @return string|float The version of the property we are trying to extract.
- */
- public function version($propertyName, $type = self::VERSION_TYPE_STRING)
- {
- if (empty($propertyName)) {
- return false;
- }
-
- if (!\is_string($this->userAgent)) {
- return false;
- }
-
- // set the $type to the default if we don't recognize the type
- if ($type !== self::VERSION_TYPE_STRING && $type !== self::VERSION_TYPE_FLOAT) {
- $type = self::VERSION_TYPE_STRING;
- }
-
- $properties = self::getProperties();
-
- // Check if the property exists in the properties array.
- if (true === isset($properties[$propertyName])) {
- // Prepare the pattern to be matched.
- // Make sure we always deal with an array (string is converted).
- $properties[$propertyName] = (array) $properties[$propertyName];
-
- foreach ($properties[$propertyName] as $propertyMatchString) {
- $propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);
-
- // Identify and extract the version.
- preg_match(sprintf('#%s#is', $propertyPattern), $this->userAgent, $match);
-
- if (false === empty($match[1])) {
- $version = ($type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1]);
-
- return $version;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Retrieve the mobile grading, using self::MOBILE_GRADE_* constants.
- * @deprecated This is no longer being maintained, it was an experiment at the time.
- * @return string One of the self::MOBILE_GRADE_* constants.
- */
- public function mobileGrade()
- {
- $isMobile = $this->isMobile();
-
- if (
- // Apple iOS 4-7.0 – Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3 / 5.1 / 6.1), iPad 3 (5.1 / 6.0), iPad Mini (6.1), iPad Retina (7.0), iPhone 3GS (4.3), iPhone 4 (4.3 / 5.1), iPhone 4S (5.1 / 6.0), iPhone 5 (6.0), and iPhone 5S (7.0)
- $this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT) >= 4.3 ||
- $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT) >= 4.3 ||
- $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT) >= 4.3 ||
-
- // Android 2.1-2.3 - Tested on the HTC Incredible (2.2), original Droid (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5)
- // Android 3.1 (Honeycomb) - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM
- // Android 4.0 (ICS) - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices
- // Android 4.1 (Jelly Bean) - Tested on a Galaxy Nexus and Galaxy 7
- ( $this->version('Android', self::VERSION_TYPE_FLOAT)>2.1 && $this->is('Webkit') ) ||
-
- // Windows Phone 7.5-8 - Tested on the HTC Surround (7.5), HTC Trophy (7.5), LG-E900 (7.5), Nokia 800 (7.8), HTC Mazaa (7.8), Nokia Lumia 520 (8), Nokia Lumia 920 (8), HTC 8x (8)
- $this->version('Windows Phone OS', self::VERSION_TYPE_FLOAT) >= 7.5 ||
-
- // Tested on the Torch 9800 (6) and Style 9670 (6), BlackBerry® Torch 9810 (7), BlackBerry Z10 (10)
- $this->is('BlackBerry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) >= 6.0 ||
- // Blackberry Playbook (1.0-2.0) - Tested on PlayBook
- $this->match('Playbook.*Tablet') ||
-
- // Palm WebOS (1.4-3.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0), HP TouchPad (3.0)
- ( $this->version('webOS', self::VERSION_TYPE_FLOAT) >= 1.4 && $this->match('Palm|Pre|Pixi') ) ||
- // Palm WebOS 3.0 - Tested on HP TouchPad
- $this->match('hp.*TouchPad') ||
-
- // Firefox Mobile 18 - Tested on Android 2.3 and 4.1 devices
- ( $this->is('Firefox') && $this->version('Firefox', self::VERSION_TYPE_FLOAT) >= 18 ) ||
-
- // Chrome for Android - Tested on Android 4.0, 4.1 device
- ( $this->is('Chrome') && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 4.0 ) ||
-
- // Skyfire 4.1 - Tested on Android 2.3 device
- ( $this->is('Skyfire') && $this->version('Skyfire', self::VERSION_TYPE_FLOAT) >= 4.1 && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 ) ||
-
- // Opera Mobile 11.5-12: Tested on Android 2.3
- ( $this->is('Opera') && $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT) >= 11.5 && $this->is('AndroidOS') ) ||
-
- // Meego 1.2 - Tested on Nokia 950 and N9
- $this->is('MeeGoOS') ||
-
- // Sailfish OS
- $this->is('SailfishOS') ||
-
- // Tizen (pre-release) - Tested on early hardware
- $this->is('Tizen') ||
-
- // Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser
- // @todo: more tests here!
- $this->is('Dolfin') && $this->version('Bada', self::VERSION_TYPE_FLOAT) >= 2.0 ||
-
- // UC Browser - Tested on Android 2.3 device
- ( ($this->is('UC Browser') || $this->is('Dolfin')) && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 ) ||
-
- // Kindle 3 and Fire - Tested on the built-in WebKit browser for each
- ( $this->match('Kindle Fire') ||
- $this->is('Kindle') && $this->version('Kindle', self::VERSION_TYPE_FLOAT) >= 3.0 ) ||
-
- // Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet
- $this->is('AndroidOS') && $this->is('NookTablet') ||
-
- // Chrome Desktop 16-24 - Tested on OS X 10.7 and Windows 7
- $this->version('Chrome', self::VERSION_TYPE_FLOAT) >= 16 && !$isMobile ||
-
- // Safari Desktop 5-6 - Tested on OS X 10.7 and Windows 7
- $this->version('Safari', self::VERSION_TYPE_FLOAT) >= 5.0 && !$isMobile ||
+ /**
+ * Mobile detection type.
+ *
+ * @deprecated since version 2.6.9
+ */
+ const DETECTION_TYPE_MOBILE = 'mobile';
+
+ /**
+ * Extended detection type.
+ *
+ * @deprecated since version 2.6.9
+ */
+ const DETECTION_TYPE_EXTENDED = 'extended';
+
+ /**
+ * A frequently used regular expression to extract version #s.
+ *
+ * @deprecated since version 2.6.9
+ */
+ const VER = '([\w._\+]+)';
+
+ /**
+ * Top-level device.
+ */
+ const MOBILE_GRADE_A = 'A';
+
+ /**
+ * Mid-level device.
+ */
+ const MOBILE_GRADE_B = 'B';
+
+ /**
+ * Low-level device.
+ */
+ const MOBILE_GRADE_C = 'C';
+
+ /**
+ * Stores the version number of the current release.
+ */
+ const VERSION = '2.8.41';
+
+ /**
+ * A type for the version() method indicating a string return value.
+ */
+ const VERSION_TYPE_STRING = 'text';
+
+ /**
+ * A type for the version() method indicating a float return value.
+ */
+ const VERSION_TYPE_FLOAT = 'float';
+
+ /**
+ * A cache for resolved matches
+ * @var array
+ */
+ protected $cache = array();
+
+ /**
+ * The User-Agent HTTP header is stored in here.
+ * @var string
+ */
+ protected $userAgent = null;
+
+ /**
+ * HTTP headers in the PHP-flavor. So HTTP_USER_AGENT and SERVER_SOFTWARE.
+ * @var array
+ */
+ protected $httpHeaders = array();
+
+ /**
+ * CloudFront headers. E.g. CloudFront-Is-Desktop-Viewer, CloudFront-Is-Mobile-Viewer & CloudFront-Is-Tablet-Viewer.
+ * @var array
+ */
+ protected $cloudfrontHeaders = array();
+
+ /**
+ * The matching Regex.
+ * This is good for debug.
+ * @var string
+ */
+ protected $matchingRegex = null;
+
+ /**
+ * The matches extracted from the regex expression.
+ * This is good for debug.
+ *
+ * @var string
+ */
+ protected $matchesArray = null;
+
+ /**
+ * The detection type, using self::DETECTION_TYPE_MOBILE or self::DETECTION_TYPE_EXTENDED.
+ *
+ * @deprecated since version 2.6.9
+ *
+ * @var string
+ */
+ protected $detectionType = self::DETECTION_TYPE_MOBILE;
+
+ /**
+ * HTTP headers that trigger the 'isMobile' detection
+ * to be true.
+ *
+ * @var array
+ */
+ protected static $mobileHeaders = array(
+
+ 'HTTP_ACCEPT' => array('matches' => array(
+ // Opera Mini; @reference: http://dev.opera.com/articles/view/opera-binary-markup-language/
+ 'application/x-obml2d',
+ // BlackBerry devices.
+ 'application/vnd.rim.html',
+ 'text/vnd.wap.wml',
+ 'application/vnd.wap.xhtml+xml'
+ )),
+ 'HTTP_X_WAP_PROFILE' => null,
+ 'HTTP_X_WAP_CLIENTID' => null,
+ 'HTTP_WAP_CONNECTION' => null,
+ 'HTTP_PROFILE' => null,
+ // Reported by Opera on Nokia devices (eg. C3).
+ 'HTTP_X_OPERAMINI_PHONE_UA' => null,
+ 'HTTP_X_NOKIA_GATEWAY_ID' => null,
+ 'HTTP_X_ORANGE_ID' => null,
+ 'HTTP_X_VODAFONE_3GPDPCONTEXT' => null,
+ 'HTTP_X_HUAWEI_USERID' => null,
+ // Reported by Windows Smartphones.
+ 'HTTP_UA_OS' => null,
+ // Reported by Verizon, Vodafone proxy system.
+ 'HTTP_X_MOBILE_GATEWAY' => null,
+ // Seen this on HTC Sensation. SensationXE_Beats_Z715e.
+ 'HTTP_X_ATT_DEVICEID' => null,
+ // Seen this on a HTC.
+ 'HTTP_UA_CPU' => array('matches' => array('ARM')),
+ );
+
+ /**
+ * List of mobile devices (phones).
+ *
+ * @var array
+ */
+ protected static $phoneDevices = array(
+ 'iPhone' => '\biPhone\b|\biPod\b', // |\biTunes
+ 'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+|\b(BBA100|BBB100|BBD100|BBE100|BBF100|STH100)\b-[0-9]+',
+ 'Pixel' => '; \bPixel\b',
+ 'HTC' => 'HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200|ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\bEVO\b|T-Mobile G1|Z520m|Android [0-9.]+; Pixel',
+ 'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 5X|Nexus 6',
+ // @todo: Is 'Dell Streak' a tablet or a phone? ;)
+ 'Dell' => 'Dell[;]? (Streak|Aero|Venue|Venue Pro|Flash|Smoke|Mini 3iX)|XCD28|XCD35|\b001DL\b|\b101DL\b|\bGS01\b',
+ 'Motorola' => 'Motorola|DROIDX|DROID BIONIC|\bDroid\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\bMoto E\b|XT1068|XT1092|XT1052',
+ 'Samsung' => '\bSamsung\b|SM-G950F|SM-G955F|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K|SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558|GT-I9205|SM-G9350|SM-J120F|SM-G920F|SM-G920V|SM-G930F|SM-N910C|SM-A310F|GT-I9190|SM-J500FN|SM-G903F|SM-J330F|SM-G610F|SM-G981B|SM-G892A|SM-A530F|SM-G988N|SM-G781B|SM-A805N|SM-G965F',
+ 'LG' => '\bLG\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323|M257)|LM-G710',
+ 'Sony' => 'SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533|SOV34|601SO|F8332',
+ 'Asus' => 'Asus.*Galaxy|PadFone.*Mobile|ASUS_Z01QD|ASUS_X00TD',
+ 'Xiaomi' => '^(?!.*\bx11\b).*xiaomi.*$|POCOPHONE F1|\bMI\b 8|\bMi\b 10|Redmi Note 9S|Redmi 5A|Redmi Note 5A Prime|Redmi Note 7 Pro|N2G47H|M2001J2G|M2001J2I|M1805E10A|M2004J11G|M1902F1G|M2002J9G|M2004J19G|M2003J6A1G|M2012K11C|M2007J1SC',
+ 'NokiaLumia' => 'Lumia [0-9]{3,4}',
+ // http://www.micromaxinfo.com/mobiles/smartphones
+ // Added because the codes might conflict with Acer Tablets.
+ 'Micromax' => 'Micromax.*\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\b',
+ // @todo Complete the regex.
+ 'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino ;
+ 'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature', // Just for fun ;)
+ // http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA (PANTECH)
+ // Most of the VEGA devices are legacy. PANTECH seem to be newer devices based on Android.
+ 'Pantech' => 'PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L|IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S|IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995|IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790',
+ // http://www.fly-phone.com/devices/smartphones/ ; Included only smartphones.
+ 'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250',
+ // http://fr.wikomobile.com
+ 'Wiko' => 'KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA(?!nna)|LENNY|BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM',
+ 'iMobile' => 'i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)',
+ // Added simvalley mobile just for fun. They have some interesting devices.
+ // http://www.simvalley.fr/telephonie---gps-_22_telephonie-mobile_telephones_.html
+ 'SimValley' => '\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\b',
+ // Wolfgang - a brand that is sold by Aldi supermarkets.
+ // http://www.wolfgangmobile.com/
+ 'Wolfgang' => 'AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q',
+ 'Alcatel' => 'Alcatel',
+ 'Nintendo' => 'Nintendo (3DS|Switch)',
+ // http://en.wikipedia.org/wiki/Amoi
+ 'Amoi' => 'Amoi',
+ // http://en.wikipedia.org/wiki/INQ
+ 'INQ' => 'INQ',
+ 'OnePlus' => 'ONEPLUS',
+ // @Tapatalk is a mobile app; http://support.tapatalk.com/threads/smf-2-0-2-os-and-browser-detection-plugin-and-tapatalk.15565/#post-79039
+ 'GenericPhone' => 'Tapatalk|PDA;|SAGEM|\bmmp\b|pocket|\bpsp\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\bwap\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser',
+ );
+
+ /**
+ * List of tablet devices.
+ *
+ * @var array
+ */
+ protected static $tabletDevices = array(
+ // @todo: check for mobile friendly emails topic.
+ 'iPad' => 'iPad|iPad.*Mobile',
+ // Removed |^.*Android.*Nexus(?!(?:Mobile).)*$
+ // @see #442
+ // @todo Merge NexusTablet into GoogleTablet.
+ 'NexusTablet' => 'Android.*Nexus[\s]+(7|9|10)',
+ // https://en.wikipedia.org/wiki/Pixel_C
+ 'GoogleTablet' => 'Android.*Pixel C',
+ 'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-T116BU|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y?|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587|SM-P350|SM-P555M|SM-P355M|SM-T113NU|SM-T815Y|SM-T585|SM-T285|SM-T825|SM-W708|SM-T835|SM-T830|SM-T837V|SM-T720|SM-T510|SM-T387V|SM-P610|SM-T290|SM-T515|SM-T590|SM-T595|SM-T725|SM-T817P|SM-P585N0|SM-T395|SM-T295|SM-T865|SM-P610N|SM-P615|SM-T970|SM-T380|SM-T5950|SM-T905|SM-T231|SM-T500|SM-T860|SM-T536|SM-T837A|SM-X200|SM-T220|SM-T870|SM-X906C', // SCH-P709|SCH-P729|SM-T2558|GT-I9205 - Samsung Mega - treat them like a regular phone.
+ // http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
+ 'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\b|Android.*Silk/[0-9.]+ like Chrome/[0-9.]+ (?!Mobile)',
+ // Only the Surface tablets with Windows RT are considered mobile.
+ // http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
+ 'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)',
+ // http://shopping1.hp.com/is-bin/INTERSHOP.enfinity/WFS/WW-USSMBPublicStore-Site/en_US/-/USD/ViewStandardCatalog-Browse?CatalogCategoryID=JfIQ7EN5lqMAAAEyDcJUDwMT
+ 'HPTablet' => 'HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10',
+ // Watch out for PadFone, see #132.
+ // http://www.asus.com/de/Tablets_Mobile/Memo_Pad_Products/
+ 'AsusTablet' => '^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\bK00F\b|\bK00C\b|\bK00E\b|\bK00L\b|TX201LA|ME176C|ME102A|\bM80TA\b|ME372CL|ME560CG|ME372CG|ME302KL| K01A | K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\bME70C\b|ME581C|ME581CL|ME8510C|ME181C|P01Y|PO1MA|P01Z|\bP027\b|\bP024\b|\bP00C\b',
+ 'BlackBerryTablet' => 'PlayBook|RIM Tablet',
+ 'HTCtablet' => 'HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410',
+ 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
+ 'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2',
+ // http://www.acer.ro/ac/ro/RO/content/drivers
+ // http://www.packardbell.co.uk/pb/en/GB/content/download (Packard Bell is part of Acer)
+ // http://us.acer.com/ac/en/US/content/group/tablets
+ // http://www.acer.de/ac/de/DE/content/models/tablets/
+ // Can conflict with Micromax and Motorola phones codes.
+ 'AcerTablet' => 'Android.*; \b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\b|W3-810|\bA3-A10\b|\bA3-A11\b|\bA3-A20\b|\bA3-A30|A3-A40',
+ // http://eu.computers.toshiba-europe.com/innovation/family/Tablets/1098744/banner_id/tablet_footerlink/
+ // http://us.toshiba.com/tablets/tablet-finder
+ // http://www.toshiba.co.jp/regza/tablet/
+ 'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
+ // http://www.nttdocomo.co.jp/english/service/developer/smart_phone/technical_info/spec/index.html
+ // http://www.lg.com/us/tablets
+ 'LGTablet' => '\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\b',
+ 'FujitsuTablet' => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b',
+ // Prestigio Tablets http://www.prestigio.com/support
+ 'PrestigioTablet' => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002',
+ // http://support.lenovo.com/en_GB/downloads/default.page?#
+ 'LenovoTablet' => 'Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-850M|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)|TB-X103F|TB-X304X|TB-X304F|TB-X304L|TB-X505F|TB-X505L|TB-X505X|TB-X605F|TB-X605L|TB-8703F|TB-8703X|TB-8703N|TB-8704N|TB-8704F|TB-8704X|TB-8704V|TB-7304F|TB-7304I|TB-7304X|Tab2A7-10F|Tab2A7-20F|TB2-X30L|YT3-X50L|YT3-X50F|YT3-X50M|YT-X705F|YT-X703F|YT-X703L|YT-X705L|YT-X705X|TB2-X30F|TB2-X30L|TB2-X30M|A2107A-F|A2107A-H|TB3-730F|TB3-730M|TB3-730X|TB-7504F|TB-7504X|TB-X704F|TB-X104F|TB3-X70F|TB-X705F|TB-8504F|TB3-X70L|TB3-710F|TB-X704L|TB-J606F|TB-X606F|TB-X306X|YT-J706X',
+ // http://www.dell.com/support/home/us/en/04/Products/tab_mob/tablets
+ 'DellTablet' => 'Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7',
+ 'XiaomiTablet' => '21051182G',
+ // http://www.yarvik.com/en/matrix/tablets/
+ 'YarvikTablet' => 'Android.*\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\b',
+ 'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
+ 'ArnovaTablet' => '97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2',
+ // http://www.intenso.de/kategorie_en.php?kategorie=33
+ // @todo: http://www.nbhkdz.com/read/b8e64202f92a2df129126bff.html - investigate
+ 'IntensoTablet' => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004',
+ // IRU.ru Tablets http://www.iru.ru/catalog/soho/planetable/
+ 'IRUTablet' => 'M702pro',
+ 'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b',
+ // http://www.e-boda.ro/tablete-pc.html
+ 'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)',
+ // http://www.allview.ro/produse/droseries/lista-tablete-pc/
+ 'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)',
+ // http://wiki.archosfans.com/index.php?title=Main_Page
+ // @note Rewrite the regex format after we add more UAs.
+ 'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R|Archos5|\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\b',
+ // http://www.ainol.com/plugin.php?identifier=ainol&module=product
+ 'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark',
+ 'NokiaLumiaTablet' => 'Lumia 2520',
+ // @todo: inspect http://esupport.sony.com/US/p/select-system.pl?DIRECTOR=DRIVER
+ // Readers http://www.atsuhiro-me.net/ebook/sony-reader/sony-reader-web-browser
+ // http://www.sony.jp/support/tablet/
+ 'SonyTablet' => 'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP641|SGP612|SOT31|SGP771|SGP611|SGP612|SGP712',
+ // http://www.support.philips.com/support/catalog/worldproducts.jsp?userLanguage=en&userCountry=cn&categoryid=3G_LTE_TABLET_SU_CN_CARE&title=3G%20tablets%20/%20LTE%20range&_dyncharset=UTF-8
+ 'PhilipsTablet' => '\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\b',
+ // db + http://www.cube-tablet.com/buy-products.html
+ 'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
+ // http://www.cobyusa.com/?p=pcat&pcat_id=3001
+ 'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
+ // http://www.match.net.cn/products.asp
+ 'MIDTablet' => 'M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10',
+ // http://www.msi.com/support
+ // @todo Research the Windows Tablets.
+ 'MSITablet' => 'MSI \b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\b',
+ // @todo http://www.kyoceramobile.com/support/drivers/
+ // 'KyoceraTablet' => null,
+ // @todo http://intexuae.com/index.php/category/mobile-devices/tablets-products/
+ // 'IntextTablet' => null,
+ // http://pdadb.net/index.php?m=pdalist&list=SMiT (NoName Chinese Tablets)
+ // http://www.imp3.net/14/show.php?itemid=20454
+ 'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
+ // http://www.rock-chips.com/index.php?do=prod&pid=2
+ 'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
+ // http://www.fly-phone.com/devices/tablets/ ; http://www.fly-phone.com/service/
+ 'FlyTablet' => 'IQ310|Fly Vision',
+ // http://www.bqreaders.com/gb/tablets-prices-sale.html
+ 'bqTablet' => 'Android.*(bq)?.*\b(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris ([E|M]10|M8))\b|Maxwell.*Lite|Maxwell.*Plus',
+ // http://www.huaweidevice.com/worldwide/productFamily.do?method=index&directoryId=5011&treeId=3290
+ // http://www.huaweidevice.com/worldwide/downloadCenter.do?method=index&directoryId=3372&treeId=0&tb=1&type=software (including legacy tablets)
+ 'HuaweiTablet' => 'MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim|M2-A01L|BAH-L09|BAH-W09|AGS-L09|CMR-AL19|KOB2-L09|BG2-U01|BG2-W09|BG2-U03',
+ // Nec or Medias Tab
+ 'NecTablet' => '\bN-06D|\bN-08D',
+ // Pantech Tablets: http://www.pantechusa.com/phones/
+ 'PantechTablet' => 'Pantech.*P4100',
+ // Broncho Tablets: http://www.broncho.cn/ (hard to find)
+ 'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)',
+ // http://versusuk.com/support.html
+ 'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b',
+ // http://www.zync.in/index.php/our-products/tablet-phablets
+ 'ZyncTablet' => 'z1000|Z99 2G|z930|z990|z909|Z919|z900', // Removed "z999" because of https://github.com/serbanghita/Mobile-Detect/issues/717
+ // http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/
+ 'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
+ // https://www.nabitablet.com/
+ 'NabiTablet' => 'Android.*\bNabi',
+ 'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
+ // French Danew Tablets http://www.danew.com/produits-tablette.php
+ 'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
+ // Texet Tablets and Readers http://www.texet.ru/tablet/
+ 'TexetTablet' => 'NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE',
+ // Avoid detecting 'PLAYSTATION 3' as mobile.
+ 'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
+ // http://www.trekstor.de/surftabs.html
+ 'TrekstorTablet' => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab',
+ // http://www.pyleaudio.com/Products.aspx?%2fproducts%2fPersonal-Electronics%2fTablets
+ 'PyleAudioTablet' => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b',
+ // http://www.advandigital.com/index.php?link=content-product&jns=JP001
+ // because of the short codenames we have to include whitespaces to reduce the possible conflicts.
+ 'AdvanTablet' => 'Android.* \b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\b ',
+ // http://www.danytech.com/category/tablet-pc
+ 'DanyTechTablet' => 'Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1',
+ // http://www.galapad.net/product.html ; https://github.com/serbanghita/Mobile-Detect/issues/761
+ 'GalapadTablet' => 'Android [0-9.]+; [a-z-]+; \bG1\b',
+ // http://www.micromaxinfo.com/tablet/funbook
+ 'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b',
+ // http://www.karbonnmobiles.com/products_tablet.php
+ 'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b',
+ // http://www.myallfine.com/Products.asp
+ 'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide',
+ // http://www.proscanvideo.com/products-search.asp?itemClass=TABLET&itemnmbr=
+ 'PROSCANTablet' => '\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\b',
+ // http://www.yonesnav.com/products/products.php
+ 'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026',
+ // http://www.cjshowroom.com/eproducts.aspx?classcode=004001001
+ // China manufacturer makes tablets for different small brands (eg. http://www.zeepad.net/index.html)
+ 'ChangJiaTablet' => 'TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503',
+ // http://www.gloryunion.cn/products.asp
+ // http://www.allwinnertech.com/en/apply/mobile.html
+ // http://www.ptcl.com.pk/pd_content.php?pd_id=284 (EVOTAB)
+ // @todo: Softwiner tablets?
+ // aka. Cute or Cool tablets. Not sure yet, must research to avoid collisions.
+ 'GUTablet' => 'TX-A1301|TX-M9002|Q702|kf026', // A12R|D75A|D77|D79|R83|A95|A106C|R15|A75|A76|D71|D72|R71|R73|R77|D82|R85|D92|A97|D92|R91|A10F|A77F|W71F|A78F|W78F|W81F|A97F|W91F|W97F|R16G|C72|C73E|K72|K73|R96G
+ // http://www.pointofview-online.com/showroom.php?shop_mode=product_listing&category_id=118
+ 'PointOfViewTablet' => 'TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10',
+ // http://www.overmax.pl/pl/katalog-produktow,p8/tablety,c14/
+ // @todo: add more tests.
+ 'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)|Qualcore 1027',
+ // http://hclmetablet.com/India/index.php
+ 'HCLTablet' => 'HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync',
+ // http://www.edigital.hu/Tablet_es_e-book_olvaso/Tablet-c18385.html
+ 'DPSTablet' => 'DPS Dream 9|DPS Dual 7',
+ // http://www.visture.com/index.asp
+ 'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10',
+ // http://www.mijncresta.nl/tablet
+ 'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989',
+ // MediaTek - http://www.mediatek.com/_en/01_products/02_proSys.php?cata_sn=1&cata1_sn=1&cata2_sn=309
+ 'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b',
+ // Concorde tab
+ 'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan',
+ // GoClever Tablets - http://www.goclever.com/uk/products,c1/tablet,c5/
+ 'GoCleverTablet' => 'GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042',
+ // Modecom Tablets - http://www.modecom.eu/tablets/portal/
+ 'ModecomTablet' => 'FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003',
+ // Vonino Tablets
+ 'VoninoTablet' => '\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\bQ8\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\b',
+ // ECS Tablets - http://www.ecs.com.tw/ECSWebSite/Product/Product_Tablet_List.aspx?CategoryID=14&MenuID=107&childid=M_107&LanID=0
+ 'ECSTablet' => 'V07OT2|TM105A|S10OT1|TR10CS1',
+ // Storex Tablets - http://storex.fr/espace_client/support.html
+ // @note: no need to add all the tablet codes since they are guided by the first regex.
+ 'StorexTablet' => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab',
+ // Generic Vodafone tablets.
+ 'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497|VFD 1400',
+ // French tablets - Essentiel B http://www.boulanger.fr/tablette_tactile_e-book/tablette_tactile_essentiel_b/cl_68908.htm?multiChoiceToDelete=brand&mc_brand=essentielb
+ // Aka: http://www.essentielb.fr/
+ 'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2',
+ // Ross & Moor - http://ross-moor.ru/
+ 'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711',
+ // i-mobile http://product.i-mobilephone.com/Mobile_Device
+ 'iMobileTablet' => 'i-mobile i-note',
+ // http://www.tolino.de/de/vergleichen/
+ 'TolinoTablet' => 'tolino tab [0-9.]+|tolino shine',
+ // AudioSonic - a Kmart brand
+ // http://www.kmart.com.au/webapp/wcs/stores/servlet/Search?langId=-1&storeId=10701&catalogId=10001&categoryId=193001&pageSize=72¤tPage=1&searchCategory=193001%2b4294965664&sortBy=p_MaxPrice%7c1
+ 'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b',
+ // AMPE Tablets - http://www.ampe.com.my/product-category/tablets/
+ // @todo: add them gradually to avoid conflicts.
+ 'AMPETablet' => 'Android.* A78 ',
+ // Skk Mobile - http://skkmobile.com.ph/product_tablets.php
+ 'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)',
+ // Tecno Mobile (only tablet) - http://www.tecno-mobile.com/index.php/product?filterby=smart&list_order=all&page=1
+ 'TecnoTablet' => 'TECNO P9|TECNO DP8D',
+ // JXD (consoles & tablets) - http://jxd.hk/products.asp?selectclassid=009008&clsid=3
+ 'JXDTablet' => 'Android.* \b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\b',
+ // i-Joy tablets - http://www.i-joy.es/en/cat/products/tablets/
+ 'iJoyTablet' => 'Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)',
+ // http://www.intracon.eu/tablet
+ 'FX2Tablet' => 'FX2 PAD7|FX2 PAD10',
+ // http://www.xoro.de/produkte/
+ // @note: Might be the same brand with 'Simply tablets'
+ 'XoroTablet' => 'KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151',
+ // http://www1.viewsonic.com/products/computing/tablets/
+ 'ViewsonicTablet' => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a',
+ // https://www.verizonwireless.com/tablets/verizon/
+ 'VerizonTablet' => 'QTAQZ3|QTAIR7|QTAQTZ3|QTASUN1|QTASUN2|QTAXIA1',
+ // http://www.odys.de/web/internet-tablet_en.html
+ 'OdysTablet' => 'LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\bXELIO\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10',
+ // http://www.captiva-power.de/products.html#tablets-en
+ 'CaptivaTablet' => 'CAPTIVA PAD',
+ // IconBIT - http://www.iconbit.com/products/tablets/
+ 'IconbitTablet' => 'NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S',
+ // http://www.teclast.com/topic.php?channelID=70&topicID=140&pid=63
+ 'TeclastTablet' => 'T98 4G|\bP80\b|\bX90HD\b|X98 Air|X98 Air 3G|\bX89\b|P80 3G|\bX80h\b|P98 Air|\bX89HD\b|P98 3G|\bP90HD\b|P89 3G|X98 3G|\bP70h\b|P79HD 3G|G18d 3G|\bP79HD\b|\bP89s\b|\bA88\b|\bP10HD\b|\bP19HD\b|G18 3G|\bP78HD\b|\bA78\b|\bP75\b|G17s 3G|G17h 3G|\bP85t\b|\bP90\b|\bP11\b|\bP98t\b|\bP98HD\b|\bG18d\b|\bP85s\b|\bP11HD\b|\bP88s\b|\bA80HD\b|\bA80se\b|\bA10h\b|\bP89\b|\bP78s\b|\bG18\b|\bP85\b|\bA70h\b|\bA70\b|\bG17\b|\bP18\b|\bA80s\b|\bA11s\b|\bP88HD\b|\bA80h\b|\bP76s\b|\bP76h\b|\bP98\b|\bA10HD\b|\bP78\b|\bP88\b|\bA11\b|\bA10t\b|\bP76a\b|\bP76t\b|\bP76e\b|\bP85HD\b|\bP85a\b|\bP86\b|\bP75HD\b|\bP76v\b|\bA12\b|\bP75a\b|\bA15\b|\bP76Ti\b|\bP81HD\b|\bA10\b|\bT760VE\b|\bT720HD\b|\bP76\b|\bP73\b|\bP71\b|\bP72\b|\bT720SE\b|\bC520Ti\b|\bT760\b|\bT720VE\b|T720-3GE|T720-WiFi',
+ // Onda - http://www.onda-tablet.com/buy-android-onda.html?dir=desc&limit=all&order=price
+ 'OndaTablet' => '\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\b[\s]+|V10 \b4G\b',
+ 'JaytechTablet' => 'TPC-PA762',
+ 'BlaupunktTablet' => 'Endeavour 800NG|Endeavour 1010',
+ // http://www.digma.ru/support/download/
+ // @todo: Ebooks also (if requested)
+ 'DigmaTablet' => '\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\b',
+ // http://www.evolioshop.com/ro/tablete-pc.html
+ // http://www.evolio.ro/support/downloads_static.html?cat=2
+ // @todo: Research some more
+ 'EvolioTablet' => 'ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\bEvotab\b|\bNeura\b',
+ // @todo http://www.lavamobiles.com/tablets-data-cards
+ 'LavaTablet' => 'QPAD E704|\bIvoryS\b|E-TAB IVORY|\bE-TAB\b',
+ // http://www.breezetablet.com/
+ 'AocTablet' => 'MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712',
+ // http://www.mpmaneurope.com/en/products/internet-tablets-14/android-tablets-14/
+ 'MpmanTablet' => 'MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\bMPG7\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010',
+ // https://www.celkonmobiles.com/?_a=categoryphones&sid=2
+ 'CelkonTablet' => 'CT695|CT888|CT[\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\bCT-1\b',
+ // http://www.wolderelectronics.com/productos/manuales-y-guias-rapidas/categoria-2-miTab
+ 'WolderTablet' => 'miTab \b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\b',
+ 'MediacomTablet' => 'M-MPI10C3G|M-SP10EG|M-SP10EGP|M-SP10HXAH|M-SP7HXAH|M-SP10HXBH|M-SP8HXAH|M-SP8MXA',
+ // http://www.mi.com/en
+ 'MiTablet' => '\bMI PAD\b|\bHM NOTE 1W\b',
+ // http://www.nbru.cn/index.html
+ 'NibiruTablet' => 'Nibiru M1|Nibiru Jupiter One',
+ // http://navroad.com/products/produkty/tablety/
+ // http://navroad.com/products/produkty/tablety/
+ 'NexoTablet' => 'NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI',
+ // http://leader-online.com/new_site/product-category/tablets/
+ // http://www.leader-online.net.au/List/Tablet
+ 'LeaderTablet' => 'TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100',
+ // http://www.datawind.com/ubislate/
+ 'UbislateTablet' => 'UbiSlate[\s]?7C',
+ // http://www.pocketbook-int.com/ru/support
+ 'PocketBookTablet' => 'Pocketbook',
+ // http://www.kocaso.com/product_tablet.html
+ 'KocasoTablet' => '\b(TB-1207)\b',
+ // http://global.hisense.com/product/asia/tablet/Sero7/201412/t20141215_91832.htm
+ 'HisenseTablet' => '\b(F5281|E2371)\b',
+ // http://www.tesco.com/direct/hudl/
+ 'Hudl' => 'Hudl HT7S3|Hudl 2',
+ // http://www.telstra.com.au/home-phone/thub-2/
+ 'TelstraTablet' => 'T-Hub2',
+ 'GenericTablet' => 'Android.*\b97D\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|rk30sdk|\bEVOTAB\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\bM6pro\b|CT1020W|arc 10HD|\bTP750\b|\bQTAQZ3\b|WVT101|TM1088|KT107'
+ );
+
+ /**
+ * List of mobile Operating Systems.
+ *
+ * @var array
+ */
+ protected static $operatingSystems = array(
+ 'AndroidOS' => 'Android',
+ 'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os',
+ 'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino',
+ 'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b',
+ // @reference: http://en.wikipedia.org/wiki/Windows_Mobile
+ 'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Windows Mobile|Windows Phone [0-9.]+|WCE;',
+ // @reference: http://en.wikipedia.org/wiki/Windows_Phone
+ // http://wifeng.cn/?r=blog&a=view&id=106
+ // http://nicksnettravels.builttoroam.com/post/2011/01/10/Bogus-Windows-Phone-7-User-Agent-String.aspx
+ // http://msdn.microsoft.com/library/ms537503.aspx
+ // https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
+ 'WindowsPhoneOS' => 'Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;',
+ 'iOS' => '\biPhone.*Mobile|\biPod|\biPad|AppleCoreMedia',
+ // https://en.wikipedia.org/wiki/IPadOS
+ 'iPadOS' => 'CPU OS 13',
+ // @reference https://en.m.wikipedia.org/wiki/Sailfish_OS
+ // https://sailfishos.org/
+ 'SailfishOS' => 'Sailfish',
+ // http://en.wikipedia.org/wiki/MeeGo
+ // @todo: research MeeGo in UAs
+ 'MeeGoOS' => 'MeeGo',
+ // http://en.wikipedia.org/wiki/Maemo
+ // @todo: research Maemo in UAs
+ 'MaemoOS' => 'Maemo',
+ 'JavaOS' => 'J2ME/|\bMIDP\b|\bCLDC\b', // '|Java/' produces bug #135
+ 'webOS' => 'webOS|hpwOS',
+ 'badaOS' => '\bBada\b',
+ 'BREWOS' => 'BREW',
+ );
+
+ /**
+ * List of mobile User Agents.
+ *
+ * IMPORTANT: This is a list of only mobile browsers.
+ * Mobile Detect 2.x supports only mobile browsers,
+ * it was never designed to detect all browsers.
+ * The change will come in 2017 in the 3.x release for PHP7.
+ *
+ * @var array
+ */
+ protected static $browsers = array(
+ //'Vivaldi' => 'Vivaldi',
+ // @reference: https://developers.google.com/chrome/mobile/docs/user-agent
+ 'Chrome' => '\bCrMo\b|CriOS.*Mobile|Android.*Chrome/[.0-9]* Mobile',
+ 'Dolfin' => '\bDolfin\b',
+ 'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+$|Coast/[0-9.]+',
+ 'Skyfire' => 'Skyfire',
+ // Added "Edge on iOS" https://github.com/serbanghita/Mobile-Detect/issues/764
+ 'Edge' => 'EdgiOS.*Mobile|Mobile Safari/[.0-9]* Edge',
+ 'IE' => 'IEMobile|MSIEMobile', // |Trident/[.0-9]+
+ 'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS.*Mobile',
+ 'Bolt' => 'bolt',
+ 'TeaShark' => 'teashark',
+ 'Blazer' => 'Blazer',
+ // @reference: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/OptimizingforSafarioniPhone/OptimizingforSafarioniPhone.html#//apple_ref/doc/uid/TP40006517-SW3
+ // Excluded "Edge on iOS" https://github.com/serbanghita/Mobile-Detect/issues/764
+ 'Safari' => 'Version((?!\bEdgiOS\b).)*Mobile.*Safari|Safari.*Mobile|MobileSafari',
+ // http://en.wikipedia.org/wiki/Midori_(web_browser)
+ //'Midori' => 'midori',
+ //'Tizen' => 'Tizen',
+ 'WeChat' => '\bMicroMessenger\b',
+ 'UCBrowser' => 'UC.*Browser|UCWEB',
+ 'baiduboxapp' => 'baiduboxapp',
+ 'baidubrowser' => 'baidubrowser',
+ // https://github.com/serbanghita/Mobile-Detect/issues/7
+ 'DiigoBrowser' => 'DiigoBrowser',
+ // http://www.puffinbrowser.com/index.php
+ // https://github.com/serbanghita/Mobile-Detect/issues/752
+ // 'Puffin' => 'Puffin',
+ // http://mercury-browser.com/index.html
+ 'Mercury' => '\bMercury\b',
+ // http://en.wikipedia.org/wiki/Obigo_Browser
+ 'ObigoBrowser' => 'Obigo',
+ // http://en.wikipedia.org/wiki/NetFront
+ 'NetFront' => 'NF-Browser',
+ // @reference: http://en.wikipedia.org/wiki/Minimo
+ // http://en.wikipedia.org/wiki/Vision_Mobile_Browser
+ 'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger',
+ // @reference: https://en.wikipedia.org/wiki/Pale_Moon_(web_browser)
+ 'PaleMoon' => 'Android.*PaleMoon|Mobile.*PaleMoon',
+ );
+
+ /**
+ * Utilities.
+ *
+ * @var array
+ */
+ protected static $utilities = array(
+ // Experimental. When a mobile device wants to switch to 'Desktop Mode'.
+ // http://scottcate.com/technology/windows-phone-8-ie10-desktop-or-mobile/
+ // https://github.com/serbanghita/Mobile-Detect/issues/57#issuecomment-15024011
+ // https://developers.facebook.com/docs/sharing/webmasters/crawler/
+ 'Bot' => 'Googlebot|facebookexternalhit|Google-AMPHTML|s~amp-validator|AdsBot-Google|Google Keyword Suggestion|Facebot|YandexBot|YandexMobileBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|Exabot|MJ12bot|YandexImages|TurnitinBot|Pingdom|contentkingapp|AspiegelBot',
+ 'MobileBot' => 'Googlebot-Mobile|AdsBot-Google-Mobile|YahooSeeker/M1A1-R2D2',
+ 'DesktopMode' => 'WPDesktop',
+ 'TV' => 'SonyDTV|HbbTV', // experimental
+ 'WebKit' => '(webkit)[ /]([\w.]+)',
+ // @todo: Include JXD consoles.
+ 'Console' => '\b(Nintendo|Nintendo WiiU|Nintendo 3DS|Nintendo Switch|PLAYSTATION|Xbox)\b',
+ 'Watch' => 'SM-V700',
+ );
+
+ /**
+ * All possible HTTP headers that represent the
+ * User-Agent string.
+ *
+ * @var array
+ */
+ protected static $uaHttpHeaders = array(
+ // The default User-Agent string.
+ 'HTTP_USER_AGENT',
+ // Header can occur on devices using Opera Mini.
+ 'HTTP_X_OPERAMINI_PHONE_UA',
+ // Vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/
+ 'HTTP_X_DEVICE_USER_AGENT',
+ 'HTTP_X_ORIGINAL_USER_AGENT',
+ 'HTTP_X_SKYFIRE_PHONE',
+ 'HTTP_X_BOLT_PHONE_UA',
+ 'HTTP_DEVICE_STOCK_UA',
+ 'HTTP_X_UCBROWSER_DEVICE_UA'
+ );
+
+ /**
+ * The individual segments that could exist in a User-Agent string. VER refers to the regular
+ * expression defined in the constant self::VER.
+ *
+ * @var array
+ */
+ protected static $properties = array(
+
+ // Build
+ 'Mobile' => 'Mobile/[VER]',
+ 'Build' => 'Build/[VER]',
+ 'Version' => 'Version/[VER]',
+ 'VendorID' => 'VendorID/[VER]',
+
+ // Devices
+ 'iPad' => 'iPad.*CPU[a-z ]+[VER]',
+ 'iPhone' => 'iPhone.*CPU[a-z ]+[VER]',
+ 'iPod' => 'iPod.*CPU[a-z ]+[VER]',
+ //'BlackBerry' => array('BlackBerry[VER]', 'BlackBerry [VER];'),
+ 'Kindle' => 'Kindle/[VER]',
+
+ // Browser
+ 'Chrome' => array('Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]'),
+ 'Coast' => array('Coast/[VER]'),
+ 'Dolfin' => 'Dolfin/[VER]',
+ // @reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
+ 'Firefox' => array('Firefox/[VER]', 'FxiOS/[VER]'),
+ 'Fennec' => 'Fennec/[VER]',
+ // http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
+ // https://msdn.microsoft.com/en-us/library/ie/hh869301(v=vs.85).aspx
+ 'Edge' => 'Edge/[VER]',
+ 'IE' => array('IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];', 'Trident/[0-9.]+;.*rv:[VER]'),
+ // http://en.wikipedia.org/wiki/NetFront
+ 'NetFront' => 'NetFront/[VER]',
+ 'NokiaBrowser' => 'NokiaBrowser/[VER]',
+ 'Opera' => array( ' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]' ),
+ 'Opera Mini' => 'Opera Mini/[VER]',
+ 'Opera Mobi' => 'Version/[VER]',
+ 'UCBrowser' => array( 'UCWEB[VER]', 'UC.*Browser/[VER]' ),
+ 'MQQBrowser' => 'MQQBrowser/[VER]',
+ 'MicroMessenger' => 'MicroMessenger/[VER]',
+ 'baiduboxapp' => 'baiduboxapp/[VER]',
+ 'baidubrowser' => 'baidubrowser/[VER]',
+ 'SamsungBrowser' => 'SamsungBrowser/[VER]',
+ 'Iron' => 'Iron/[VER]',
+ // @note: Safari 7534.48.3 is actually Version 5.1.
+ // @note: On BlackBerry the Version is overwriten by the OS.
+ 'Safari' => array( 'Version/[VER]', 'Safari/[VER]' ),
+ 'Skyfire' => 'Skyfire/[VER]',
+ 'Tizen' => 'Tizen/[VER]',
+ 'Webkit' => 'webkit[ /][VER]',
+ 'PaleMoon' => 'PaleMoon/[VER]',
+ 'SailfishBrowser' => 'SailfishBrowser/[VER]',
+
+ // Engine
+ 'Gecko' => 'Gecko/[VER]',
+ 'Trident' => 'Trident/[VER]',
+ 'Presto' => 'Presto/[VER]',
+ 'Goanna' => 'Goanna/[VER]',
+
+ // OS
+ 'iOS' => ' \bi?OS\b [VER][ ;]{1}',
+ 'Android' => 'Android [VER]',
+ 'Sailfish' => 'Sailfish [VER]',
+ 'BlackBerry' => array('BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'),
+ 'BREW' => 'BREW [VER]',
+ 'Java' => 'Java/[VER]',
+ // @reference: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/08/29/introducing-the-ie9-on-windows-phone-mango-user-agent-string.aspx
+ // @reference: http://en.wikipedia.org/wiki/Windows_NT#Releases
+ 'Windows Phone OS' => array( 'Windows Phone OS [VER]', 'Windows Phone [VER]'),
+ 'Windows Phone' => 'Windows Phone [VER]',
+ 'Windows CE' => 'Windows CE/[VER]',
+ // http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/6be392da-4d2f-41b4-8354-8dcee20c85cd
+ 'Windows NT' => 'Windows NT [VER]',
+ 'Symbian' => array('SymbianOS/[VER]', 'Symbian/[VER]'),
+ 'webOS' => array('webOS/[VER]', 'hpwOS/[VER];'),
+ );
+
+ /**
+ * Construct an instance of this class.
+ *
+ * @param array $headers Specify the headers as injection. Should be PHP _SERVER flavored.
+ * If left empty, will use the global _SERVER['HTTP_*'] vars instead.
+ * @param string $userAgent Inject the User-Agent header. If null, will use HTTP_USER_AGENT
+ * from the $headers array instead.
+ */
+ public function __construct(
+ array $headers = null,
+ $userAgent = null
+ ) {
+ $this->setHttpHeaders($headers);
+ $this->setUserAgent($userAgent);
+ }
+
+ /**
+ * Get the current script version.
+ * This is useful for the demo.php file,
+ * so people can check on what version they are testing
+ * for mobile devices.
+ *
+ * @return string The version number in semantic version format.
+ */
+ public static function getScriptVersion()
+ {
+ return self::VERSION;
+ }
+
+ /**
+ * Set the HTTP Headers. Must be PHP-flavored. This method will reset existing headers.
+ *
+ * @param array $httpHeaders The headers to set. If null, then using PHP's _SERVER to extract
+ * the headers. The default null is left for backwards compatibility.
+ */
+ public function setHttpHeaders($httpHeaders = null)
+ {
+ // use global _SERVER if $httpHeaders aren't defined
+ if (!is_array($httpHeaders) || !count($httpHeaders)) {
+ $httpHeaders = $_SERVER;
+ }
+
+ // clear existing headers
+ $this->httpHeaders = array();
+
+ // Only save HTTP headers. In PHP land, that means only _SERVER vars that
+ // start with HTTP_.
+ foreach ($httpHeaders as $key => $value) {
+ if (substr($key, 0, 5) === 'HTTP_') {
+ $this->httpHeaders[$key] = $value;
+ }
+ }
+
+ // In case we're dealing with CloudFront, we need to know.
+ $this->setCfHeaders($httpHeaders);
+ }
+
+ /**
+ * Retrieves the HTTP headers.
+ *
+ * @return array
+ */
+ public function getHttpHeaders()
+ {
+ return $this->httpHeaders;
+ }
+
+ /**
+ * Retrieves a particular header. If it doesn't exist, no exception/error is caused.
+ * Simply null is returned.
+ *
+ * @param string $header The name of the header to retrieve. Can be HTTP compliant such as
+ * "User-Agent" or "X-Device-User-Agent" or can be php-esque with the
+ * all-caps, HTTP_ prefixed, underscore separated awesomeness.
+ *
+ * @return string|null The value of the header.
+ */
+ public function getHttpHeader($header)
+ {
+ // are we using PHP-flavored headers?
+ if (strpos($header, '_') === false) {
+ $header = str_replace('-', '_', $header);
+ $header = strtoupper($header);
+ }
+
+ // test the alternate, too
+ $altHeader = 'HTTP_' . $header;
+
+ //Test both the regular and the HTTP_ prefix
+ if (isset($this->httpHeaders[$header])) {
+ return $this->httpHeaders[$header];
+ } elseif (isset($this->httpHeaders[$altHeader])) {
+ return $this->httpHeaders[$altHeader];
+ }
+
+ return null;
+ }
+
+ public function getMobileHeaders()
+ {
+ return self::$mobileHeaders;
+ }
+
+ /**
+ * Get all possible HTTP headers that
+ * can contain the User-Agent string.
+ *
+ * @return array List of HTTP headers.
+ */
+ public function getUaHttpHeaders()
+ {
+ return self::$uaHttpHeaders;
+ }
+
+
+ /**
+ * Set CloudFront headers
+ * http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-device
+ *
+ * @param array $cfHeaders List of HTTP headers
+ *
+ * @return boolean If there were CloudFront headers to be set
+ */
+ public function setCfHeaders($cfHeaders = null) {
+ // use global _SERVER if $cfHeaders aren't defined
+ if (!is_array($cfHeaders) || !count($cfHeaders)) {
+ $cfHeaders = $_SERVER;
+ }
+
+ // clear existing headers
+ $this->cloudfrontHeaders = array();
+
+ // Only save CLOUDFRONT headers. In PHP land, that means only _SERVER vars that
+ // start with cloudfront-.
+ $response = false;
+ foreach ($cfHeaders as $key => $value) {
+ if (substr(strtolower($key), 0, 16) === 'http_cloudfront_') {
+ $this->cloudfrontHeaders[strtoupper($key)] = $value;
+ $response = true;
+ }
+ }
+
+ return $response;
+ }
+
+ /**
+ * Retrieves the cloudfront headers.
+ *
+ * @return array
+ */
+ public function getCfHeaders()
+ {
+ return $this->cloudfrontHeaders;
+ }
+
+ /**
+ * @param string $userAgent
+ * @return string
+ */
+ private function prepareUserAgent($userAgent) {
+ $userAgent = trim($userAgent);
+ $userAgent = substr($userAgent, 0, 500);
+ return $userAgent;
+ }
+
+ /**
+ * Set the User-Agent to be used.
+ *
+ * @param string $userAgent The user agent string to set.
+ *
+ * @return string|null
+ */
+ public function setUserAgent($userAgent = null)
+ {
+ // Invalidate cache due to #375
+ $this->cache = array();
+
+ if (false === empty($userAgent)) {
+ return $this->userAgent = $this->prepareUserAgent($userAgent);
+ } else {
+ $this->userAgent = null;
+ foreach ($this->getUaHttpHeaders() as $altHeader) {
+ if (false === empty($this->httpHeaders[$altHeader])) { // @todo: should use getHttpHeader(), but it would be slow. (Serban)
+ $this->userAgent .= $this->httpHeaders[$altHeader] . " ";
+ }
+ }
+
+ if (!empty($this->userAgent)) {
+ return $this->userAgent = $this->prepareUserAgent($this->userAgent);
+ }
+ }
+
+ if (count($this->getCfHeaders()) > 0) {
+ return $this->userAgent = 'Amazon CloudFront';
+ }
+ return $this->userAgent = null;
+ }
+
+ /**
+ * Retrieve the User-Agent.
+ *
+ * @return string|null The user agent if it's set.
+ */
+ public function getUserAgent()
+ {
+ return $this->userAgent;
+ }
+
+ /**
+ * Set the detection type. Must be one of self::DETECTION_TYPE_MOBILE or
+ * self::DETECTION_TYPE_EXTENDED. Otherwise, nothing is set.
+ *
+ * @deprecated since version 2.6.9
+ *
+ * @param string $type The type. Must be a self::DETECTION_TYPE_* constant. The default
+ * parameter is null which will default to self::DETECTION_TYPE_MOBILE.
+ */
+ public function setDetectionType($type = null)
+ {
+ if ($type === null) {
+ $type = self::DETECTION_TYPE_MOBILE;
+ }
+
+ if ($type !== self::DETECTION_TYPE_MOBILE && $type !== self::DETECTION_TYPE_EXTENDED) {
+ return;
+ }
+
+ $this->detectionType = $type;
+ }
+
+ public function getMatchingRegex()
+ {
+ return $this->matchingRegex;
+ }
+
+ public function getMatchesArray()
+ {
+ return $this->matchesArray;
+ }
+
+ /**
+ * Retrieve the list of known phone devices.
+ *
+ * @return array List of phone devices.
+ */
+ public static function getPhoneDevices()
+ {
+ return self::$phoneDevices;
+ }
+
+ /**
+ * Retrieve the list of known tablet devices.
+ *
+ * @return array List of tablet devices.
+ */
+ public static function getTabletDevices()
+ {
+ return self::$tabletDevices;
+ }
+
+ /**
+ * Alias for getBrowsers() method.
+ *
+ * @return array List of user agents.
+ */
+ public static function getUserAgents()
+ {
+ return self::getBrowsers();
+ }
+
+ /**
+ * Retrieve the list of known browsers. Specifically, the user agents.
+ *
+ * @return array List of browsers / user agents.
+ */
+ public static function getBrowsers()
+ {
+ return self::$browsers;
+ }
+
+ /**
+ * Retrieve the list of known utilities.
+ *
+ * @return array List of utilities.
+ */
+ public static function getUtilities()
+ {
+ return self::$utilities;
+ }
+
+ /**
+ * Method gets the mobile detection rules. This method is used for the magic methods $detect->is*().
+ *
+ * @deprecated since version 2.6.9
+ *
+ * @return array All the rules (but not extended).
+ */
+ public static function getMobileDetectionRules()
+ {
+ static $rules;
+
+ if (!$rules) {
+ $rules = array_merge(
+ self::$phoneDevices,
+ self::$tabletDevices,
+ self::$operatingSystems,
+ self::$browsers
+ );
+ }
+
+ return $rules;
+
+ }
+
+ /**
+ * Method gets the mobile detection rules + utilities.
+ * The reason this is separate is because utilities rules
+ * don't necessary imply mobile. This method is used inside
+ * the new $detect->is('stuff') method.
+ *
+ * @deprecated since version 2.6.9
+ *
+ * @return array All the rules + extended.
+ */
+ public function getMobileDetectionRulesExtended()
+ {
+ static $rules;
+
+ if (!$rules) {
+ // Merge all rules together.
+ $rules = array_merge(
+ self::$phoneDevices,
+ self::$tabletDevices,
+ self::$operatingSystems,
+ self::$browsers,
+ self::$utilities
+ );
+ }
+
+ return $rules;
+ }
+
+ /**
+ * Retrieve the current set of rules.
+ *
+ * @deprecated since version 2.6.9
+ *
+ * @return array
+ */
+ public function getRules()
+ {
+ if ($this->detectionType == self::DETECTION_TYPE_EXTENDED) {
+ return self::getMobileDetectionRulesExtended();
+ } else {
+ return self::getMobileDetectionRules();
+ }
+ }
+
+ /**
+ * Retrieve the list of mobile operating systems.
+ *
+ * @return array The list of mobile operating systems.
+ */
+ public static function getOperatingSystems()
+ {
+ return self::$operatingSystems;
+ }
+
+ /**
+ * Check the HTTP headers for signs of mobile.
+ * This is the fastest mobile check possible; it's used
+ * inside isMobile() method.
+ *
+ * @return bool
+ */
+ public function checkHttpHeadersForMobile()
+ {
+
+ foreach ($this->getMobileHeaders() as $mobileHeader => $matchType) {
+ if (isset($this->httpHeaders[$mobileHeader])) {
+ if (isset($matchType['matches']) && is_array($matchType['matches'])) {
+ foreach ($matchType['matches'] as $_match) {
+ if (strpos($this->httpHeaders[$mobileHeader], $_match) !== false) {
+ return true;
+ }
+ }
+
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Magic overloading method.
+ *
+ * @method boolean is[...]()
+ * @param string $name
+ * @param array $arguments
+ * @return mixed
+ * @throws BadMethodCallException when the method doesn't exist and doesn't start with 'is'
+ */
+ public function __call($name, $arguments)
+ {
+ // make sure the name starts with 'is', otherwise
+ if (substr($name, 0, 2) !== 'is') {
+ throw new BadMethodCallException("No such method exists: $name");
+ }
+
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
+
+ $key = substr($name, 2);
+
+ return $this->matchUAAgainstKey($key);
+ }
+
+ /**
+ * Find a detection rule that matches the current User-agent.
+ *
+ * @param null $userAgent deprecated
+ * @return boolean
+ */
+ protected function matchDetectionRulesAgainstUA($userAgent = null)
+ {
+ // Begin general search.
+ foreach ($this->getRules() as $_regex) {
+ if (empty($_regex)) {
+ continue;
+ }
+
+ if ($this->match($_regex, $userAgent)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Search for a certain key in the rules array.
+ * If the key is found then try to match the corresponding
+ * regex against the User-Agent.
+ *
+ * @param string $key
+ *
+ * @return boolean
+ */
+ protected function matchUAAgainstKey($key)
+ {
+ // Make the keys lowercase so we can match: isIphone(), isiPhone(), isiphone(), etc.
+ $key = strtolower($key);
+ if (false === isset($this->cache[$key])) {
+
+ // change the keys to lower case
+ $_rules = array_change_key_case($this->getRules());
+
+ if (false === empty($_rules[$key])) {
+ $this->cache[$key] = $this->match($_rules[$key]);
+ }
+
+ if (false === isset($this->cache[$key])) {
+ $this->cache[$key] = false;
+ }
+ }
+
+ return $this->cache[$key];
+ }
+
+ /**
+ * Check if the device is mobile.
+ * Returns true if any type of mobile device detected, including special ones
+ * @param null $userAgent deprecated
+ * @param null $httpHeaders deprecated
+ * @return bool
+ */
+ public function isMobile($userAgent = null, $httpHeaders = null)
+ {
+
+ if ($httpHeaders) {
+ $this->setHttpHeaders($httpHeaders);
+ }
+
+ if ($userAgent) {
+ $this->setUserAgent($userAgent);
+ }
+
+ // Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
+ if ($this->getUserAgent() === 'Amazon CloudFront') {
+ $cfHeaders = $this->getCfHeaders();
+ if(array_key_exists('HTTP_CLOUDFRONT_IS_MOBILE_VIEWER', $cfHeaders) && $cfHeaders['HTTP_CLOUDFRONT_IS_MOBILE_VIEWER'] === 'true') {
+ return true;
+ }
+ }
+
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
+
+ if ($this->checkHttpHeadersForMobile()) {
+ return true;
+ } else {
+ return $this->matchDetectionRulesAgainstUA();
+ }
+
+ }
+
+ /**
+ * Check if the device is a tablet.
+ * Return true if any type of tablet device is detected.
+ *
+ * @param string $userAgent deprecated
+ * @param array $httpHeaders deprecated
+ * @return bool
+ */
+ public function isTablet($userAgent = null, $httpHeaders = null)
+ {
+ // Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
+ if ($this->getUserAgent() === 'Amazon CloudFront') {
+ $cfHeaders = $this->getCfHeaders();
+ if(array_key_exists('HTTP_CLOUDFRONT_IS_TABLET_VIEWER', $cfHeaders) && $cfHeaders['HTTP_CLOUDFRONT_IS_TABLET_VIEWER'] === 'true') {
+ return true;
+ }
+ }
+
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
+
+ foreach (self::$tabletDevices as $_regex) {
+ if ($this->match($_regex, $userAgent)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * This method checks for a certain property in the
+ * userAgent.
+ * @todo: The httpHeaders part is not yet used.
+ *
+ * @param string $key
+ * @param string $userAgent deprecated
+ * @param string $httpHeaders deprecated
+ * @return bool|int|null
+ */
+ public function is($key, $userAgent = null, $httpHeaders = null)
+ {
+ // Set the UA and HTTP headers only if needed (eg. batch mode).
+ if ($httpHeaders) {
+ $this->setHttpHeaders($httpHeaders);
+ }
+
+ if ($userAgent) {
+ $this->setUserAgent($userAgent);
+ }
+
+ $this->setDetectionType(self::DETECTION_TYPE_EXTENDED);
+
+ return $this->matchUAAgainstKey($key);
+ }
+
+ /**
+ * Some detection rules are relative (not standard),
+ * because of the diversity of devices, vendors and
+ * their conventions in representing the User-Agent or
+ * the HTTP headers.
+ *
+ * This method will be used to check custom regexes against
+ * the User-Agent string.
+ *
+ * @param $regex
+ * @param string $userAgent
+ * @return bool
+ *
+ * @todo: search in the HTTP headers too.
+ */
+ public function match($regex, $userAgent = null)
+ {
+ if (!\is_string($userAgent) && !\is_string($this->userAgent)) {
+ return false;
+ }
+
+ $match = (bool) preg_match(sprintf('#%s#is', $regex), (false === empty($userAgent) ? $userAgent : $this->userAgent), $matches);
+ // If positive match is found, store the results for debug.
+ if ($match) {
+ $this->matchingRegex = $regex;
+ $this->matchesArray = $matches;
+ }
+
+ return $match;
+ }
+
+ /**
+ * Get the properties array.
+ *
+ * @return array
+ */
+ public static function getProperties()
+ {
+ return self::$properties;
+ }
+
+ /**
+ * Prepare the version number.
+ *
+ * @todo Remove the error supression from str_replace() call.
+ *
+ * @param string $ver The string version, like "2.6.21.2152";
+ *
+ * @return float
+ */
+ public function prepareVersionNo($ver)
+ {
+ $ver = str_replace(array('_', ' ', '/'), '.', $ver);
+ $arrVer = explode('.', $ver, 2);
+
+ if (isset($arrVer[1])) {
+ $arrVer[1] = @str_replace('.', '', $arrVer[1]); // @todo: treat strings versions.
+ }
+
+ return (float) implode('.', $arrVer);
+ }
+
+ /**
+ * Check the version of the given property in the User-Agent.
+ * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
+ *
+ * @param string $propertyName The name of the property. See self::getProperties() array
+ * keys for all possible properties.
+ * @param string $type Either self::VERSION_TYPE_STRING to get a string value or
+ * self::VERSION_TYPE_FLOAT indicating a float value. This parameter
+ * is optional and defaults to self::VERSION_TYPE_STRING. Passing an
+ * invalid parameter will default to the this type as well.
+ *
+ * @return string|float|false The version of the property we are trying to extract.
+ */
+ public function version($propertyName, $type = self::VERSION_TYPE_STRING)
+ {
+ if (empty($propertyName)) {
+ return false;
+ }
+
+ if (!\is_string($this->userAgent)) {
+ return false;
+ }
+
+ // set the $type to the default if we don't recognize the type
+ if ($type !== self::VERSION_TYPE_STRING && $type !== self::VERSION_TYPE_FLOAT) {
+ $type = self::VERSION_TYPE_STRING;
+ }
+
+ $properties = self::getProperties();
+
+ // Check if the property exists in the properties array.
+ if (true === isset($properties[$propertyName])) {
+
+ // Prepare the pattern to be matched.
+ // Make sure we always deal with an array (string is converted).
+ $properties[$propertyName] = (array) $properties[$propertyName];
+
+ foreach ($properties[$propertyName] as $propertyMatchString) {
+
+ $propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);
+
+ // Identify and extract the version.
+ preg_match(sprintf('#%s#is', $propertyPattern), $this->userAgent, $match);
+
+ if (false === empty($match[1])) {
+ $version = ($type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1]);
+
+ return $version;
+ }
+
+ }
+
+ }
+
+ return false;
+ }
+
+ /**
+ * Retrieve the mobile grading, using self::MOBILE_GRADE_* constants.
+ * @deprecated This is no longer being maintained, it was an experiment at the time.
+ * @return string One of the self::MOBILE_GRADE_* constants.
+ */
+ public function mobileGrade()
+ {
+ $isMobile = $this->isMobile();
+
+ if (
+ // Apple iOS 4-7.0 – Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3 / 5.1 / 6.1), iPad 3 (5.1 / 6.0), iPad Mini (6.1), iPad Retina (7.0), iPhone 3GS (4.3), iPhone 4 (4.3 / 5.1), iPhone 4S (5.1 / 6.0), iPhone 5 (6.0), and iPhone 5S (7.0)
+ $this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT) >= 4.3 ||
+ $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT) >= 4.3 ||
+ $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT) >= 4.3 ||
+
+ // Android 2.1-2.3 - Tested on the HTC Incredible (2.2), original Droid (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5)
+ // Android 3.1 (Honeycomb) - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM
+ // Android 4.0 (ICS) - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices
+ // Android 4.1 (Jelly Bean) - Tested on a Galaxy Nexus and Galaxy 7
+ ( $this->version('Android', self::VERSION_TYPE_FLOAT)>2.1 && $this->is('Webkit') ) ||
+
+ // Windows Phone 7.5-8 - Tested on the HTC Surround (7.5), HTC Trophy (7.5), LG-E900 (7.5), Nokia 800 (7.8), HTC Mazaa (7.8), Nokia Lumia 520 (8), Nokia Lumia 920 (8), HTC 8x (8)
+ $this->version('Windows Phone OS', self::VERSION_TYPE_FLOAT) >= 7.5 ||
+
+ // Tested on the Torch 9800 (6) and Style 9670 (6), BlackBerry® Torch 9810 (7), BlackBerry Z10 (10)
+ $this->is('BlackBerry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) >= 6.0 ||
+ // Blackberry Playbook (1.0-2.0) - Tested on PlayBook
+ $this->match('Playbook.*Tablet') ||
+
+ // Palm WebOS (1.4-3.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0), HP TouchPad (3.0)
+ ( $this->version('webOS', self::VERSION_TYPE_FLOAT) >= 1.4 && $this->match('Palm|Pre|Pixi') ) ||
+ // Palm WebOS 3.0 - Tested on HP TouchPad
+ $this->match('hp.*TouchPad') ||
+
+ // Firefox Mobile 18 - Tested on Android 2.3 and 4.1 devices
+ ( $this->is('Firefox') && $this->version('Firefox', self::VERSION_TYPE_FLOAT) >= 18 ) ||
+
+ // Chrome for Android - Tested on Android 4.0, 4.1 device
+ ( $this->is('Chrome') && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 4.0 ) ||
+
+ // Skyfire 4.1 - Tested on Android 2.3 device
+ ( $this->is('Skyfire') && $this->version('Skyfire', self::VERSION_TYPE_FLOAT) >= 4.1 && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 ) ||
+
+ // Opera Mobile 11.5-12: Tested on Android 2.3
+ ( $this->is('Opera') && $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT) >= 11.5 && $this->is('AndroidOS') ) ||
+
+ // Meego 1.2 - Tested on Nokia 950 and N9
+ $this->is('MeeGoOS') ||
+
+ // Sailfish OS
+ $this->is('SailfishOS') ||
- // Firefox Desktop 10-18 - Tested on OS X 10.7 and Windows 7
- $this->version('Firefox', self::VERSION_TYPE_FLOAT) >= 10.0 && !$isMobile ||
-
- // Internet Explorer 7-9 - Tested on Windows XP, Vista and 7
- $this->version('IE', self::VERSION_TYPE_FLOAT) >= 7.0 && !$isMobile ||
-
- // Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7
- $this->version('Opera', self::VERSION_TYPE_FLOAT) >= 10 && !$isMobile
- ) {
- return self::MOBILE_GRADE_A;
- }
-
- if (
- $this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT)<4.3 ||
- $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT)<4.3 ||
- $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT)<4.3 ||
-
- // Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
- $this->is('Blackberry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) >= 5 && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<6 ||
-
- //Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3
- ($this->version('Opera Mini', self::VERSION_TYPE_FLOAT) >= 5.0 && $this->version('Opera Mini', self::VERSION_TYPE_FLOAT) <= 7.0 &&
- ($this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 || $this->is('iOS')) ) ||
-
- // Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
- $this->match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') ||
-
- // @todo: report this (tested on Nokia N71)
- $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT) >= 11 && $this->is('SymbianOS')
- ) {
- return self::MOBILE_GRADE_B;
- }
+ // Tizen (pre-release) - Tested on early hardware
+ $this->is('Tizen') ||
- if (
- // Blackberry 4.x - Tested on the Curve 8330
- $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) <= 5.0 ||
- // Windows Mobile - Tested on the HTC Leo (WinMo 5.2)
- $this->match('MSIEMobile|Windows CE.*Mobile') || $this->version('Windows Mobile', self::VERSION_TYPE_FLOAT) <= 5.2 ||
+ // Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser
+ // @todo: more tests here!
+ $this->is('Dolfin') && $this->version('Bada', self::VERSION_TYPE_FLOAT) >= 2.0 ||
- // Tested on original iPhone (3.1), iPhone 3 (3.2)
- $this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT) <= 3.2 ||
- $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT) <= 3.2 ||
- $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT) <= 3.2 ||
+ // UC Browser - Tested on Android 2.3 device
+ ( ($this->is('UC Browser') || $this->is('Dolfin')) && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 ) ||
- // Internet Explorer 7 and older - Tested on Windows XP
- $this->version('IE', self::VERSION_TYPE_FLOAT) <= 7.0 && !$isMobile
- ) {
- return self::MOBILE_GRADE_C;
- }
+ // Kindle 3 and Fire - Tested on the built-in WebKit browser for each
+ ( $this->match('Kindle Fire') ||
+ $this->is('Kindle') && $this->version('Kindle', self::VERSION_TYPE_FLOAT) >= 3.0 ) ||
- // All older smartphone platforms and featurephones - Any device that doesn't support media queries
- // will receive the basic, C grade experience.
- return self::MOBILE_GRADE_C;
- }
+ // Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet
+ $this->is('AndroidOS') && $this->is('NookTablet') ||
+
+ // Chrome Desktop 16-24 - Tested on OS X 10.7 and Windows 7
+ $this->version('Chrome', self::VERSION_TYPE_FLOAT) >= 16 && !$isMobile ||
+
+ // Safari Desktop 5-6 - Tested on OS X 10.7 and Windows 7
+ $this->version('Safari', self::VERSION_TYPE_FLOAT) >= 5.0 && !$isMobile ||
+
+ // Firefox Desktop 10-18 - Tested on OS X 10.7 and Windows 7
+ $this->version('Firefox', self::VERSION_TYPE_FLOAT) >= 10.0 && !$isMobile ||
+
+ // Internet Explorer 7-9 - Tested on Windows XP, Vista and 7
+ $this->version('IE', self::VERSION_TYPE_FLOAT) >= 7.0 && !$isMobile ||
+
+ // Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7
+ $this->version('Opera', self::VERSION_TYPE_FLOAT) >= 10 && !$isMobile
+ ){
+ return self::MOBILE_GRADE_A;
+ }
+
+ if (
+ $this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT)<4.3 ||
+ $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT)<4.3 ||
+ $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT)<4.3 ||
+
+ // Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
+ $this->is('Blackberry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) >= 5 && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<6 ||
+
+ //Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3
+ ($this->version('Opera Mini', self::VERSION_TYPE_FLOAT) >= 5.0 && $this->version('Opera Mini', self::VERSION_TYPE_FLOAT) <= 7.0 &&
+ ($this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 || $this->is('iOS')) ) ||
+
+ // Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
+ $this->match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') ||
+
+ // @todo: report this (tested on Nokia N71)
+ $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT) >= 11 && $this->is('SymbianOS')
+ ){
+ return self::MOBILE_GRADE_B;
+ }
+
+ if (
+ // Blackberry 4.x - Tested on the Curve 8330
+ $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) <= 5.0 ||
+ // Windows Mobile - Tested on the HTC Leo (WinMo 5.2)
+ $this->match('MSIEMobile|Windows CE.*Mobile') || $this->version('Windows Mobile', self::VERSION_TYPE_FLOAT) <= 5.2 ||
+
+ // Tested on original iPhone (3.1), iPhone 3 (3.2)
+ $this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT) <= 3.2 ||
+ $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT) <= 3.2 ||
+ $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT) <= 3.2 ||
+
+ // Internet Explorer 7 and older - Tested on Windows XP
+ $this->version('IE', self::VERSION_TYPE_FLOAT) <= 7.0 && !$isMobile
+ ){
+ return self::MOBILE_GRADE_C;
+ }
+
+ // All older smartphone platforms and featurephones - Any device that doesn't support media queries
+ // will receive the basic, C grade experience.
+ return self::MOBILE_GRADE_C;
+ }
}
diff --git a/htdocs/includes/mobiledetect/mobiledetectlib/README.md b/htdocs/includes/mobiledetect/mobiledetectlib/README.md
index 1eda7e7e5c6..f2cba5ada29 100644
--- a/htdocs/includes/mobiledetect/mobiledetectlib/README.md
+++ b/htdocs/includes/mobiledetect/mobiledetectlib/README.md
@@ -215,6 +215,8 @@ Made by [Nikola Posa](https://github.com/nikolaposa).
**Symfony**
+* [Symfony3 MobileDetectBundle](https://github.com/tattali/MobileDetectBundle) is a Symfony bundle to detect mobile devices, manage mobile view and redirect to the mobile and tablet version. Made by [Théo Attali](https://github.com/tattali).
+
* [Symfony2 Mobile Detect Bundle](https://github.com/suncat2000/MobileDetectBundle) is a bundle for detecting mobile devices,
manage mobile view and redirect to the mobile and tablet version.
Made by [Nikolay Ivlev](https://github.com/suncat2000).
diff --git a/htdocs/includes/odtphp/odf.php b/htdocs/includes/odtphp/odf.php
index 47226a60829..e3160b880f9 100644
--- a/htdocs/includes/odtphp/odf.php
+++ b/htdocs/includes/odtphp/odf.php
@@ -18,6 +18,7 @@ class OdfException extends Exception
* @copyright 2010-2015 - Laurent Destailleur - eldy@users.sourceforge.net
* @copyright 2010 - Vikas Mahajan - http://vikasmahajan.wordpress.com
* @copyright 2012 - Stephen Larroque - lrq3000@gmail.com
+ * @copyright 2023 - Thomas Negre - contact@open-dsi.fr
* @license https://www.gnu.org/copyleft/gpl.html GPL License
* @version 1.5.0
*/
@@ -46,6 +47,9 @@ class Odf
public $userdefined=array();
const PIXEL_TO_CM = 0.026458333;
+ const FIND_TAGS_REGEX = '/<([A-Za-z0-9]+)(?:\s([A-Za-z]+(?:\-[A-Za-z]+)?(?:=(?:".*?")|(?:[0-9]+))))*(?:(?:\s\/>)|(?:>(.*)<\/\1>))/';
+ const FIND_ENCODED_TAGS_REGEX = '/<([A-Za-z]+)(?:\s([A-Za-z]+(?:\-[A-Za-z]+)?(?:=(?:".*?")|(?:[0-9]+))))*(?:(?:\s\/>)|(?:>(.*)<\/\1>))/';
+
/**
* Class constructor
@@ -161,12 +165,16 @@ class Odf
*/
public function convertVarToOdf($value, $encode = true, $charset = 'ISO-8859')
{
- $value = $encode ? htmlspecialchars($value) : $value;
- $value = ($charset == 'ISO-8859') ? utf8_encode($value) : $value;
+ $value = html_entity_decode($value, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401);
+
+ // fix breaklines.
+ $value = preg_replace('/ /', " ", $value);
$convertedValue = $value;
// Check if the value includes html tags
if ($this->_hasHtmlTag($value) === true) {
+ $value = strip_tags($value, '');
+
// Default styles for strong/b, i/em, u, s, sub & sup
$automaticStyles = array(
' ',
@@ -180,7 +188,7 @@ class Odf
$customStyles = array();
$fontDeclarations = array();
- $convertedValue = $this->_replaceHtmlWithOdtTag($this->_getDataFromHtml($value), $customStyles, $fontDeclarations);
+ $convertedValue = $this->_replaceHtmlWithOdtTag($this->_getDataFromHtml($value), $customStyles, $fontDeclarations, $encode, $charset);
foreach ($customStyles as $key => $val) {
array_push($automaticStyles, '' . $val . ' ');
@@ -204,21 +212,23 @@ class Odf
}
$this->contentXml = str_replace('', $fonts . '', $this->contentXml);
} else {
- $convertedValue = preg_replace('/(\r\n|\r|\n)/i', " ", $value);
+ $convertedValue = $this->encode_chars($convertedValue, $encode, $charset);
+ $convertedValue = preg_replace('/(\r\n|\r|\n)/i', " ", $convertedValue);
}
return $convertedValue;
}
/**
- * Replaces html tags in with odt tags and returns an odt string
- *
- * @param array $tags An array with html tags generated by the getDataFromHtml() function
- * @param array $customStyles An array of style defenitions that should be included inside the odt file
- * @param array $fontDeclarations An array of font declarations that should be included inside the odt file
- * @return string
- */
- private function _replaceHtmlWithOdtTag($tags, &$customStyles, &$fontDeclarations)
+ * Replaces html tags in with odt tags and returns an odt string. Encodes and converts inner text.
+ * @param array $tags An array with html tags generated by the getDataFromHtml() function
+ * @param array $customStyles An array of style defenitions that should be included inside the odt file
+ * @param array $fontDeclarations An array of font declarations that should be included inside the odt file
+ * @param bool $encode If true, special XML characters are encoded
+ * @param string $charset Charset. See encode_chars()
+ * @return string
+ */
+ private function _replaceHtmlWithOdtTag($tags, &$customStyles, &$fontDeclarations, $encode = false, $charset = '')
{
if ($customStyles == null) $customStyles = array();
if ($fontDeclarations == null) $fontDeclarations = array();
@@ -228,7 +238,8 @@ class Odf
foreach ((array) $tags as $tag) {
// Check if the current item is a tag or just plain text
if (isset($tag['text'])) {
- $odtResult .= $tag['text'];
+ $text = $this->encode_chars($tag['text'], $encode, $charset);
+ $odtResult .= $text;
} elseif (isset($tag['name'])) {
switch ($tag['name']) {
case 'br':
@@ -236,23 +247,23 @@ class Odf
break;
case 'strong':
case 'b':
- $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . ' ';
+ $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $this->encode_chars($tag['innerText'], $encode, $charset)) . ' ';
break;
case 'i':
case 'em':
- $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . ' ';
+ $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $this->encode_chars($tag['innerText'], $encode, $charset)) . ' ';
break;
case 'u':
- $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . ' ';
+ $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $this->encode_chars($tag['innerText'], $encode, $charset)) . ' ';
break;
case 's':
- $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . ' ';
+ $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $this->encode_chars($tag['innerText'], $encode, $charset)) . ' ';
break;
case 'sub':
- $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . ' ';
+ $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $this->encode_chars($tag['innerText'], $encode, $charset)) . ' ';
break;
case 'sup':
- $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . ' ';
+ $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $this->encode_chars($tag['innerText'], $encode, $charset)) . ' ';
break;
case 'span':
if (isset($tag['attributes']['style'])) {
@@ -287,9 +298,9 @@ class Odf
}
if (strlen($odtStyles) > 0) {
// Generate a unique id for the style (using microtime and random because some CPUs are really fast...)
- $key = floatval(str_replace('.', '', microtime(true)))+rand(0, 10);
+ $key = floatval(str_replace('.', '', microtime(true))) + rand(0, 10);
$customStyles[$key] = $odtStyles;
- $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $tag['innerText']) . ' ';
+ $odtResult .= '' . ($tag['children'] != null ? $this->_replaceHtmlWithOdtTag($tag['children'], $customStyles, $fontDeclarations) : $this->encode_chars($tag['innerText'], $encode, $charset)) . ' ';
}
}
break;
@@ -303,15 +314,30 @@ class Odf
}
/**
- * Checks if the given text is a html string
- * @param string $text The text to check
- * @return bool
+ * Correctly encode chars
+ * @param string $text The text to encode or not
+ * @param bool $encode If true, special XML characters are encoded
+ * @param string $charset Charset
+ * @return string The converted text
+ * @see self::convertVarToOdf()
*/
- private function _isHtmlTag($text)
+ private function encode_chars($text, $encode = false, $charset = '')
{
- return preg_match('/<([A-Za-z]+)(?:\s([A-Za-z]+(?:\-[A-Za-z]+)?(?:=(?:".*?")|(?:[0-9]+))))*(?:(?:\s\/>)|(?:>(.*)<\/\1>))/', $text);
+ $newtext = $encode ? htmlspecialchars($text, ENT_QUOTES | ENT_XML1) : $text;
+ $newtext = ($charset == 'ISO-8859') ? utf8_encode($newtext) : $newtext;
+ return $newtext;
}
+ /**
+ * Checks if the given text is a html string
+ * @param string $text The text to check
+ * @return bool
+ */
+ private function _isHtmlTag($text)
+ {
+ return preg_match(self::FIND_TAGS_REGEX, $text);
+ }
+
/**
* Checks if the given text includes a html string
* @param string $text The text to check
@@ -319,7 +345,7 @@ class Odf
*/
private function _hasHtmlTag($text)
{
- $result = preg_match_all('/<([A-Za-z]+)(?:\s([A-Za-z]+(?:\-[A-Za-z]+)?(?:=(?:".*?")|(?:[0-9]+))))*(?:(?:\s\/>)|(?:>(.*)<\/\1>))/', $text);
+ $result = preg_match_all(self::FIND_TAGS_REGEX, $text);
return is_numeric($result) && $result > 0;
}
@@ -334,9 +360,8 @@ class Odf
$tempHtml = $html;
while (strlen($tempHtml) > 0) {
- $matches = array();
// Check if the string includes a html tag
- if (preg_match_all('/<([A-Za-z]+)(?:\s([A-Za-z]+(?:\-[A-Za-z]+)?(?:=(?:".*?")|(?:[0-9]+))))*(?:(?:\s\/>)|(?:>(.*)<\/\1>))/', $tempHtml, $matches)) {
+ if (preg_match_all(self::FIND_TAGS_REGEX, $tempHtml, $matches)) {
$tagOffset = strpos($tempHtml, $matches[0][0]);
// Check if the string starts with the html tag
if ($tagOffset > 0) {
@@ -348,13 +373,12 @@ class Odf
$tempHtml = substr($tempHtml, $tagOffset);
}
// Extract the attribute data from the html tag
- $explodedAttributes = array();
preg_match_all('/([0-9A-Za-z]+(?:="[0-9A-Za-z\:\-\s\,\;\#]*")?)+/', $matches[2][0], $explodedAttributes);
$explodedAttributes = array_filter($explodedAttributes[0]);
$attributes = array();
// Store each attribute with its name in the $attributes array
$explodedAttributesCount = count($explodedAttributes);
- for ($i=0; $i<$explodedAttributesCount; $i++) {
+ for ($i = 0; $i < $explodedAttributesCount; $i++) {
$attribute = trim($explodedAttributes[$i]);
// Check if the attribute has a value (like style="") or has no value (like required)
if (strpos($attribute, '=') !== false) {
@@ -368,7 +392,7 @@ class Odf
// Split the style properties and store them in an array
$explodedStyles = explode(';', $attrValue);
$explodedStylesCount = count($explodedStyles);
- for ($n=0; $n<$explodedStylesCount; $n++) {
+ for ($n = 0; $n < $explodedStylesCount; $n++) {
$splitStyle = explode(':', $explodedStyles[$n]);
$attributes[$attrName][trim($splitStyle[0])] = trim($splitStyle[1]);
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/.gitattributes b/htdocs/includes/phpoffice/phpspreadsheet/.gitattributes
new file mode 100644
index 00000000000..0375f558339
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/.gitattributes
@@ -0,0 +1,4 @@
+/tests export-ignore
+README.md export-ignore
+*.min.js binary
+/.github export-ignore
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/.gitignore b/htdocs/includes/phpoffice/phpspreadsheet/.gitignore
new file mode 100644
index 00000000000..0723541d7af
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/.gitignore
@@ -0,0 +1,10 @@
+/tests/codeCoverage
+/analysis
+/vendor/
+/phpunit.xml
+
+## IDE support
+*.buildpath
+*.project
+/.settings
+/.idea
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/.php_cs.dist b/htdocs/includes/phpoffice/phpspreadsheet/.php_cs.dist
new file mode 100644
index 00000000000..23216924880
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/.php_cs.dist
@@ -0,0 +1,183 @@
+exclude(['vendor', 'tests/data/Calculation'])
+ ->in('samples')
+ ->in('src')
+ ->in('tests/PhpSpreadsheetTests')
+ ;
+
+return PhpCsFixer\Config::create()
+ ->setRiskyAllowed(true)
+ ->setFinder($finder)
+ ->setCacheFile(sys_get_temp_dir() . '/php-cs-fixer' . preg_replace('~\W~', '-', __DIR__))
+ ->setRules([
+ 'align_multiline_comment' => true,
+ 'array_syntax' => ['syntax' => 'short'],
+ 'backtick_to_shell_exec' => true,
+ 'binary_operator_spaces' => true,
+ 'blank_line_after_namespace' => true,
+ 'blank_line_after_opening_tag' => true,
+ 'blank_line_before_statement' => true,
+ 'braces' => true,
+ 'cast_spaces' => true,
+ 'class_attributes_separation' => ['elements' => ['method', 'property']], // const are often grouped with other related const
+ 'class_definition' => true,
+ 'class_keyword_remove' => false, // ::class keyword gives us beter support in IDE
+ 'combine_consecutive_issets' => true,
+ 'combine_consecutive_unsets' => true,
+ 'compact_nullable_typehint' => true,
+ 'concat_space' => ['spacing' => 'one'],
+ 'declare_equal_normalize' => true,
+ 'declare_strict_types' => false, // Too early to adopt strict types
+ 'dir_constant' => true,
+ 'doctrine_annotation_array_assignment' => true,
+ 'doctrine_annotation_braces' => true,
+ 'doctrine_annotation_indentation' => true,
+ 'doctrine_annotation_spaces' => true,
+ 'elseif' => true,
+ 'encoding' => true,
+ 'ereg_to_preg' => true,
+ 'escape_implicit_backslashes' => true,
+ 'explicit_indirect_variable' => false, // I feel it makes the code actually harder to read
+ 'explicit_string_variable' => false, // I feel it makes the code actually harder to read
+ 'final_internal_class' => true,
+ 'full_opening_tag' => true,
+ 'function_declaration' => true,
+ 'function_to_constant' => true,
+ 'function_typehint_space' => true,
+ 'general_phpdoc_annotation_remove' => false, // No use for that
+ 'hash_to_slash_comment' => true,
+ 'header_comment' => false, // We don't use common header in all our files
+ 'heredoc_to_nowdoc' => false, // Not sure about this one
+ 'include' => true,
+ 'increment_style' => true,
+ 'indentation_type' => true,
+ 'is_null' => ['use_yoda_style' => false],
+ 'linebreak_after_opening_tag' => true,
+ 'line_ending' => true,
+ 'list_syntax' => ['syntax' => 'short'],
+ 'lowercase_cast' => true,
+ 'lowercase_constants' => true,
+ 'lowercase_keywords' => true,
+ 'magic_constant_casing' => true,
+ 'mb_str_functions' => false, // No, too dangerous to change that
+ 'method_argument_space' => true,
+ 'method_chaining_indentation' => true,
+ 'method_separation' => true,
+ 'modernize_types_casting' => true,
+ 'multiline_comment_opening_closing' => true,
+ 'native_function_casing' => true,
+ 'native_function_invocation' => false, // This is risky and seems to be micro-optimization that make code uglier so not worth it, at least for now
+ 'new_with_braces' => true,
+ 'no_alias_functions' => true,
+ 'no_blank_lines_after_class_opening' => true,
+ 'no_blank_lines_after_phpdoc' => true,
+ 'no_blank_lines_before_namespace' => false, // we want 1 blank line before namespace
+ 'no_break_comment' => true,
+ 'no_closing_tag' => true,
+ 'no_empty_comment' => true,
+ 'no_empty_phpdoc' => true,
+ 'no_empty_statement' => true,
+ 'no_extra_blank_lines' => true,
+ 'no_homoglyph_names' => true,
+ 'no_leading_import_slash' => true,
+ 'no_leading_namespace_whitespace' => true,
+ 'no_mixed_echo_print' => true,
+ 'no_multiline_whitespace_around_double_arrow' => true,
+ 'no_multiline_whitespace_before_semicolons' => true,
+ 'non_printable_character' => true,
+ 'no_null_property_initialization' => true,
+ 'no_php4_constructor' => true,
+ 'normalize_index_brace' => true,
+ 'no_short_bool_cast' => true,
+ 'no_short_echo_tag' => true,
+ 'no_singleline_whitespace_before_semicolons' => true,
+ 'no_spaces_after_function_name' => true,
+ 'no_spaces_around_offset' => true,
+ 'no_spaces_inside_parenthesis' => true,
+ 'no_superfluous_elseif' => false, // Might be risky on a huge code base
+ 'not_operator_with_space' => false, // No we prefer to keep '!' without spaces
+ 'not_operator_with_successor_space' => false, // idem
+ 'no_trailing_comma_in_list_call' => true,
+ 'no_trailing_comma_in_singleline_array' => true,
+ 'no_trailing_whitespace_in_comment' => true,
+ 'no_trailing_whitespace' => true,
+ 'no_unneeded_control_parentheses' => true,
+ 'no_unneeded_curly_braces' => true,
+ 'no_unneeded_final_method' => true,
+ 'no_unreachable_default_argument_value' => true,
+ 'no_unused_imports' => true,
+ 'no_useless_else' => true,
+ 'no_useless_return' => true,
+ 'no_whitespace_before_comma_in_array' => true,
+ 'no_whitespace_in_blank_line' => true,
+ 'object_operator_without_whitespace' => true,
+ 'ordered_class_elements' => false, // We prefer to keep some freedom
+ 'ordered_imports' => true,
+ 'phpdoc_add_missing_param_annotation' => true,
+ 'phpdoc_align' => false, // Waste of time
+ 'phpdoc_annotation_without_dot' => true,
+ 'phpdoc_indent' => true,
+ 'phpdoc_inline_tag' => true,
+ 'phpdoc_no_access' => true,
+ 'phpdoc_no_alias_tag' => true,
+ 'phpdoc_no_empty_return' => true,
+ 'phpdoc_no_package' => true,
+ 'phpdoc_no_useless_inheritdoc' => true,
+ 'phpdoc_order' => true,
+ 'phpdoc_return_self_reference' => true,
+ 'phpdoc_scalar' => true,
+ 'phpdoc_separation' => true,
+ 'phpdoc_single_line_var_spacing' => true,
+ 'phpdoc_summary' => true,
+ 'phpdoc_to_comment' => true,
+ 'phpdoc_trim' => true,
+ 'phpdoc_types_order' => true,
+ 'phpdoc_types' => true,
+ 'phpdoc_var_without_name' => true,
+ 'php_unit_construct' => true,
+ 'php_unit_dedicate_assert' => true,
+ 'php_unit_expectation' => true,
+ 'php_unit_fqcn_annotation' => true,
+ 'php_unit_mock' => true,
+ 'php_unit_namespaced' => true,
+ 'php_unit_no_expectation_annotation' => true,
+ 'php_unit_strict' => false, // We sometime actually need assertEquals
+ 'php_unit_test_annotation' => true,
+ 'php_unit_test_class_requires_covers' => false, // We don't care as much as we should about coverage
+ 'pow_to_exponentiation' => false,
+ 'protected_to_private' => true,
+ 'psr0' => true,
+ 'psr4' => true,
+ 'random_api_migration' => false, // This breaks our unit tests
+ 'return_type_declaration' => true,
+ 'self_accessor' => true,
+ 'semicolon_after_instruction' => false, // Buggy in `samples/index.php`
+ 'short_scalar_cast' => true,
+ 'silenced_deprecation_error' => true,
+ 'simplified_null_return' => false, // While technically correct we prefer to be explicit when returning null
+ 'single_blank_line_at_eof' => true,
+ 'single_blank_line_before_namespace' => true,
+ 'single_class_element_per_statement' => true,
+ 'single_import_per_statement' => true,
+ 'single_line_after_imports' => true,
+ 'single_line_comment_style' => true,
+ 'single_quote' => true,
+ 'space_after_semicolon' => true,
+ 'standardize_not_equals' => true,
+ 'static_lambda' => false, // Risky if we can't guarantee nobody use `bindTo()`
+ 'strict_comparison' => false, // No, too dangerous to change that
+ 'strict_param' => false, // No, too dangerous to change that
+ 'switch_case_semicolon_to_colon' => true,
+ 'switch_case_space' => true,
+ 'ternary_operator_spaces' => true,
+ 'ternary_to_null_coalescing' => true,
+ 'trailing_comma_in_multiline_array' => true,
+ 'trim_array_spaces' => true,
+ 'unary_operator_spaces' => true,
+ 'visibility_required' => true,
+ 'void_return' => false, // Cannot use that with PHP 5.6
+ 'whitespace_after_comma_in_array' => true,
+ 'yoda_style' => false,
+ ]);
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/.scrutinizer.yml b/htdocs/includes/phpoffice/phpspreadsheet/.scrutinizer.yml
new file mode 100644
index 00000000000..748f3ac3fa1
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/.scrutinizer.yml
@@ -0,0 +1,27 @@
+checks:
+ php: true
+
+coding_style:
+ php:
+ spaces:
+ before_parentheses:
+ closure_definition: true
+ around_operators:
+ concatenation: true
+
+build:
+ nodes:
+ analysis:
+ tests:
+ override:
+ - php-scrutinizer-run
+
+tools:
+ external_code_coverage:
+ timeout: 3600
+
+build_failure_conditions:
+ - 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed
+ - 'issues.severity(>= MAJOR).new.exists' # New issues of major or higher severity
+ - 'project.metric_change("scrutinizer.test_coverage", < 0)' # Code Coverage decreased from previous inspection
+ - 'patches.label("Unused Use Statements").new.exists' # No new unused imports patches allowed
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/.travis.yml b/htdocs/includes/phpoffice/phpspreadsheet/.travis.yml
new file mode 100644
index 00000000000..82e25cee1aa
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/.travis.yml
@@ -0,0 +1,57 @@
+language: php
+dist: bionic
+
+php:
+ - 7.1
+ - 7.2
+ - 7.3
+ - 7.4
+
+cache:
+ directories:
+ - vendor
+ - $HOME/.composer/cache
+
+before_script:
+ # Deactivate xdebug
+ - phpenv config-rm xdebug.ini
+ - composer install --ignore-platform-reqs
+
+script:
+ - ./vendor/bin/phpunit
+
+jobs:
+ include:
+
+ - stage: Code style
+ php: 7.2
+ script:
+ - ./vendor/bin/php-cs-fixer fix --diff --verbose --dry-run
+ - ./vendor/bin/phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PSR2 -n
+
+ - stage: Coverage
+ php: 7.2
+ script:
+ - pecl install pcov
+ - composer require pcov/clobber --dev
+ - ./vendor/bin/pcov clobber
+ - ./vendor/bin/phpunit --coverage-clover coverage-clover.xml
+ after_script:
+ - wget https://scrutinizer-ci.com/ocular.phar
+ - php ocular.phar code-coverage:upload --format=php-clover tests/coverage-clover.xml
+
+ - stage: API documentations
+ if: tag is present
+ php: 7.4
+ before_script:
+ - curl -O https://github.com/phpDocumentor/phpDocumentor/releases/download/v3.0.0-rc/phpDocumentor.phar
+ script:
+ - php phpDocumentor.phar --directory src/ --target docs/api
+ deploy:
+ provider: pages
+ skip-cleanup: true
+ local-dir: docs/api
+ github-token: $GITHUB_TOKEN
+ on:
+ all_branches: true
+ condition: $TRAVIS_BRANCH =~ ^master$
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/CHANGELOG.PHPExcel.md b/htdocs/includes/phpoffice/phpspreadsheet/CHANGELOG.PHPExcel.md
new file mode 100644
index 00000000000..3c299020ed0
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/CHANGELOG.PHPExcel.md
@@ -0,0 +1,1593 @@
+# Changelog for PHPExcel
+
+This is the historic changelog of the project when it was still called PHPExcel.
+It exists only for historical purposes and versions mentioned here should not be
+confused with PhpSpreadsheet versions.
+
+## [1.8.1] - 2015-04-30
+
+### Bugfixes
+
+- Fix for Writing an Open Document cell with non-numeric formula - @goncons [#397](https://github.com/PHPOffice/PHPExcel/issues/397)
+- Avoid potential divide by zero in basedrawing - @sarciszewski [#329](https://github.com/PHPOffice/PHPExcel/issues/329)
+- XML External Entity (XXE) Processing, different behaviour between simplexml_load_string() and simplexml_load_file(). - @ymaerschalck [#405](https://github.com/PHPOffice/PHPExcel/issues/405)
+- Fix to ensure that current cell is maintained when executing formula calculations - @MarkBaker
+- Keep/set the value on Reader _loadSheetsOnly as NULL, courtesy of Restless-ET - @MarkBaker [#350](https://github.com/PHPOffice/PHPExcel/issues/350)
+- Loading an Excel 2007 spreadsheet throws an "Autofilter must be set on a range of cells" exception - @MarkBaker [CodePlex #18105](https://phpexcel.codeplex.com/workitem/18105)
+- Fix to autoloader registration for backward compatibility with PHP 5.2.0 not accepting the prepend flag - @MarkBaker [#388](https://github.com/PHPOffice/PHPExcel/issues/388)
+- DOM loadHTMLFile() failing with options flags when using PHP < 5.4.0 - @MarkBaker [#384](https://github.com/PHPOffice/PHPExcel/issues/384)
+- Fix for percentage operator in formulae for BIFF Writer - @MarkBaker
+- Fix to getStyle() call for cell object - @MarkBaker
+- Discard Autofilters in Excel2007 Reader when filter range isn't a valid range - @MarkBaker
+- Fix invalid NA return in VLOOKUP - @frozenstupidity [#423](https://github.com/PHPOffice/PHPExcel/issues/423)
+- "No Impact" conditional formatting fix for NumberFormat - @wiseloren [CodePlex #21454](https://phpexcel.codeplex.com/workitem/21454)
+- Bug in Excel2003XML reader, parsing merged cells - @bobwitlox [#467](https://github.com/PHPOffice/PHPExcel/issues/467)
+- Fix for CEIL() and FLOOR() when number argument is zero - @MarkBaker [#302](https://github.com/PHPOffice/PHPExcel/issues/302)
+
+### General
+
+- Remove cells cleanly when calling RemoveRow() or RemoveColumn() - @MarkBaker
+- Small performance improvement for autosize columns - @MarkBaker
+- Change the getter/setter for zeroHeight to camel case - @frost-nzcr4 [#379](https://github.com/PHPOffice/PHPExcel/issues/379)
+- DefaultValueBinder is too much aggressive when converting string to numeric - @MarkBaker [#394](https://github.com/PHPOffice/PHPExcel/issues/394)
+- Default precalculate formulas to false for writers - @MarkBaker
+- Set default Cyclic Reference behaviour to 1 to eliminate exception when using a single cyclic iteration in formulae - @MarkBaker
+
+### Features
+
+- Some Excel writer libraries erroneously use Codepage 21010 for UTF-16LE - @MarkBaker [#396](https://github.com/PHPOffice/PHPExcel/issues/396)
+- Methods to manage most of the existing options for Chart Axis, Major Grid-lines and Minor Grid-lines - @WiktrzGE [#404](https://github.com/PHPOffice/PHPExcel/issues/404)
+- ODS read/write comments in the cell - @frost-nzcr4 [#403](https://github.com/PHPOffice/PHPExcel/issues/403)
+- Additional Mac CJK codepage definitions - @CQD [#389](https://github.com/PHPOffice/PHPExcel/issues/389)
+- Update Worksheet.php getStyleByColumnAndRow() to allow a range of cells rather than just a single cell - @bolovincev [#269](https://github.com/PHPOffice/PHPExcel/issues/269)
+- New methods added for testing cell status within merge groups - @MarkBaker
+- Handling merge cells in HTML Reader - @cifren/MBaker [#205](https://github.com/PHPOffice/PHPExcel/issues/205)
+- Helper to convert basic HTML markup to a Rich Text object - @MarkBaker
+- Improved Iterators - @MarkBaker
+ - New Column Iterator
+ - Support for row and column ranges
+ - Improved handling for next/prev
+
+### Security
+
+- XML filescan in XML-based Readers to prevent XML Entity Expansion (XEE) - @MarkBaker
+ - (see http://projects.webappsec.org/w/page/13247002/XML%20Entity%20Expansion for an explanation of XEE injection) attacks
+ - Reference CVE-2015-3542 - Identification of problem courtesy of Dawid Golunski (Pentest Ltd.)
+
+## [1.8.0] - 2014-03-02
+
+### Bugfixes
+
+- Undefined variable: fileHandle in CSV Reader - @MarkBaker [CodePlex #19830](https://phpexcel.codeplex.com/workitem/19830)
+- Out of memory in style/supervisor.php - @MarkBaker [CodePlex #19968](https://phpexcel.codeplex.com/workitem/19968)
+- Style error with merged cells in PDF Writer - @MarkBaker
+- Problem with cloning worksheets - @MarkBaker
+- Bug fix reading Open Office files - @tavoarcila [#259](https://github.com/PHPOffice/PHPExcel/issues/259)
+- Serious bug in absolute cell reference used in shared formula - @MarkBaker [CodePlex #20397](https://phpexcel.codeplex.com/workitem/20397)
+ - Would also have affected insert/delete column/row- CHOOSE() returns "#VALUE!" if the 1st entry is chosen - @RomanSyroeshko [#267](https://github.com/PHPOffice/PHPExcel/issues/267)
+- When duplicating styles, styles shifted by one column to the right - @Gemorroj [#268](https://github.com/PHPOffice/PHPExcel/issues/268)
+ - Fix also applied to duplicating conditional styles- Fix for formulae that reference a sheet whose name begins with a digit: - @IndrekHaav [#212](https://github.com/PHPOffice/PHPExcel/issues/212)
+ - these were erroneously identified as numeric values, causing the parser to throw an undefined variable error.- Fixed undefined variable error due to $styleArray being used before it's initialised - @IndrekHaav [CodePlex #16208](https://phpexcel.codeplex.com/workitem/16208)
+- ISTEXT() return wrong result if referencing an empty but formatted cell - @PowerKiKi [#273](https://github.com/PHPOffice/PHPExcel/issues/273)
+- Binary comparison of strings are case insensitive - @PowerKiKi [#270](https://github.com/PHPOffice/PHPExcel/issues/270), [#31](https://github.com/PHPOffice/PHPExcel/issues/31)
+- Insert New Row/Column Before is not correctly updating formula references - @MarkBaker [#275](https://github.com/PHPOffice/PHPExcel/issues/275)
+- Passing an array of cells to _generateRow() in the HTML/PDF Writer causes caching problems with last cell in the range - @MarkBaker [#257](https://github.com/PHPOffice/PHPExcel/issues/257)
+- Fix to empty worksheet garbage collection when using cell caching - @MarkBaker [#193](https://github.com/PHPOffice/PHPExcel/issues/193)
+- Excel2007 does not correctly mark rows as hidden - @Jazzo [#248](https://github.com/PHPOffice/PHPExcel/issues/248)
+- Fixed typo in Chart/Layout set/getYMode() - @Roy Shahbazian [#299](https://github.com/PHPOffice/PHPExcel/issues/299)
+- Fatal error: Call to a member function cellExists() line: 3327 in calculation.php if referenced worksheet doesn't exist - @EliuFlorez [#279](https://github.com/PHPOffice/PHPExcel/issues/279)
+- AdvancedValueBinder "Division by zero"-error - @MarkBaker [#290](https://github.com/PHPOffice/PHPExcel/issues/290)
+- Adding Sheet to Workbook Bug - @MarkBaker [CodePlex #20604](https://phpexcel.codeplex.com/workitem/20604)
+- Calculation engine incorrectly evaluates empty cells as #VALUE - @MarkBaker [CodePlex #20703](https://phpexcel.codeplex.com/workitem/20703)
+- Formula references to cell on another sheet in ODS files - @MarkBaker [CodePlex #20760](https://phpexcel.codeplex.com/workitem/20760)
+
+### Features
+
+- LibreOffice created XLSX files results in an empty file. - @MarkBaker [#321](https://github.com/PHPOffice/PHPExcel/issues/321), [#158](https://github.com/PHPOffice/PHPExcel/issues/158), [CodePlex #17824](https://phpexcel.codeplex.com/workitem/17824)
+- Implementation of the Excel HLOOKUP() function - @amerov
+- Added "Quote Prefix" to style settings (Excel2007 Reader and Writer only) - @MarkBaker
+- Added Horizontal FILL alignment for Excel5 and Excel2007 Readers/Writers, and Horizontal DISTRIBUTED alignment for Excel2007 Reader/Writer - @MarkBaker
+- Add support for reading protected (RC4 encrypted) .xls files - @trvrnrth [#261](https://github.com/PHPOffice/PHPExcel/issues/261)
+
+### General
+
+- Adding support for macros, Ribbon in Excel 2007 - @LWol [#252](https://github.com/PHPOffice/PHPExcel/issues/252)
+- Remove array_shift in ReferenceHelper::insertNewBefore improves column or row delete speed - @cdhutch [CodePlex #20055](https://phpexcel.codeplex.com/workitem/20055)
+- Improve stock chart handling and rendering, with help from Swashata Ghosh - @MarkBaker
+- Fix to calculation properties for Excel2007 so that the opening application will only recalculate on load if it's actually required - @MarkBaker
+- Modified Excel2007 Writer to default preCalculateFormulas to false - @MarkBaker
+ - Note that autosize columns will still recalculate affected formulae internally- Functionality to getHighestRow() for a specified column, and getHighestColumn() for a specified row - @dresenhista [#242](https://github.com/PHPOffice/PHPExcel/issues/242)
+- Modify PHPExcel_Reader_Excel2007 to use zipClass from PHPExcel_Settings::getZipClass() - @adamriyadi [#247](https://github.com/PHPOffice/PHPExcel/issues/247)
+ - This allows the use of PCLZip when reading for people that don't have access to ZipArchive
+### Security
+
+- Convert properties to string in OOCalc reader - @infojunkie [#276](https://github.com/PHPOffice/PHPExcel/issues/276)
+- Disable libxml external entity loading by default. - @maartenba [#322](https://github.com/PHPOffice/PHPExcel/issues/322)
+ - This is to prevent XML External Entity Processing (XXE) injection attacks (see https://websec.io/2012/08/27/Preventing-XEE-in-PHP.html for an explanation of XXE injection).
+ - Reference CVE-2014-2054
+
+## [1.7.9] - 2013-06-02
+
+### Features
+
+- Include charts option for HTML Writer - @MarkBaker
+- Added composer file - @MarkBaker
+- cache_in_memory_gzip "eats" last worksheet line, cache_in_memory doesn't - @MarkBaker [CodePlex #18844](https://phpexcel.codeplex.com/workitem/18844)
+- echo statements in HTML.php - @MarkBaker [#104](https://github.com/PHPOffice/PHPExcel/issues/104)
+
+### Bugfixes
+
+- Added getStyle() method to Cell object - @MarkBaker
+- Error in PHPEXCEL/Calculation.php script on line 2976 (stack pop check) - @Asker [CodePlex #18777](https://phpexcel.codeplex.com/workitem/18777)
+- CSV files without a file extension being identified as HTML - @MarkBaker [CodePlex #18794](https://phpexcel.codeplex.com/workitem/18794)
+- Wrong check for maximum number of rows in Excel5 Writer - @AndreKR [#66](https://github.com/PHPOffice/PHPExcel/issues/66)
+- Cache directory for DiscISAM cache storage cannot be set - @MarkBaker [#67](https://github.com/PHPOffice/PHPExcel/issues/67)
+- Fix to Excel2007 Reader for hyperlinks with an anchor fragment (following a #), otherwise they were treated as sheet references - @MarkBaker [CodePlex #17976](https://phpexcel.codeplex.com/workitem/17976)
+- getSheetNames() fails on numeric (floating point style) names with trailing zeroes - @MarkBaker [CodePlex #18963](https://phpexcel.codeplex.com/workitem/18963)
+- Modify cell's getCalculatedValue() method to return the content of RichText objects rather than the RichText object itself - @MarkBaker
+- Fixed formula/formatting bug when removing rows - @techhead [#70](https://github.com/PHPOffice/PHPExcel/issues/70)
+- Fix to cellExists for non-existent namedRanges - @alexgann [#63](https://github.com/PHPOffice/PHPExcel/issues/63)
+- Sheet View in Excel5 Writer - @Progi1984 [#22](https://github.com/PHPOffice/PHPExcel/issues/22)
+- PHPExcel_Worksheet::getCellCollection() may not return last cached cell - @amironov [#82](https://github.com/PHPOffice/PHPExcel/issues/82)
+- Rich Text containing UTF-8 characters creating unreadable content with Excel5 Writer - @teso [CodePlex #18551](https://phpexcel.codeplex.com/workitem/18551)
+- Work item GH-8/CP11704 : Conditional formatting in Excel 5 Writer - @Progi1984
+- canRead() Error for GoogleDocs ODS files: in ODS files from Google Docs there is no mimetype file - @MarkBaker [#113](https://github.com/PHPOffice/PHPExcel/issues/113)
+- "Sheet index is out of bounds." Exception - @MarkBaker [#80](https://github.com/PHPOffice/PHPExcel/issues/80)
+- Fixed number format fatal error - @ccorliss [#105](https://github.com/PHPOffice/PHPExcel/issues/105)
+- Add DROP TABLE in destructor for SQLite and SQLite3 cache controllers - @MarkBaker
+- Fix merged-cell borders on HTML/PDF output - @alexgann [#154](https://github.com/PHPOffice/PHPExcel/issues/154)
+- Fix: Hyperlinks break when removing rows - @Shanto [#161](https://github.com/PHPOffice/PHPExcel/issues/161)
+- Fix Extra Table Row From Images and Charts - @neclimdul [#166](https://github.com/PHPOffice/PHPExcel/issues/166)
+
+### General
+
+- Single cell print area - @MarkBaker [#130](https://github.com/PHPOffice/PHPExcel/issues/130)
+- Improved AdvancedValueBinder for currency - @kea [#69](https://github.com/PHPOffice/PHPExcel/issues/69)
+- Fix for environments where there is no access to /tmp but to upload_tmp_dir - @MarkBaker
+ - Provided an option to set the sys_get_temp_dir() call to use the upload_tmp_dir; though by default the standard temp directory will still be used- Search style by identity in PHPExcel_Worksheet::duplicateStyle() - @amironov [#84](https://github.com/PHPOffice/PHPExcel/issues/84)
+- Fill SheetView IO in Excel5 - @karak [#85](https://github.com/PHPOffice/PHPExcel/issues/85)
+- Memory and Speed improvements in PHPExcel_Reader_Excel5 - @cfhay [CodePlex #18958](https://phpexcel.codeplex.com/workitem/18958)
+- Modify listWorksheetNames() and listWorksheetInfo to use XMLReader with streamed XML rather than SimpleXML - @MarkBaker [#78](https://github.com/PHPOffice/PHPExcel/issues/78)
+- Restructuring of PHPExcel Exceptions - @dbonsch
+- Refactor Calculation Engine from singleton to a Multiton - @MarkBaker
+ - Ensures that calculation cache is maintained independently for different workbooks
+
+## [1.7.8] - 2012-10-12
+
+### Features
+
+- Phar builder script to add phar file as a distribution option - @kkamkou
+- Refactor PDF Writer to allow use with a choice of PDF Rendering library - @MarkBaker
+ - rather than restricting to tcPDF
+ - Current options are tcPDF, mPDF, DomPDF
+ - tcPDF Library has now been removed from the deployment bundle- Initial version of HTML Reader - @MarkBaker
+- Implement support for AutoFilter in PHPExcel_Writer_Excel5 - @Progi1984
+- Modified ERF and ERFC Engineering functions to accept Excel 2010's modified acceptance of negative arguments - @MarkBaker
+- Support SheetView `view` attribute (Excel2007) - @k1LoW
+- Excel compatibility option added for writing CSV files - @MarkBaker
+ - While Excel 2010 can read CSV files with a simple UTF-8 BOM, Excel2007 and earlier require UTF-16LE encoded tab-separated files.
+ - The new setExcelCompatibility(TRUE) option for the CSV Writer will generate files with this formatting for easy import into Excel2007 and below.- Language implementations for Turkish (tr) - @MarkBaker
+- Added fraction tests to advanced value binder - @MarkBaker
+
+### General
+
+- Allow call to font setUnderline() for underline format to specify a simple boolean for UNDERLINE_NONE or UNDERLINE_SINGLE - @MarkBaker
+- Add Currency detection to the Advanced Value Binder - @alexgann
+- setCellValueExplicitByColumnAndRow() do not return PHPExcel_Worksheet - @MarkBaker [CodePlex #18404](https://phpexcel.codeplex.com/workitem/18404)
+- Reader factory doesn't read anymore XLTX and XLT files - @MarkBaker [CodePlex #18324](https://phpexcel.codeplex.com/workitem/18324)
+- Magic __toString() method added to Cell object to return raw data value as a string - @MarkBaker
+- Add cell indent to html rendering - @alexgann
+
+### Bugfixes
+
+- ZeroHeight for rows in sheet format - @Raghav1981
+- OOCalc cells containing inside the tag - @cyberconte
+- Fix to listWorksheetInfo() method for OOCalc Reader - @schir1964
+- Support for "e" (epoch) date format mask - @MarkBaker
+ - Rendered as a 4-digit CE year in non-Excel outputs- Background color cell is always black when editing cell - @MarkBaker
+- Allow "no impact" to formats on Conditional Formatting - @MarkBaker
+- OOCalc Reader fix for NULL cells - @wackonline
+- Fix to excel2007 Chart Writer when a $plotSeriesValues is empty - @seltzlab
+- Various fixes to Chart handling - @MarkBaker
+- Error loading xlsx file with column breaks - @MarkBaker [CodePlex #18370](https://phpexcel.codeplex.com/workitem/18370)
+- OOCalc Reader now handles percentage and currency data types - @MarkBaker
+- mb_stripos empty delimiter - @MarkBaker
+- getNestingLevel() Error on Excel5 Read - @takaakik
+- Fix to Excel5 Reader when cell annotations are defined before their referenced text objects - @MarkBaker
+- OOCalc Reader modified to process number-rows-repeated - @MarkBaker
+- Chart Title compatibility on Excel 2007 - @MarkBaker [CodePlex #18377](https://phpexcel.codeplex.com/workitem/18377)
+- Chart Refresh returning cell reference rather than values - @MarkBaker [CodePlex #18146](https://phpexcel.codeplex.com/workitem/18146)
+- Autoshape being identified in twoCellAnchor when includeCharts is TRUE triggering load error - @MarkBaker [CodePlex #18145](https://phpexcel.codeplex.com/workitem/18145)
+- v-type texts for series labels now recognised and parsed correctly - @MarkBaker [CodePlex #18325](https://phpexcel.codeplex.com/workitem/18325)
+- load file failed if the file has no extensionType - @wolf5x [CodePlex #18492](https://phpexcel.codeplex.com/workitem/18492)
+- Pattern fill colours in Excel2007 Style Writer - @dverspui
+- Excel2007 Writer order of font style elements to conform with Excel2003 using compatibility pack - @MarkBaker
+- Problems with $_activeSheetIndex when decreased below 0. - @MarkBaker [CodePlex #18425](https://phpexcel.codeplex.com/workitem/18425)
+- PHPExcel_CachedObjectStorage_SQLite3::cacheMethodIsAvailable() uses class_exists - autoloader throws error - @MarkBaker [CodePlex #18597](https://phpexcel.codeplex.com/workitem/18597)
+- Cannot access private property PHPExcel_CachedObjectStorageFactory::$_cacheStorageMethod - @MarkBaker [CodePlex #18598](https://phpexcel.codeplex.com/workitem/18598)
+- Data titles for charts - @MarkBaker [CodePlex #18397](https://phpexcel.codeplex.com/workitem/18397)
+ - PHPExcel_Chart_Layout now has methods for getting/setting switches for displaying/hiding chart data labels- Discard single cell merge ranges when reading (stupid that Excel allows them in the first place) - @MarkBaker
+- Discard hidden autoFilter named ranges - @MarkBaker
+
+## [1.7.7] - 2012-05-19
+
+### Bugfixes
+
+- Support for Rich-Text in PHPExcel_Writer_Excel5 - @Progi1984 [CodePlex #8916](https://phpexcel.codeplex.com/workitem/8916)
+- Change iterators to implement Iterator rather than extend CachingIterator, as a fix for PHP 5.4. changes in SPL - @MarkBaker
+- Invalid cell coordinate in Autofilter for Excel2007 Writer - @MarkBaker [CodePlex #15459](https://phpexcel.codeplex.com/workitem/15459)
+- PCLZip library issue - @MarkBaker [CodePlex #15518](https://phpexcel.codeplex.com/workitem/15518)
+- Excel2007 Reader canRead function bug - @MarkBaker [CodePlex #15537](https://phpexcel.codeplex.com/workitem/15537)
+- Support for Excel functions whose return can be used as either a value or as a cell reference depending on its context within a formula - @MarkBaker
+- ini_set() call in Calculation class destructor - @gilles06 [CodePlex #15707](https://phpexcel.codeplex.com/workitem/15707)
+- RangeToArray strange array keys - @MarkBaker [CodePlex #15786](https://phpexcel.codeplex.com/workitem/15786)
+- INDIRECT() function doesn't work with named ranges - @MarkBaker [CodePlex #15762](https://phpexcel.codeplex.com/workitem/15762)
+- Locale-specific fix to text functions when passing a boolean argument instead of a string - @MarkBaker
+- reader/CSV fails on this file - @MarkBaker [CodePlex #16246](https://phpexcel.codeplex.com/workitem/16246)
+ - auto_detect_line_endings now set in CSV reader- $arguments improperly used in CachedObjectStorage/PHPTemp.php - @MarkBaker [CodePlex #16212](https://phpexcel.codeplex.com/workitem/16212)
+- Bug In Cache System (cell reference when throwing caching errors) - @MarkBaker [CodePlex #16643](https://phpexcel.codeplex.com/workitem/16643)
+- PHP Invalid index notice on writing excel file when active sheet has been deleted - @MarkBaker [CodePlex #16895](https://phpexcel.codeplex.com/workitem/16895)
+- External links in Excel2010 files cause Fatal error - @MarkBaker [CodePlex #16956](https://phpexcel.codeplex.com/workitem/16956)
+- Previous calculation engine error conditions trigger cyclic reference errors - @MarkBaker [CodePlex #16960](https://phpexcel.codeplex.com/workitem/16960)
+- PHPExcel_Style::applyFromArray() returns null rather than style object in advanced mode - @mkopinsky [CodePlex #16266](https://phpexcel.codeplex.com/workitem/16266)
+- Cell::getFormattedValue returns RichText object instead of string - @fauvel [CodePlex #16958](https://phpexcel.codeplex.com/workitem/16958)
+- Indexed colors do not refer to Excel's indexed colors? - @MarkBaker [CodePlex #17166](https://phpexcel.codeplex.com/workitem/17166)
+- Indexed colors should be consistent with Excel and start from 1 (current index starts at 0) - @MarkBaker [CodePlex #17199](https://phpexcel.codeplex.com/workitem/17199)
+- Named Range definition in .xls when sheet reeference is quote wrapped - @MarkBaker [CodePlex #17262](https://phpexcel.codeplex.com/workitem/17262)
+- duplicateStyle() method doesn't duplicate conditional formats - @MarkBaker [CodePlex #17403](https://phpexcel.codeplex.com/workitem/17403)
+ - Added an equivalent duplicateConditionalStyle() method for duplicating conditional styles- =sumproduct(A,B) <> =sumproduct(B,A) in xlsx - @bnr [CodePlex #17501](https://phpexcel.codeplex.com/workitem/17501)
+
+### Features
+
+- OOCalc cells contain same data bug? - @cyberconte [CodePlex #17471](https://phpexcel.codeplex.com/workitem/17471)
+- listWorksheetInfo() method added to Readers... courtesy of Christopher Mullins - @schir1964
+- Options for cell caching using Igbinary and SQLite/SQlite3. - @MarkBaker
+- Additional row iterator options: allow a start row to be defined in the constructor; seek(), and prev() methods added. - @MarkBaker
+- Implement document properties in Excel5 writer - @Progi1984 [CodePlex #9759](https://phpexcel.codeplex.com/workitem/9759)
+
+### General
+
+- Implement chart functionality (EXPERIMENTAL) - @MarkBaker [CodePlex #16](https://phpexcel.codeplex.com/workitem/16)
+ - Initial definition of chart objects.
+ - Reading Chart definitions through the Excel2007 Reader
+ - Facility to render charts to images using the 3rd-party jpgraph library
+ - Writing Charts using the Excel2007 Writer- Fix to build to ensure that Examples are included with the documentation - @MarkBaker
+- Reduce cell caching overhead using dirty flag to ensure that cells are only rewritten to the cache if they have actually been changed - @MarkBaker
+- Improved memory usage in CSV Writer - @MarkBaker
+- Improved speed and memory usage in Excel5 Writer - @MarkBaker
+- Experimental - @MarkBaker
+ - Added getHighestDataColumn(), getHighestDataRow(), getHighestRowAndColumn() and calculateWorksheetDataDimension() methods for the worksheet that return the highest row and column that have cell records- Support for Rich-Text in PHPExcel_Writer_Excel5 - @Progi1984 [CodePlex #8916](https://phpexcel.codeplex.com/workitem/8916)
+- Two easy to fix Issues concerning PHPExcel_Token_Stack (l10n/UC) - @MarkBaker [CodePlex #15405](https://phpexcel.codeplex.com/workitem/15405)
+- Locale file paths not fit for windows - @MarkBaker [CodePlex #15461](https://phpexcel.codeplex.com/workitem/15461)
+- Add file directory as a cache option for cache_to_discISAM - @MarkBaker [CodePlex #16643](https://phpexcel.codeplex.com/workitem/16643)
+- Datatype.php & constant TYPE_NULL - @MarkBaker [CodePlex #16923](https://phpexcel.codeplex.com/workitem/16923)
+- Ensure use of system temp directory for all temporary work files, unless explicitly specified - @MarkBaker
+- [Patch] faster stringFromColumnIndex() - @char101 [CodePlex #16359](https://phpexcel.codeplex.com/workitem/16359)
+- Fix for projects that still use old autoloaders - @whit1206 [CodePlex #16028](https://phpexcel.codeplex.com/workitem/16028)
+- Unknown codepage: 10007 - @atz [CodePlex #17024](https://phpexcel.codeplex.com/workitem/17024)
+ - Additional Mac codepages
+
+## [1.7.6] - 2011-02-27
+
+### Features
+
+- Provide option to use PCLZip as an alternative to ZipArchive. - @MarkBaker
+ - This allows the writing of Excel2007 files, even without ZipArchive enabled (it does require zlib), or when php_zip is one of the buggy PHP 5.2.6 or 5.2.8 versions
+ - It can be enabled using PHPExcel_Settings::setZipClass(PHPExcel_Settings::PCLZIP);
+ - Note that it is not yet implemented as an alternative to ZipArchive for those Readers that are extracting from zips- Added listWorksheetNames() method to Readers that support multiple worksheets in a workbook, allowing a user to extract a list of all the worksheet names from a file without parsing/loading the whole file. - @MarkBaker [CodePlex #14979](https://phpexcel.codeplex.com/workitem/14979)
+- Speed boost and memory reduction in the Worksheet toArray() method. - @MarkBaker
+- Added new rangeToArray() and namedRangeToArray() methods to the PHPExcel_Worksheet object. - @MarkBaker
+ - Functionally, these are identical to the toArray() method, except that they take an additional first parameter of a Range (e.g. 'B2:C3') or a Named Range name.
+ - Modified the toArray() method so that it actually uses rangeToArray().- Added support for cell comments in the OOCalc, Gnumeric and Excel2003XML Readers, and in the Excel5 Reader - @MarkBaker
+- Improved toFormattedString() handling for Currency and Accounting formats to render currency symbols - @MarkBaker
+
+### Bugfixes
+
+- Implement more Excel calculation functions - @MarkBaker
+ - Implemented the DAVERAGE(), DCOUNT(), DCOUNTA(), DGET(), DMAX(), DMIN(), DPRODUCT(), DSTDEV(), DSTDEVP(), DSUM(), DVAR() and DVARP() Database functions- Simple =IF() formula disappears - @MarkBaker [CodePlex #14888](https://phpexcel.codeplex.com/workitem/14888)
+- PHP Warning: preg_match(): Compilation failed: PCRE does not support \\L, \\l, \\N, \\P, \\p, \\U, \\u, or \\X - @MarkBaker [CodePlex #14898](https://phpexcel.codeplex.com/workitem/14898)
+- VLOOKUP choking on parameters in PHPExcel.1.7.5/PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #14901](https://phpexcel.codeplex.com/workitem/14901)
+- PHPExcel_Cell::isInRange() incorrect results - offset by one column - @MarkBaker [CodePlex #14973](https://phpexcel.codeplex.com/workitem/14973)
+- Treat CodePage of 0 as CP1251 (for .xls files written by applications that don't set the CodePage correctly, such as Apple Numbers) - @MarkBaker
+- Need method for removing autoFilter - @MarkBaker [CodePlex #11583](https://phpexcel.codeplex.com/workitem/11583)
+- coordinateFromString throws exception for rows greater than 99,999 - @MarkBaker [CodePlex #15029](https://phpexcel.codeplex.com/workitem/15029)
+- PHPExcel Excel2007 Reader colour problems with solidfill - @MarkBaker [CodePlex #14999](https://phpexcel.codeplex.com/workitem/14999)
+- Formatting get lost and edit a template XLSX file - @MarkBaker [CodePlex #13215](https://phpexcel.codeplex.com/workitem/13215)
+- Excel 2007 Reader /writer lost fontcolor - @MarkBaker [CodePlex #14029](https://phpexcel.codeplex.com/workitem/14029)
+- file that makes cells go black - @MarkBaker [CodePlex #13374](https://phpexcel.codeplex.com/workitem/13374)
+- Minor patchfix for Excel2003XML Reader when XML is defined with a charset attribute - @MarkBaker
+- PHPExcel_Worksheet->toArray() index problem - @MarkBaker [CodePlex #15089](https://phpexcel.codeplex.com/workitem/15089)
+- Merge cells 'un-merge' when using an existing spreadsheet - @MarkBaker [CodePlex #15094](https://phpexcel.codeplex.com/workitem/15094)
+- Worksheet fromArray() only working with 2-D arrays - @MarkBaker [CodePlex #15129](https://phpexcel.codeplex.com/workitem/15129)
+- rangeToarray function modified for non-existent cells - @xkeshav [CodePlex #15172](https://phpexcel.codeplex.com/workitem/15172)
+- Images not getting copyied with the ->clone function - @MarkBaker [CodePlex #14980](https://phpexcel.codeplex.com/workitem/14980)
+- AdvancedValueBinder.php: String sometimes becomes a date when it shouldn't - @MarkBaker [CodePlex #11576](https://phpexcel.codeplex.com/workitem/11576)
+- Fix Excel5 Writer so that it only writes column dimensions for columns that are actually used rather than the full range (A to IV) - @MarkBaker
+- FreezePane causing damaged or modified error - @MarkBaker [CodePlex #15198](https://phpexcel.codeplex.com/workitem/15198)
+ - The freezePaneByColumnAndRow() method row argument should default to 1 rather than 0.
+ - Default row argument for all __ByColumnAndRow() methods should be 1- Column reference rather than cell reference in Print Area definition - @MarkBaker [CodePlex #15121](https://phpexcel.codeplex.com/workitem/15121)
+ - Fix Excel2007 Writer to handle print areas that are defined as row or column ranges rather than just as cell ranges- Reduced false positives from isDateTimeFormatCode() method by suppressing testing within quoted strings - @MarkBaker
+- Caching and tmp partition exhaustion - @MarkBaker [CodePlex #15312](https://phpexcel.codeplex.com/workitem/15312)
+- Writing to Variable No Longer Works. $_tmp_dir Missing in PHPExcel\PHPExcel\Shared\OLE\PPS\Root.php - @MarkBaker [CodePlex #15308](https://phpexcel.codeplex.com/workitem/15308)
+- Named ranges with dot don't get parsed properly - @MarkBaker [CodePlex #15379](https://phpexcel.codeplex.com/workitem/15379)
+- insertNewRowBefore fails to consistently update references - @MarkBaker [CodePlex #15096](https://phpexcel.codeplex.com/workitem/15096)
+- "i" is not a valid character for Excel date format masks (in isDateTimeFormatCode() method) - @MarkBaker
+- PHPExcel_ReferenceHelper::insertNewBefore() is missing an 'Update worksheet: comments' section - @MKunert [CodePlex #15421](https://phpexcel.codeplex.com/workitem/15421)
+
+### General
+
+- Full column/row references in named ranges not supported by updateCellReference() - @MarkBaker [CodePlex #15409](https://phpexcel.codeplex.com/workitem/15409)
+- Improved performance (speed), for building the Shared Strings table in the Excel2007 Writer. - @MarkBaker
+- Improved performance (speed), for PHP to Excel date conversions - @MarkBaker
+- Enhanced SheetViews element structures in the Excel2007 Writer for frozen panes. - @MarkBaker
+- Removed Serialized Reader/Writer as these no longer work. - @MarkBaker
+
+## [1.7.5] - 2010-12-10
+
+### Features
+
+- Implement Gnumeric File Format - @MarkBaker [CodePlex #8769](https://phpexcel.codeplex.com/workitem/8769)
+ - Initial work on Gnumeric Reader (Worksheet Data, Document Properties and basic Formatting)- Support for Extended Workbook Properties in Excel2007, Excel5 and OOCalc Readers; support for User-defined Workbook Properties in Excel2007 and OOCalc Readers - @MarkBaker
+- Support for Extended and User-defined Workbook Properties in Excel2007 Writer - @MarkBaker
+- Provided a setGenerateSheetNavigationBlock(false); option to suppress generation of the sheet navigation block when writing multiple worksheets to HTML - @MarkBaker
+- Advanced Value Binder now recognises TRUE/FALSE strings (locale-specific) and converts to boolean - @MarkBaker
+- PHPExcel_Worksheet->toArray() is returning truncated values - @MarkBaker [CodePlex #14301](https://phpexcel.codeplex.com/workitem/14301)
+- Configure PDF Writer margins based on Excel Worksheet Margin Settings value - @MarkBaker
+- Added Contiguous flag for the CSV Reader, when working with Read Filters - @MarkBaker
+- Added getFormattedValue() method for cell object - @MarkBaker
+- Added strictNullComparison argument to the worksheet fromArray() method - @MarkBaker
+
+### Bugfixes
+
+- Fix to toFormattedString() method in PHPExcel_Style_NumberFormat to handle fractions with a # code for the integer part - @MarkBaker
+- NA() doesn't propagate in matrix calc - quick fix in JAMA/Matrix.php - @MarkBaker [CodePlex #14143](https://phpexcel.codeplex.com/workitem/14143)
+- Excel5 : Formula : String constant containing double quotation mark - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895)
+- Excel5 : Formula : Percent - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895)
+- Excel5 : Formula : Error constant - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895)
+- Excel5 : Formula : Concatenation operator - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895)
+- Worksheet clone broken for CachedObjectStorage_Memory - @MarkBaker [CodePlex #14146](https://phpexcel.codeplex.com/workitem/14146)
+- PHPExcel_Reader_Excel2007 fails when gradient fill without type is present in a file - @MarkBaker [CodePlex #12998](https://phpexcel.codeplex.com/workitem/12998)
+- @ format for numeric strings in XLSX to CSV conversion - @MarkBaker [CodePlex #14176](https://phpexcel.codeplex.com/workitem/14176)
+- Advanced Value Binder Not Working? - @MarkBaker [CodePlex #14223](https://phpexcel.codeplex.com/workitem/14223)
+- unassigned object variable in PHPExcel->removeCellXfByIndex - @MarkBaker [CodePlex #14226](https://phpexcel.codeplex.com/workitem/14226)
+- problem with getting cell values from another worksheet... (if cell doesn't exist) - @MarkBaker [CodePlex #14236](https://phpexcel.codeplex.com/workitem/14236)
+- Setting cell values to one char strings & Trouble reading one character string (thanks gorfou) - @MarkBaker
+- Worksheet title exception when duplicate worksheet is being renamed but exceeds the 31 character limit - @MarkBaker [CodePlex #14256](https://phpexcel.codeplex.com/workitem/14256)
+- Named range with sheet name that contains the $ throws exception when getting the cell - @MarkBaker [CodePlex #14086](https://phpexcel.codeplex.com/workitem/14086)
+- Added autoloader to DefaultValueBinder and AdvancedValueBinder - @MarkBaker
+- Modified PHPExcel_Shared_Date::isDateTimeFormatCode() to return false if format code begins with "_" or with "0 " to prevent false positives - @MarkBaker
+ - These leading characters are most commonly associated with number, currency or accounting (or occasionally fraction) formats- BUG : Excel5 and setReadFilter ? - @MarkBaker [CodePlex #14374](https://phpexcel.codeplex.com/workitem/14374)
+- Wrong exception message while deleting column - @MarkBaker [CodePlex #14425](https://phpexcel.codeplex.com/workitem/14425)
+- Formula evaluation fails with Japanese sheet refs - @MarkBaker [CodePlex #14679](https://phpexcel.codeplex.com/workitem/14679)
+- PHPExcel_Writer_PDF does not handle cell borders correctly - @MarkBaker [CodePlex #13559](https://phpexcel.codeplex.com/workitem/13559)
+- Style : applyFromArray() for 'allborders' not working - @MarkBaker [CodePlex #14831](https://phpexcel.codeplex.com/workitem/14831)
+
+### General
+
+- Using $this when not in object context in Excel5 Reader - @MarkBaker [CodePlex #14837](https://phpexcel.codeplex.com/workitem/14837)
+- Removes a unnecessary loop through each cell when applying conditional formatting to a range. - @MarkBaker
+- Removed spurious PHP end tags (?>) - @MarkBaker
+- Improved performance (speed) and reduced memory overheads, particularly for the Writers, but across the whole library. - @MarkBaker
+
+## [1.7.4] - 2010-08-26
+
+### Bugfixes
+
+- Excel5 : Formula : Power - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895)
+- Excel5 : Formula : Unary plus - @Progi1984 [CodePlex #7895](https://phpexcel.codeplex.com/workitem/7895)
+- Excel5 : Just write the Escher stream if necessary in Worksheet - @Progi1984
+- Syntax errors in memcache.php 1.7.3c - @MarkBaker [CodePlex #13433](https://phpexcel.codeplex.com/workitem/13433)
+- Support for row or column ranges in the calculation engine, e.g. =SUM(C:C) or =SUM(1:2) - @MarkBaker
+ - Also support in the calculation engine for absolute row or column ranges e.g. =SUM($C:$E) or =SUM($3:5)- Picture problem with Excel 2003 - @Erik Tilt [CodePlex #13455](https://phpexcel.codeplex.com/workitem/13455)
+- Wrong variable used in addExternalSheet in PHPExcel.php - @MarkBaker [CodePlex #13484](https://phpexcel.codeplex.com/workitem/13484)
+- "Invalid cell coordinate" error when formula access data from an other sheet - @MarkBaker [CodePlex #13515](https://phpexcel.codeplex.com/workitem/13515)
+- (related to Work item 13515) Calculation engine confusing cell range worksheet when referencing cells in a different worksheet to the formula - @MarkBaker
+- Wrong var naming in Worksheet->garbageCollect() - @MarkBaker [CodePlex #13752](https://phpexcel.codeplex.com/workitem/13752)
+- PHPExcel_Style_*::__clone() methods cause cloning loops? - @MarkBaker [CodePlex #13764](https://phpexcel.codeplex.com/workitem/13764)
+- Recent builds causing problems loading xlsx files? (ZipArchive issue?) - @MarkBaker [CodePlex #11488](https://phpexcel.codeplex.com/workitem/11488)
+- cache_to_apc causes fatal error when processing large data sets - @MarkBaker [CodePlex #13856](https://phpexcel.codeplex.com/workitem/13856)
+- OOCalc reader misses first line if it's a 'table-header-row' - @MarkBaker [CodePlex #13880](https://phpexcel.codeplex.com/workitem/13880)
+- using cache with copy or clone bug? - @MarkBaker [CodePlex #14011](https://phpexcel.codeplex.com/workitem/14011)
+ - Fixed $worksheet->copy() or clone $worksheet when using cache_in_memory, cache_in_memory_gzip, cache_in_memory_serialized, cache_to_discISAM, cache_to_phpTemp, cache_to_apc and cache_to_memcache;
+ - Fixed but untested when using cache_to_wincache.
+### Features
+
+- Standard Deviation functions returning DIV/0 Error when Standard Deviation is zero - @MarkBaker [CodePlex #13450](https://phpexcel.codeplex.com/workitem/13450)
+- Support for print area with several ranges in the Excel2007 reader, and improved features for editing print area with several ranges - @MarkBaker
+- Improved Cell Exception Reporting - @MarkBaker [CodePlex #13769](https://phpexcel.codeplex.com/workitem/13769)
+
+### General
+
+- Fixed problems with reading Excel2007 Properties - @MarkBaker
+- PHP Strict Standards: Non-static method PHPExcel_Shared_String::utf16_decode() should not be called statically - @MarkBaker
+- Array functions were ignored when loading an existing file containing them, and as a result, they would lose their 'cse' status. - @MarkBaker
+- Minor memory tweaks to Excel2007 Writer - @MarkBaker
+- Modified ReferenceHelper updateFormulaReferences() method to handle updates to row and column cell ranges (including absolute references e.g. =SUM(A:$E) or =SUM($5:5), and range/cell references that reference a worksheet by name), and to provide both performance and memory improvements. - @MarkBaker
+- Modified Excel2007 Reader so that ReferenceHelper class is instantiated only once rather than for every shared formula in a workbook. - @MarkBaker
+- Correct handling for additional (synonym) formula tokens in Excel5 Reader - @MarkBaker
+- Additional reading of some Excel2007 Extended Properties (Company, Manager) - @MarkBaker
+
+## [1.7.3c] - 2010-06-01
+
+### Bugfixes
+
+- Fatal error: Class 'ZipArchive' not found... ...Reader/Excel2007.php on line 217 - @MarkBaker [CodePlex #13012](https://phpexcel.codeplex.com/workitem/13012)
+- PHPExcel_Writer_Excel2007 error after 1.7.3b - @MarkBaker [CodePlex #13398](https://phpexcel.codeplex.com/workitem/13398)
+
+## [1.7.3b] - 2010-05-31
+
+### Bugfixes
+
+- Infinite loop when reading - @MarkBaker [CodePlex #12903](https://phpexcel.codeplex.com/workitem/12903)
+- Wrong method chaining on PHPExcel_Worksheet class - @MarkBaker [CodePlex #13381](https://phpexcel.codeplex.com/workitem/13381)
+
+## [1.7.3] - 2010-05-17
+
+### General
+
+- Applied patch 4990 (modified) - @Erik Tilt
+- Applied patch 5568 (modified) - @MarkBaker
+- Applied patch 5943 - @MarkBaker
+- Upgrade build script to use Phing - @MarkBaker [CodePlex #13042](https://phpexcel.codeplex.com/workitem/13042)
+- Replacing var with public/private - @Erik Tilt [CodePlex #11586](https://phpexcel.codeplex.com/workitem/11586)
+- Applied Anthony's Sterling's Class Autoloader to reduce memory overhead by "Lazy Loading" of classes - @MarkBaker
+- Modification to functions that accept a date parameter to support string values containing ordinals as per Excel (English language only) - @MarkBaker
+- Modify PHPExcel_Style_NumberFormat::toFormattedString() to handle dates that fall outside of PHP's 32-bit date range - @MarkBaker
+- Applied patch 5207 - @MarkBaker
+
+### Features
+
+- PHPExcel developer documentation: Set page margins - @Erik Tilt [CodePlex #11970](https://phpexcel.codeplex.com/workitem/11970)
+- Special characters and accents in SYLK reader - @Erik Tilt [CodePlex #11038](https://phpexcel.codeplex.com/workitem/11038)
+- Implement more Excel calculation functions - @MarkBaker
+ - Implemented the COUPDAYS(), COUPDAYBS(), COUPDAYSNC(), COUPNCD(), COUPPCD() and PRICE() Financial functions
+ - Implemented the N() and TYPE() Information functions
+ - Implemented the HYPERLINK() Lookup and Reference function- Horizontal page break support in PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #11526](https://phpexcel.codeplex.com/workitem/11526)
+- Introduce method setActiveSheetIndexByName() - @Erik Tilt [CodePlex #11529](https://phpexcel.codeplex.com/workitem/11529)
+- AdvancedValueBinder.php: Automatically wrap text when there is new line in string (ALT+"Enter") - @Erik Tilt [CodePlex #11550](https://phpexcel.codeplex.com/workitem/11550)
+- Data validation support in PHPExcel_Reader_Excel5 and PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10300](https://phpexcel.codeplex.com/workitem/10300)
+- Improve autosize calculation - @MarkBaker [CodePlex #11616](https://phpexcel.codeplex.com/workitem/11616)
+- Methods to translate locale-specific function names in formulae - @MarkBaker
+ - Language implementations for Czech (cs), Danish (da), German (de), English (uk), Spanish (es), Finnish (fi), French (fr), Hungarian (hu), Italian (it), Dutch (nl), Norwegian (no), Polish (pl), Portuguese (pt), Brazilian Portuguese (pt_br), Russian (ru) and Swedish (sv)- Implement document properties in Excel5 reader/writer - @Erik Tilt [CodePlex #9759](https://phpexcel.codeplex.com/workitem/9759)
+ - Fixed so far for PHPExcel_Reader_Excel5- Show/hide row and column headers in worksheet - @Erik Tilt [CodePlex #11849](https://phpexcel.codeplex.com/workitem/11849)
+- Can't set font on writing PDF (by key) - @Erik Tilt [CodePlex #11919](https://phpexcel.codeplex.com/workitem/11919)
+- Thousands scale (1000^n) support in PHPExcel_Style_NumberFormat::toFormattedString - @Erik Tilt [CodePlex #12096](https://phpexcel.codeplex.com/workitem/12096)
+- Implement repeating rows in PDF and HTML writer - @Erik Tilt
+- Sheet tabs in PHPExcel_Writer_HTML - @Erik Tilt [CodePlex #12289](https://phpexcel.codeplex.com/workitem/12289)
+- Add Wincache CachedObjectProvider - @MarkBaker [CodePlex #13041](https://phpexcel.codeplex.com/workitem/13041)
+- Configure PDF Writer paper size based on Excel Page Settings value, and provided methods to override paper size and page orientation with the writer - @MarkBaker
+ - Note PHPExcel defaults to Letter size, while the previous PDF writer enforced A4 size, so PDF writer will now default to Letter- Initial implementation of cell caching: allowing larger workbooks to be managed, but at a cost in speed - @MarkBaker
+
+### Bugfixes
+
+- Added an identify() method to the IO Factory that identifies the reader which will be used to load a particular file without actually loading it. - @MarkBaker
+- Warning messages with INDEX function having 2 arguments - @MarkBaker [CodePlex #10979](https://phpexcel.codeplex.com/workitem/10979)
+- setValue('=') should result in string instead of formula - @Erik Tilt [CodePlex #11473](https://phpexcel.codeplex.com/workitem/11473)
+- method _raiseFormulaError should no be private - @MarkBaker [CodePlex #11471](https://phpexcel.codeplex.com/workitem/11471)
+- Fatal error: Call to undefined function mb_substr() in ...Classes\PHPExcel\Reader\Excel5.php on line 2903 - @Erik Tilt [CodePlex #11485](https://phpexcel.codeplex.com/workitem/11485)
+- getBold(), getItallic(), getStrikeThrough() not always working with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #11487](https://phpexcel.codeplex.com/workitem/11487)
+- AdvancedValueBinder.php not working correctly for $cell->setValue('hh:mm:ss') - @Erik Tilt [CodePlex #11492](https://phpexcel.codeplex.com/workitem/11492)
+- Fixed leap year handling for the YEARFRAC() Date/Time function when basis ia 1 (Actual/actual) - @MarkBaker
+- Warning messages - @MarkBaker [CodePlex #11490](https://phpexcel.codeplex.com/workitem/11490)
+ - Calculation Engine code modified to enforce strict standards for pass by reference- PHPExcel_Cell_AdvancedValueBinder doesnt work for dates in far future - @Erik Tilt [CodePlex #11483](https://phpexcel.codeplex.com/workitem/11483)
+- MSODRAWING bug with long CONTINUE record in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #11528](https://phpexcel.codeplex.com/workitem/11528)
+- PHPExcel_Reader_Excel2007 reads print titles as named range when there is more than one sheet - @Erik Tilt [CodePlex #11571](https://phpexcel.codeplex.com/workitem/11571)
+- missing @return in phpdocblock in reader classes - @Erik Tilt [CodePlex #11561](https://phpexcel.codeplex.com/workitem/11561)
+- AdvancedValueBinder.php: String sometimes becomes a date when it shouldn't - @Erik Tilt [CodePlex #11576](https://phpexcel.codeplex.com/workitem/11576)
+- Small numbers escape treatment in PHPExcel_Style_NumberFormat::toFormattedString() - @Erik Tilt [CodePlex #11588](https://phpexcel.codeplex.com/workitem/11588)
+- Blank styled cells are not blank in output by HTML writer due to - @Erik Tilt [CodePlex #11590](https://phpexcel.codeplex.com/workitem/11590)
+- Calculation engine bug: Existing, blank cell + number gives #NUM - @MarkBaker [CodePlex #11587](https://phpexcel.codeplex.com/workitem/11587)
+- AutoSize only measures length of first line in cell with multiple lines (ALT+Enter) - @Erik Tilt [CodePlex #11608](https://phpexcel.codeplex.com/workitem/11608)
+- Fatal error running Tests/12serializedfileformat.php (PHPExcel 1.7.2) - @Erik Tilt [CodePlex #11608](https://phpexcel.codeplex.com/workitem/11608)
+- Fixed various errors in the WORKDAY() and NETWORKDAYS() Date/Time functions (particularly related to holidays) - @MarkBaker
+- Uncaught exception 'Exception' with message 'Valid scale is between 10 and 400.' in Classes/PHPExcel/Worksheet/SheetView.php:115 - @Erik Tilt [CodePlex #11660](https://phpexcel.codeplex.com/workitem/11660)
+- "Unrecognized token 39 in formula" with PHPExcel_Reader_Excel5 (occuring with add-in functions) - @Erik Tilt [CodePlex #11551](https://phpexcel.codeplex.com/workitem/11551)
+- Excel2007 reader not reading PHPExcel_Style_Conditional::CONDITION_EXPRESSION - @Erik Tilt [CodePlex #11668](https://phpexcel.codeplex.com/workitem/11668)
+- Fix to the BESSELI(), BESSELJ(), BESSELK(), BESSELY() and COMPLEX() Engineering functions to use correct default values for parameters - @MarkBaker
+- DATEVALUE function not working for pure time values + allow DATEVALUE() function to handle partial dates (e.g. "1-Jun" or "12/2010") - @MarkBaker [CodePlex #11525](https://phpexcel.codeplex.com/workitem/11525)
+- Fix for empty quoted strings in formulae - @MarkBaker
+- Trap for division by zero in Bessel functions - @MarkBaker
+- Fix to OOCalc Reader to convert semi-colon (;) argument separator in formulae to a comma (,) - @MarkBaker
+- PHPExcel_Writer_Excel5_Parser cannot parse formula like =SUM(C$5:C5) - @Erik Tilt [CodePlex #11693](https://phpexcel.codeplex.com/workitem/11693)
+- Fix to OOCalc Reader to handle dates that fall outside 32-bit PHP's date range - @MarkBaker
+- File->sys_get_temp_dir() can fail in safe mode - @Erik Tilt [CodePlex #11692](https://phpexcel.codeplex.com/workitem/11692)
+- Sheet references in Excel5 writer do not work when referenced sheet title contains non-Latin symbols - @Erik Tilt [CodePlex #11727](https://phpexcel.codeplex.com/workitem/11727)
+- Bug in HTML writer can result in missing rows in output - @Erik Tilt [CodePlex #11743](https://phpexcel.codeplex.com/workitem/11743)
+- setShowGridLines(true) not working with PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #11674](https://phpexcel.codeplex.com/workitem/11674)
+- PHPExcel_Worksheet_RowIterator initial position incorrect - @Erik Tilt [CodePlex #11836](https://phpexcel.codeplex.com/workitem/11836)
+- PHPExcel_Worksheet_HeaderFooterDrawing Strict Exception thrown (by jshaw86) - @Erik Tilt [CodePlex #11835](https://phpexcel.codeplex.com/workitem/11835)
+- Parts of worksheet lost when there are embedded charts (Excel5 reader) - @Erik Tilt [CodePlex #11850](https://phpexcel.codeplex.com/workitem/11850)
+- VLOOKUP() function error when lookup value is passed as a cell reference rather than an absolute value - @MarkBaker
+- First segment of Rich-Text not read correctly by PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #12041](https://phpexcel.codeplex.com/workitem/12041)
+- Fatal Error with getCell('name') when name matches the pattern for a cell reference - @MarkBaker [CodePlex #12048](https://phpexcel.codeplex.com/workitem/12048)
+- excel5 writer appears to be swapping image locations - @Erik Tilt [CodePlex #12039](https://phpexcel.codeplex.com/workitem/12039)
+- Undefined index: host in ZipStreamWrapper.php, line 94 and line 101 - @Erik Tilt [CodePlex #11954](https://phpexcel.codeplex.com/workitem/11954)
+- BIFF8 File Format problem (too short COLINFO record) - @Erik Tilt [CodePlex #11672](https://phpexcel.codeplex.com/workitem/11672)
+- Column width sometimes changed after read/write with Excel2007 reader/writer - @Erik Tilt [CodePlex #12121](https://phpexcel.codeplex.com/workitem/12121)
+- Worksheet.php throws a fatal error when styling is turned off via setReadDataOnly on the reader - @Erik Tilt [CodePlex #11964](https://phpexcel.codeplex.com/workitem/11964)
+- Checking for Circular References in Formulae - @MarkBaker [CodePlex #11851](https://phpexcel.codeplex.com/workitem/11851)
+ - Calculation Engine code now traps for cyclic references, raising an error or throwing an exception, or allows 1 or more iterations through cyclic references, based on a configuration setting- PNG transparency using Excel2007 writer - @Erik Tilt [CodePlex #12244](https://phpexcel.codeplex.com/workitem/12244)
+- Custom readfilter error when cell formulas reference excluded cells (Excel5 reader) - @Erik Tilt [CodePlex #12221](https://phpexcel.codeplex.com/workitem/12221)
+- Protection problem in XLS - @Erik Tilt [CodePlex #12288](https://phpexcel.codeplex.com/workitem/12288)
+- getColumnDimension()->setAutoSize() incorrect on cells with Number Formatting - @Erik Tilt [CodePlex #12300](https://phpexcel.codeplex.com/workitem/12300)
+- Notices reading Excel file with Add-in funcitons (PHPExcel_Reader_Excel5) - @Erik Tilt [CodePlex #12378](https://phpexcel.codeplex.com/workitem/12378)
+- Excel5 reader not reading formulas with deleted sheet references - @Erik Tilt [CodePlex #12380](https://phpexcel.codeplex.com/workitem/12380)
+- Named range (defined name) scope problems for in PHPExcel - @Erik Tilt [CodePlex #12404](https://phpexcel.codeplex.com/workitem/12404)
+- PHP Parse error: syntax error, unexpected T_PUBLIC in PHPExcel/Calculation.php on line 3482 - @Erik Tilt [CodePlex #12423](https://phpexcel.codeplex.com/workitem/12423)
+- Named ranges don't appear in name box using Excel5 writer - @Erik Tilt [CodePlex #12505](https://phpexcel.codeplex.com/workitem/12505)
+- Many merged cells + autoSize column -> slows down the writer - @Erik Tilt [CodePlex #12509](https://phpexcel.codeplex.com/workitem/12509)
+- Incorrect fallback order comment in Shared/Strings.php ConvertEncoding() - @Erik Tilt [CodePlex #12539](https://phpexcel.codeplex.com/workitem/12539)
+- IBM AIX iconv() will not work, should revert to mbstring etc. instead - @Erik Tilt [CodePlex #12538](https://phpexcel.codeplex.com/workitem/12538)
+- Excel5 writer and mbstring functions overload - @Erik Tilt [CodePlex #12568](https://phpexcel.codeplex.com/workitem/12568)
+- OFFSET needs to flattenSingleValue the $rows and $columns args - @MarkBaker [CodePlex #12672](https://phpexcel.codeplex.com/workitem/12672)
+- Formula with DMAX(): Notice: Undefined offset: 2 in ...\PHPExcel\Calculation.php on line 2365 - @MarkBaker [CodePlex #12546](https://phpexcel.codeplex.com/workitem/12546)
+ - Note that the Database functions have not yet been implemented- Call to a member function getParent() on a non-object in Classes\\PHPExcel\\Calculation.php Title is required - @MarkBaker [CodePlex #12839](https://phpexcel.codeplex.com/workitem/12839)
+- Cyclic Reference in Formula - @MarkBaker [CodePlex #12935](https://phpexcel.codeplex.com/workitem/12935)
+- Memory error...data validation? - @MarkBaker [CodePlex #13025](https://phpexcel.codeplex.com/workitem/13025)
+
+## [1.7.2] - 2010-01-11
+
+### General
+
+- Applied patch 4362 - @Erik Tilt
+- Applied patch 4363 (modified) - @Erik Tilt
+- 1.7.1 Extremely Slow - Refactored PHPExcel_Calculation_Functions::flattenArray() method and set calculation cache timer default to 2.5 seconds - @MarkBaker [CodePlex #10874](https://phpexcel.codeplex.com/workitem/10874)
+- Allow formulae to contain line breaks - @MarkBaker
+- split() function deprecated in PHP 5.3.0 - @Erik Tilt [CodePlex #10910](https://phpexcel.codeplex.com/workitem/10910)
+- sys_get_temp_dir() requires PHP 5.2.1, not PHP 5.2 [provide fallback function for PHP 5.2.0] - @Erik Tilt
+- Implementation of the ISPMT() Financial function by Matt Groves - @MarkBaker
+- Put the example of formula with more arguments in documentation - @MarkBaker [CodePlex #11052](https://phpexcel.codeplex.com/workitem/11052)
+
+### Features
+
+- Improved accuracy for the GAMMAINV() Statistical Function - @MarkBaker
+- XFEXT record support to fix colors change from Excel5 reader, and copy/paste color change with Excel5 writer - @Erik Tilt [CodePlex #10409](https://phpexcel.codeplex.com/workitem/10409)
+ - Excel5 reader reads RGB color information in XFEXT records for borders, font color and fill color- Implement more Excel calculation functions - @MarkBaker
+ - Implemented the FVSCHEDULE(), XNPV(), IRR(), MIRR(), XIRR() and RATE() Financial functions
+ - Implemented the SUMPRODUCT() Mathematical function
+ - Implemented the ZTEST() Statistical Function- Multiple print areas in one sheet - @Erik Tilt [CodePlex #10919](https://phpexcel.codeplex.com/workitem/10919)
+- Store calculated values in output by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10930](https://phpexcel.codeplex.com/workitem/10930)
+- Sheet protection options in Excel5 reader/writer - @Erik Tilt [CodePlex #10939](https://phpexcel.codeplex.com/workitem/10939)
+- Modification of the COUNT(), AVERAGE(), AVERAGEA(), DEVSQ, AVEDEV(), STDEV(), STDEVA(), STDEVP(), STDEVPA(), VARA() and VARPA() SKEW() and KURT() functions to correctly handle boolean values depending on whether they're passed in as values, values within a matrix or values within a range of cells. - @MarkBaker
+- Cell range selection - @Erik Tilt
+- Root-relative path handling - @MarkBaker [CodePlex #10266](https://phpexcel.codeplex.com/workitem/10266)
+
+### Bugfixes
+
+- Named Ranges not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #11315](https://phpexcel.codeplex.com/workitem/11315)
+- Excel2007 Reader fails to load Apache POI generated Excel - @MarkBaker [CodePlex #11206](https://phpexcel.codeplex.com/workitem/11206)
+- Number format is broken when system's thousands separator is empty - @MarkBaker [CodePlex #11154](https://phpexcel.codeplex.com/workitem/11154)
+- ReferenceHelper::updateNamedFormulas throws errors if oldName is empty - @MarkBaker [CodePlex #11401](https://phpexcel.codeplex.com/workitem/11401)
+- parse_url() fails to parse path to an image in xlsx - @MarkBaker [CodePlex #11296](https://phpexcel.codeplex.com/workitem/11296)
+- Workaround for iconv_substr() bug in PHP 5.2.0 - @Erik Tilt [CodePlex #10876](https://phpexcel.codeplex.com/workitem/10876)
+- 1 pixel error for image width and height with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10877](https://phpexcel.codeplex.com/workitem/10877)
+- Fix to GEOMEAN() Statistical function - @MarkBaker
+- setValue('-') and setValue('.') sets numeric 0 instead of 1-character string - @Erik Tilt [CodePlex #10884](https://phpexcel.codeplex.com/workitem/10884)
+- Row height sometimes much too low after read with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10885](https://phpexcel.codeplex.com/workitem/10885)
+- Diagonal border. Miscellaneous missing support. - @Erik Tilt [CodePlex #10888](https://phpexcel.codeplex.com/workitem/10888)
+ - Constant PHPExcel_Style_Borders::DIAGONAL_BOTH added to support double-diagonal (cross)
+ - PHPExcel_Reader_Excel2007 not always reading diagonal borders (only recognizes 'true' and not '1')
+ - PHPExcel_Reader_Excel5 support for diagonal borders
+ - PHPExcel_Writer_Excel5 support for diagonal borders- Session bug: Fatal error: Call to a member function bindValue() on a non-object in ...\Classes\PHPExcel\Cell.php on line 217 - @Erik Tilt [CodePlex #10894](https://phpexcel.codeplex.com/workitem/10894)
+- Colors messed up saving twice with same instance of PHPExcel_Writer_Excel5 (regression since 1.7.0) - @Erik Tilt [CodePlex #10896](https://phpexcel.codeplex.com/workitem/10896)
+- Method PHPExcel_Worksheet::setDefaultStyle is not working - @Erik Tilt [CodePlex #10917](https://phpexcel.codeplex.com/workitem/10917)
+- PHPExcel_Reader_CSV::canRead() sometimes says false when it shouldn't - @Erik Tilt [CodePlex #10897](https://phpexcel.codeplex.com/workitem/10897)
+- Changes in workbook not picked up between two saves with PHPExcel_Writer_Excel2007 - @Erik Tilt [CodePlex #10922](https://phpexcel.codeplex.com/workitem/10922)
+- Decimal and thousands separators missing in HTML and PDF output - @Erik Tilt [CodePlex #10913](https://phpexcel.codeplex.com/workitem/10913)
+- Notices with PHPExcel_Reader_Excel5 and named array constants - @Erik Tilt [CodePlex #10936](https://phpexcel.codeplex.com/workitem/10936)
+- Calculation engine limitation on 32-bit platform with integers > 2147483647 - @MarkBaker [CodePlex #10938](https://phpexcel.codeplex.com/workitem/10938)
+- Shared(?) formulae containing absolute cell references not read correctly using Excel5 Reader - @Erik Tilt [CodePlex #10959](https://phpexcel.codeplex.com/workitem/10959)
+- Warning messages with intersection operator involving single cell - @MarkBaker [CodePlex #10962](https://phpexcel.codeplex.com/workitem/10962)
+- Infinite loop in Excel5 reader caused by zero-length string in SST - @Erik Tilt [CodePlex #10980](https://phpexcel.codeplex.com/workitem/10980)
+- Remove unnecessary cell sorting to improve speed by approx. 18% in HTML and PDF writers - @Erik Tilt [CodePlex #10983](https://phpexcel.codeplex.com/workitem/10983)
+- Cannot read A1 cell content - OO_Reader - @MarkBaker [CodePlex #10977](https://phpexcel.codeplex.com/workitem/10977)
+- Transliteration failed, invalid encoding - @Erik Tilt [CodePlex #11000](https://phpexcel.codeplex.com/workitem/11000)
+
+## [1.7.1] - 2009-11-02
+
+### General
+
+- ereg() function deprecated in PHP 5.3.0 - @Erik Tilt [CodePlex #10687](https://phpexcel.codeplex.com/workitem/10687)
+- Writer Interface Inconsequence - setTempDir and setUseDiskCaching - @MarkBaker [CodePlex #10739](https://phpexcel.codeplex.com/workitem/10739)
+
+### Features
+
+- Upgrade to TCPDF 4.8.009 - @Erik Tilt
+- Support for row and column styles (feature request) - @Erik Tilt
+ - Basic implementation for Excel2007/Excel5 reader/writer- Hyperlink to local file in Excel5 reader/writer - @Erik Tilt [CodePlex #10459](https://phpexcel.codeplex.com/workitem/10459)
+- Color Tab (Color Sheet's name) - @MarkBaker [CodePlex #10472](https://phpexcel.codeplex.com/workitem/10472)
+- Border style "double" support in PHPExcel_Writer_HTML - @Erik Tilt [CodePlex #10488](https://phpexcel.codeplex.com/workitem/10488)
+- Multi-section number format support in HTML/PDF/CSV writers - @Erik Tilt [CodePlex #10492](https://phpexcel.codeplex.com/workitem/10492)
+- Some additional performance tweaks in the calculation engine - @MarkBaker
+- Fix result of DB() and DDB() Financial functions to 2dp when in Gnumeric Compatibility mode - @MarkBaker
+- Added AMORDEGRC(), AMORLINC() and COUPNUM() Financial function (no validation of parameters yet) - @MarkBaker
+- Improved accuracy of TBILLEQ(), TBILLPRICE() and TBILLYIELD() Financial functions when in Excel or Gnumeric mode - @MarkBaker
+- Added INDIRECT() Lookup/Reference function (only supports full addresses at the moment) - @MarkBaker
+- PHPExcel_Reader_CSV::canRead() improvements - @MarkBaker [CodePlex #10498](https://phpexcel.codeplex.com/workitem/10498)
+- Input encoding option for PHPExcel_Reader_CSV - @Erik Tilt [CodePlex #10500](https://phpexcel.codeplex.com/workitem/10500)
+- Colored number format support, e.g. [Red], in HTML/PDF output - @Erik Tilt [CodePlex #10493](https://phpexcel.codeplex.com/workitem/10493)
+- Color Tab (Color Sheet's name) [Excel5 reader/writer support] - @Erik Tilt [CodePlex #10559](https://phpexcel.codeplex.com/workitem/10559)
+- Initial version of SYLK (slk) and Excel 2003 XML Readers (Cell data and basic cell formatting) - @MarkBaker
+- Initial version of Open Office Calc (ods) Reader (Cell data only) - @MarkBaker
+- Initial use of "pass by reference" in the calculation engine for ROW() and COLUMN() Lookup/Reference functions - @MarkBaker
+- COLUMNS() and ROWS() Lookup/Reference functions, and SUBSTITUTE() Text function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- AdvancedValueBinder(): Re-enable zero-padded string-to-number conversion, e.g '0004' -> 4 - @Erik Tilt [CodePlex #10502](https://phpexcel.codeplex.com/workitem/10502)
+- Make PHP type match Excel datatype - @Erik Tilt [CodePlex #10600](https://phpexcel.codeplex.com/workitem/10600)
+- Change first page number on header - @MarkBaker [CodePlex #10630](https://phpexcel.codeplex.com/workitem/10630)
+- Applied patch 3941 - @MarkBaker
+- Hidden sheets - @MB,ET [CodePlex #10745](https://phpexcel.codeplex.com/workitem/10745)
+- mbstring fallback when iconv is broken - @Erik Tilt [CodePlex #10761](https://phpexcel.codeplex.com/workitem/10761)
+- Note, can't yet handle comparison of two matrices - @MarkBaker
+- Improved handling for validation and error trapping in a number of functions - @MarkBaker
+- Improved support for fraction number formatting - @MarkBaker
+- Support Reading CSV with Byte Order Mark (BOM) - @Erik Tilt [CodePlex #10455](https://phpexcel.codeplex.com/workitem/10455)
+
+### Bugfixes
+
+- addExternalSheet() at specified index - @Erik Tilt [CodePlex #10860](https://phpexcel.codeplex.com/workitem/10860)
+- Named range can no longer be passed to worksheet->getCell() - @MarkBaker [CodePlex #10684](https://phpexcel.codeplex.com/workitem/10684)
+- RichText HTML entities no longer working in PHPExcel 1.7.0 - @Erik Tilt [CodePlex #10455](https://phpexcel.codeplex.com/workitem/10455)
+- Fit-to-width value of 1 is lost after read/write of Excel2007 spreadsheet [+ support for simultaneous scale/fitToPage] - @Erik Tilt
+- Performance issue identified by profiling - @MarkBaker [CodePlex #10469](https://phpexcel.codeplex.com/workitem/10469)
+- setSelectedCell is wrong - @Erik Tilt [CodePlex #10473](https://phpexcel.codeplex.com/workitem/10473)
+- Images get squeezed/stretched with (Mac) Verdana 10 Excel files using Excel5 reader/writer - @Erik Tilt [CodePlex #10481](https://phpexcel.codeplex.com/workitem/10481)
+- Error in argument count for DATEDIF() function - @MarkBaker [CodePlex #10482](https://phpexcel.codeplex.com/workitem/10482)
+- updateFormulaReferences is buggy - @MarkBaker [CodePlex #10452](https://phpexcel.codeplex.com/workitem/10452)
+- CellIterator returns null Cell if onlyExistingCells is set and key() is in use - @MarkBaker [CodePlex #10485](https://phpexcel.codeplex.com/workitem/10485)
+- Wrong RegEx for parsing cell references in formulas - @MarkBaker [CodePlex #10453](https://phpexcel.codeplex.com/workitem/10453)
+- Optimisation subverted to devastating effect if IterateOnlyExistingCells is clear - @MarkBaker [CodePlex #10486](https://phpexcel.codeplex.com/workitem/10486)
+- Fatal error: Uncaught exception 'Exception' with message 'Unrecognized token 6C in formula'... with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10494](https://phpexcel.codeplex.com/workitem/10494)
+- Fractions stored as text are not treated as numbers by PHPExcel's calculation engine - @MarkBaker [CodePlex #10490](https://phpexcel.codeplex.com/workitem/10490)
+- AutoFit (autosize) row height not working in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10503](https://phpexcel.codeplex.com/workitem/10503)
+- Fixed problem with null values breaking the calculation stack - @MarkBaker
+- Date number formats sometimes fail with PHPExcel_Style_NumberFormat::toFormattedString, e.g. [$-40047]mmmm d yyyy - @Erik Tilt [CodePlex #10524](https://phpexcel.codeplex.com/workitem/10524)
+- Fixed minor problem with DATEDIFF YM calculation - @MarkBaker
+- Applied patch 3695 - @MarkBaker
+- setAutosize() and Date cells not working properly - @Erik Tilt [CodePlex #10536](https://phpexcel.codeplex.com/workitem/10536)
+- Time value hour offset in output by HTML/PDF/CSV writers (system timezone problem) - @Erik Tilt [CodePlex #10556](https://phpexcel.codeplex.com/workitem/10556)
+- Control characters 0x14-0x1F are not treated by PHPExcel - @Erik Tilt [CodePlex #10558](https://phpexcel.codeplex.com/workitem/10558)
+- PHPExcel_Writer_Excel5 not working when open_basedir restriction is in effect - @Erik Tilt [CodePlex #10560](https://phpexcel.codeplex.com/workitem/10560)
+- IF formula calculation problem in PHPExcel 1.7.0 (string comparisons) - @MarkBaker [CodePlex #10563](https://phpexcel.codeplex.com/workitem/10563)
+- Improved CODE() Text function result for UTF-8 characters - @MarkBaker
+- Empty rows are collapsed with HTML/PDF writer - @Erik Tilt [CodePlex #10568](https://phpexcel.codeplex.com/workitem/10568)
+- Gaps between rows in output by PHPExcel_Writer_PDF (Upgrading to TCPDF 4.7.003) - @Erik Tilt [CodePlex #10569](https://phpexcel.codeplex.com/workitem/10569)
+- Problem reading formulas (Excel5 reader problem with "fake" shared formulas) - @Erik Tilt [CodePlex #10575](https://phpexcel.codeplex.com/workitem/10575)
+- Error type in formula: "_raiseFormulaError message is Formula Error: An unexpected error occured" - @MarkBaker [CodePlex #10588](https://phpexcel.codeplex.com/workitem/10588)
+- Miscellaneous column width problems in Excel5/Excel2007 writer - @Erik Tilt [CodePlex #10599](https://phpexcel.codeplex.com/workitem/10599)
+- Reader/Excel5 'Unrecognized token 2D in formula' in latest version - @Erik Tilt [CodePlex #10615](https://phpexcel.codeplex.com/workitem/10615)
+- on php 5.3 PHPExcel 1.7 Excel 5 reader fails in _getNextToken, token = 2C, throws exception - @Erik Tilt [CodePlex #10623](https://phpexcel.codeplex.com/workitem/10623)
+- Fatal error when altering styles after workbook has been saved - @Erik Tilt [CodePlex #10617](https://phpexcel.codeplex.com/workitem/10617)
+- Images vertically stretched or squeezed when default font size is changed (PHPExcel_Writer_Excel5) - @Erik Tilt [CodePlex #10661](https://phpexcel.codeplex.com/workitem/10661)
+- Styles not read in "manipulated" Excel2007 workbook - @Erik Tilt [CodePlex #10676](https://phpexcel.codeplex.com/workitem/10676)
+- Windows 7 says corrupt file by PHPExcel_Writer_Excel5 when opening in Excel - @Erik Tilt [CodePlex #10059](https://phpexcel.codeplex.com/workitem/10059)
+- Calculations sometimes not working with cell references to other sheets - @MarkBaker [CodePlex #10708](https://phpexcel.codeplex.com/workitem/10708)
+- Problem with merged cells after insertNewRowBefore() - @Erik Tilt [CodePlex #10706](https://phpexcel.codeplex.com/workitem/10706)
+- Applied patch 4023 - @MarkBaker
+- Fix to SUMIF() and COUNTIF() Statistical functions for when condition is a match against a string value - @MarkBaker
+- PHPExcel_Cell::coordinateFromString should throw exception for bad string parameter - @Erik Tilt [CodePlex #10721](https://phpexcel.codeplex.com/workitem/10721)
+- EucrosiaUPC (Thai font) not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10723](https://phpexcel.codeplex.com/workitem/10723)
+- Improved the return of calculated results when the result value is an array - @MarkBaker
+- Allow calculation engine to support Functions prefixed with @ within formulae - @MarkBaker
+- Intersection operator (space operator) fatal error with calculation engine - @MarkBaker [CodePlex #10632](https://phpexcel.codeplex.com/workitem/10632)
+- Chinese, Japanese, Korean characters show as squares in PDF - @Erik Tilt [CodePlex #10742](https://phpexcel.codeplex.com/workitem/10742)
+- sheet title allows invalid characters - @Erik Tilt [CodePlex #10756](https://phpexcel.codeplex.com/workitem/10756)
+- Sheet!$A$1 as function argument in formula causes infinite loop in Excel5 writer - @Erik Tilt [CodePlex #10757](https://phpexcel.codeplex.com/workitem/10757)
+- Cell range involving name not working with calculation engine - Modified calculation parser to handle range operator (:), but doesn't currently handle worksheet references with spaces or other non-alphameric characters, or trap erroneous references - @MarkBaker [CodePlex #10740](https://phpexcel.codeplex.com/workitem/10740)
+- DATE function problem with calculation engine (says too few arguments given) - @MarkBaker [CodePlex #10798](https://phpexcel.codeplex.com/workitem/10798)
+- Blank cell can cause wrong calculated value - @MarkBaker [CodePlex #10799](https://phpexcel.codeplex.com/workitem/10799)
+- Modified ROW() and COLUMN() Lookup/Reference Functions to return an array when passed a cell range, plus some additional work on INDEX() - @MarkBaker
+- Images not showing in Excel 97 using PHPExcel_Writer_Excel5 (patch by Jordi Gutiérrez Hermoso) - @Erik Tilt [CodePlex #10817](https://phpexcel.codeplex.com/workitem/10817)
+- When figures are contained in the excel sheet, Reader was stopped - @Erik Tilt [CodePlex #10785](https://phpexcel.codeplex.com/workitem/10785)
+- Formulas changed after insertNewRowBefore() - @MarkBaker [CodePlex #10818](https://phpexcel.codeplex.com/workitem/10818)
+- Cell range row offset problem with shared formulas using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10825](https://phpexcel.codeplex.com/workitem/10825)
+- Warning: Call-time pass-by-reference has been deprecated - @MarkBaker [CodePlex #10832](https://phpexcel.codeplex.com/workitem/10832)
+- Image should "Move but don't size with cells" instead of "Move and size with cells" with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10849](https://phpexcel.codeplex.com/workitem/10849)
+- Opening a Excel5 generated XLS in Excel 2007 results in header/footer entry not showing on input - @Erik Tilt [CodePlex #10856](https://phpexcel.codeplex.com/workitem/10856)
+- addExternalSheet() not returning worksheet - @Erik Tilt [CodePlex #10859](https://phpexcel.codeplex.com/workitem/10859)
+- Invalid results in formulas with named ranges - @MarkBaker [CodePlex #10629](https://phpexcel.codeplex.com/workitem/10629)
+
+## [1.7.0] - 2009-08-10
+
+### General
+
+- Expand documentation: Number formats - @Erik Tilt
+- Class 'PHPExcel_Cell_AdvancedValueBinder' not found - @Erik Tilt
+
+### Features
+
+- Change return type of date functions to PHPExcel_Calculation_Functions::RETURNDATE_EXCEL - @MarkBaker
+- New RPN and stack-based calculation engine for improved performance of formula calculation - @MarkBaker
+ - Faster (anything between 2 and 12 times faster than the old parser, depending on the complexity and nature of the formula)
+ - Significantly more memory efficient when formulae reference cells across worksheets
+ - Correct behaviour when referencing Named Ranges that exist on several worksheets
+ - Support for Excel ^ (Exponential) and % (Percentage) operators
+ - Support for matrices within basic arithmetic formulae (e.g. ={1,2,3;4,5,6;7,8,9}/2)
+ - Better trapping/handling of NaN and infinity results (return #NUM! error)
+ - Improved handling of empty parameters for Excel functions
+ - Optional logging of calculation steps- New calculation engine can be accessed independently of workbooks (for use as a standalone calculator) - @MarkBaker
+- Implement more Excel calculation functions - @MarkBaker
+ - Initial implementation of the COUNTIF() and SUMIF() Statistical functions
+ - Added ACCRINT() Financial function- Modifications to number format handling for dddd and ddd masks in dates, use of thousand separators even when locale only implements it for money, and basic fraction masks (0 ?/? and ?/?) - @MarkBaker
+- Support arbitrary fixed number of decimals in PHPExcel_Style_NumberFormat::toFormattedString() - @Erik Tilt
+- Improving performance and memory on data dumps - @Erik Tilt
+ - Various style optimizations (merging from branch wi6857-memory)
+ - Moving hyperlink and dataValidation properties from cell to worksheet for lower PHP memory usage- Provide fluent interfaces where possible - @MarkBaker
+- Make easy way to apply a border to a rectangular selection - @Erik Tilt
+- Support for system window colors in PHPExcel_Reader_Excel5 - @Erik Tilt
+- Horizontal center across selection - @Erik Tilt
+- Merged cells record, write to full record size in PHPExcel_Writer_Excel5 - @Erik Tilt
+- Add page break between sheets in exported PDF - @MarkBaker
+- Sanitization of UTF-8 input for cell values - @Erik Tilt
+- Read cached calculated value with PHPExcel_Reader_Excel5 - @Erik Tilt
+- Miscellaneous CSS improvements for PHPExcel_Writer_HTML - @Erik Tilt
+- getProperties: setCompany feature request - @Erik Tilt
+- Insert worksheet at a specified index - @MarkBaker
+- Change worksheet index - @MarkBaker
+- Readfilter for CSV reader - @MarkBaker
+- Check value of mbstring.func_overload when saving with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10172](https://phpexcel.codeplex.com/workitem/10172)
+- Eliminate dependency of an include path pointing to class directory - @Erik Tilt [CodePlex #10251](https://phpexcel.codeplex.com/workitem/10251)
+- Method for getting the correct reader for a certain file (contribution) - @Erik Tilt [CodePlex #10292](https://phpexcel.codeplex.com/workitem/10292)
+- Choosing specific row in fromArray method - @Erik Tilt [CodePlex #10287](https://phpexcel.codeplex.com/workitem/10287)
+- Shared formula support in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10319](https://phpexcel.codeplex.com/workitem/10319)
+
+### Bugfixes
+
+- Right-to-left column direction in worksheet - @MB,ET [CodePlex #10345](https://phpexcel.codeplex.com/workitem/10345)
+- PHPExcel_Reader_Excel5 not reading PHPExcel_Style_NumberFormat::FORMAT_NUMBER ('0') - @Erik Tilt
+- Fractional row height in locale other than English results in corrupt output using PHPExcel_Writer_Excel2007 - @Erik Tilt
+- Fractional (decimal) numbers not inserted correctly when locale is other than English - @Erik Tilt
+- Fractional calculated value in locale other than English results in corrupt output using PHPExcel_Writer_Excel2007 - @Erik Tilt
+- Locale aware decimal and thousands separator in exported formats HTML, CSV, PDF - @Erik Tilt
+- Cannot Add Image with Space on its Name - @MarkBaker
+- Black line at top of every page in output by PHPExcel_Writer_PDF - @Erik Tilt
+- Border styles and border colors not showing in HTML output (regression since 1.6.4) - @Erik Tilt
+- Hidden screen gridlines setting in worksheet not read by PHPExcel_Reader_Excel2007 - @Erik Tilt
+- Some valid sheet names causes corrupt output using PHPExcel_Writer_Excel2007 - @MarkBaker
+- More than 32,767 characters in a cell gives corrupt Excel file - @Erik Tilt
+- Images not getting copyied with the ->copy() function - @Erik Tilt
+- Bad calculation of column width setAutoSize(true) function - @Erik Tilt
+- Dates are sometimes offset by 1 day in output by HTML and PDF writers depending on system timezone setting - @Erik Tilt
+- Wingdings symbol fonts not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10003](https://phpexcel.codeplex.com/workitem/10003)
+- White space string prefix stripped by PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #10010](https://phpexcel.codeplex.com/workitem/10010)
+- The name of the Workbook stream MUST be "Workbook", not "Book" - @Erik Tilt [CodePlex #10023](https://phpexcel.codeplex.com/workitem/10023)
+- Avoid message "Microsoft Excel recalculates formulas..." when closing xls file from Excel - @Erik Tilt [CodePlex #10030](https://phpexcel.codeplex.com/workitem/10030)
+- Non-unique newline representation causes problems with LEN formula - @Erik Tilt [CodePlex #10031](https://phpexcel.codeplex.com/workitem/10031)
+- Newline in cell not showing with PHPExcel_Writer_HTML and PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #10033](https://phpexcel.codeplex.com/workitem/10033)
+- Rich-Text strings get prefixed by when output by HTML writer - @Erik Tilt [CodePlex #10046](https://phpexcel.codeplex.com/workitem/10046)
+- Leading spaces do not appear in output by HTML/PDF writers - @Erik Tilt [CodePlex #10052](https://phpexcel.codeplex.com/workitem/10052)
+- Empty Apache POI-generated file can not be read - @MarkBaker [CodePlex #10061](https://phpexcel.codeplex.com/workitem/10061)
+- Column width not scaling correctly with font size in HTML and PDF writers - @Erik Tilt [CodePlex #10068](https://phpexcel.codeplex.com/workitem/10068)
+- Inaccurate row heights with HTML writer - @Erik Tilt [CodePlex #10069](https://phpexcel.codeplex.com/workitem/10069)
+- Reference helper - @MarkBaker
+- Excel 5 Named ranges should not be local to the worksheet, but accessible from all worksheets - @MarkBaker
+- Row heights are ignored by PHPExcel_Writer_PDF - @Erik Tilt [CodePlex #10088](https://phpexcel.codeplex.com/workitem/10088)
+- Write raw XML - @MarkBaker
+- removeRow(), removeColumn() not always clearing cell values - @Erik Tilt [CodePlex #10098](https://phpexcel.codeplex.com/workitem/10098)
+- Problem reading certain hyperlink records with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10142](https://phpexcel.codeplex.com/workitem/10142)
+- Hyperlink cell range read failure with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #10143](https://phpexcel.codeplex.com/workitem/10143)
+- 'Column string index can not be empty.' - @MarkBaker [CodePlex #10149](https://phpexcel.codeplex.com/workitem/10149)
+- getHighestColumn() sometimes says there are 256 columns with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10204](https://phpexcel.codeplex.com/workitem/10204)
+- extractSheetTitle fails when sheet title contains exclamation mark (!) - @Erik Tilt [CodePlex #10220](https://phpexcel.codeplex.com/workitem/10220)
+- setTitle() sometimes erroneously appends integer to sheet name - @Erik Tilt [CodePlex #10221](https://phpexcel.codeplex.com/workitem/10221)
+- Mac BIFF5 Excel file read failure (missing support for Mac OS Roman character set) - @Erik Tilt [CodePlex #10229](https://phpexcel.codeplex.com/workitem/10229)
+- BIFF5 header and footer incorrectly read by PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10230](https://phpexcel.codeplex.com/workitem/10230)
+- iconv notices when reading hyperlinks with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10259](https://phpexcel.codeplex.com/workitem/10259)
+- Excel5 reader OLE read failure with small Mac BIFF5 Excel files - @Erik Tilt [CodePlex #10252](https://phpexcel.codeplex.com/workitem/10252)
+- Problem in reading formula : IF( IF ) with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10272](https://phpexcel.codeplex.com/workitem/10272)
+- Error reading formulas referencing external sheets with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10274](https://phpexcel.codeplex.com/workitem/10274)
+- Image horizontally stretched when default font size is increased (PHPExcel_Writer_Excel5) - @Erik Tilt [CodePlex #10291](https://phpexcel.codeplex.com/workitem/10291)
+- Undefined offset in Reader\Excel5.php on line 3572 - @Erik Tilt [CodePlex #10333](https://phpexcel.codeplex.com/workitem/10333)
+- PDF output different then XLS (copied data) - @MarkBaker [CodePlex #10340](https://phpexcel.codeplex.com/workitem/10340)
+- Internal hyperlinks with UTF-8 sheet names not working in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #10352](https://phpexcel.codeplex.com/workitem/10352)
+- String shared formula result read error with PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #10361](https://phpexcel.codeplex.com/workitem/10361)
+- Uncaught exception 'Exception' with message 'Valid scale is between 10 and 400.' in Classes/PHPExcel/Worksheet/PageSetup.php:338 - @Erik Tilt [CodePlex #10363](https://phpexcel.codeplex.com/workitem/10363)
+- Using setLoadSheetsOnly fails if you do not use setReadDataOnly(true) and sheet is not the first sheet - @Erik Tilt [CodePlex #10355](https://phpexcel.codeplex.com/workitem/10355)
+- getCalculatedValue() sometimes incorrect with IF formula and 0-values - @MarkBaker [CodePlex #10362](https://phpexcel.codeplex.com/workitem/10362)
+- Excel Reader 2007 problem with "shared" formulae when "master" is an error - @MarkBaker
+- Named Range Bug, using the same range name on different worksheets - @MarkBaker
+- Java code in JAMA classes - @MarkBaker
+- getCalculatedValue() not working with some formulas involving error types - @MarkBaker
+- evaluation of both return values in an IF() statement returning an error if either result was an error, irrespective of the IF evaluation - @MarkBaker
+- Power in formulas: new calculation engine no longer treats ^ as a bitwise XOR operator - @MarkBaker
+- Bugfixes and improvements to many of the Excel functions in PHPExcel - @MarkBaker
+ - Added optional "places" parameter in the BIN2HEX(), BIN2OCT, DEC2BIN(), DEC2OCT(), DEC2HEX(), HEX2BIN(), HEX2OCT(), OCT2BIN() and OCT2HEX() Engineering Functions
+ - Trap for unbalanced matrix sizes in MDETERM() and MINVERSE() Mathematic and Trigonometric functions
+ - Fix for default characters parameter value for LEFT() and RIGHT() Text functions
+ - Fix for GCD() and LCB() Mathematical functions when the parameters include a zero (0) value
+ - Fix for BIN2OCT() Engineering Function for 2s complement values (which were returning hex values)
+ - Fix for BESSELK() and BESSELY() Engineering functions
+ - Fix for IMDIV() Engineering Function when result imaginary component is positive (wasn't setting the sign)
+ - Fix for ERF() Engineering Function when called with an upper limit value for the integration
+ - Fix to DATE() Date/Time Function for year value of 0
+ - Set ISPMT() function as category FINANCIAL
+ - Fix for DOLLARDE() and DOLLARFR() Financial functions
+ - Fix to EFFECT() Financial function (treating $nominal_rate value as a variable name rather than a value)
+ - Fix to CRITBINOM() Statistical function (CurrentValue and EssentiallyZero treated as constants rather than variables)
+ - Note that an Error in the function logic can still lead to a permanent loop
+ - Fix to MOD() Mathematical function to work with floating point results
+ - Fix for QUOTIENT() Mathematical function
+ - Fix to HOUR(), MINUTE() and SECOND() Date/Time functions to return an error when passing in a floating point value of 1.0 or greater, or less than 0
+ - LOG() Function now correctly returns base-10 log when called with only one parameter, rather than the natural log as the default base
+ - Modified text functions to handle multibyte character set (UTF-8).
+
+## [1.6.7] - 2009-04-22
+
+### BREAKING CHANGE
+
+In previous versions of PHPExcel up to and including 1.6.6,
+when a cell had a date-like number format code, it was possible to enter a date
+directly using an integer PHP-time without converting to Excel date format.
+Starting with PHPExcel 1.6.7 this is no longer supported. Refer to the developer
+documentation for more information on entering dates into a cell.
+
+### General
+
+- Deprecate misspelled setStriketrough() and getStriketrough() methods - @MarkBaker [CodePlex #9416](https://phpexcel.codeplex.com/workitem/9416)
+
+### Features
+
+- Performance improvement when saving file - @MarkBaker [CodePlex #9526](https://phpexcel.codeplex.com/workitem/9526)
+- Check that sheet title has maximum 31 characters - @MarkBaker [CodePlex #9598](https://phpexcel.codeplex.com/workitem/9598)
+- True support for Excel built-in number format codes - @MB, ET [CodePlex #9631](https://phpexcel.codeplex.com/workitem/9631)
+- Ability to read defect BIFF5 Excel file without CODEPAGE record - @Erik Tilt [CodePlex #9683](https://phpexcel.codeplex.com/workitem/9683)
+- Auto-detect which reader to invoke - @MarkBaker [CodePlex #9701](https://phpexcel.codeplex.com/workitem/9701)
+- Deprecate insertion of dates using PHP-time (Unix time) [request for removal of feature] - @Erik Tilt [CodePlex #9214](https://phpexcel.codeplex.com/workitem/9214)
+- Support for entering time values like '9:45', '09:45' using AdvancedValueBinder - @Erik Tilt [CodePlex #9747](https://phpexcel.codeplex.com/workitem/9747)
+
+### Bugfixes
+
+- DataType dependent horizontal alignment in HTML and PDF writer - @Erik Tilt [CodePlex #9797](https://phpexcel.codeplex.com/workitem/9797)
+- Cloning data validation object causes script to stop - @MarkBaker [CodePlex #9375](https://phpexcel.codeplex.com/workitem/9375)
+- Simultaneous repeating rows and repeating columns not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9400](https://phpexcel.codeplex.com/workitem/9400)
+- Simultaneous repeating rows and repeating columns not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #9399](https://phpexcel.codeplex.com/workitem/9399)
+- Row outline level not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9437](https://phpexcel.codeplex.com/workitem/9437)
+- Occasional notices with PHPExcel_Reader_Excel5 when Excel file contains drawing elements - @Erik Tilt [CodePlex #9452](https://phpexcel.codeplex.com/workitem/9452)
+- PHPExcel_Reader_Excel5 fails as a whole when workbook contains images other than JPEG/PNG - @Erik Tilt [CodePlex #9453](https://phpexcel.codeplex.com/workitem/9453)
+- Excel5 writer checks for iconv but does not necessarily use it - @Erik Tilt [CodePlex #9444](https://phpexcel.codeplex.com/workitem/9444)
+- Altering a style on copied worksheet alters also the original - @Erik Tilt [CodePlex #9463](https://phpexcel.codeplex.com/workitem/9463)
+- Formulas are incorrectly updated when a sheet is renamed - @MarkBaker [CodePlex #9480](https://phpexcel.codeplex.com/workitem/9480)
+- PHPExcel_Worksheet::extractSheetTitle not treating single quotes correctly - @MarkBaker [CodePlex #9513](https://phpexcel.codeplex.com/workitem/9513)
+- PHP Warning raised in function array_key_exists - @MarkBaker [CodePlex #9477](https://phpexcel.codeplex.com/workitem/9477)
+- getAlignWithMargins() gives wrong value when using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9599](https://phpexcel.codeplex.com/workitem/9599)
+- getScaleWithDocument() gives wrong value when using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9600](https://phpexcel.codeplex.com/workitem/9600)
+- PHPExcel_Reader_Excel2007 not reading the first user-defined number format - @MarkBaker [CodePlex #9630](https://phpexcel.codeplex.com/workitem/9630)
+- Print area converted to uppercase after read with PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9647](https://phpexcel.codeplex.com/workitem/9647)
+- Incorrect reading of scope for named range using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9661](https://phpexcel.codeplex.com/workitem/9661)
+- Error with pattern (getFillType) and rbg (getRGB) - @MarkBaker [CodePlex #9690](https://phpexcel.codeplex.com/workitem/9690)
+- AdvancedValueBinder affected by system timezone setting when inserting date values - @Erik Tilt [CodePlex #9712](https://phpexcel.codeplex.com/workitem/9712)
+- PHPExcel_Reader_Excel2007 not reading value of active sheet index - @Erik Tilt [CodePlex #9743](https://phpexcel.codeplex.com/workitem/9743)
+- getARGB() sometimes returns SimpleXMLElement object instead of string with PHPExcel_Reader_Excel2007 - @Erik Tilt [CodePlex #9742](https://phpexcel.codeplex.com/workitem/9742)
+- Negative image offset causes defects in 14excel5.xls and 20readexcel5.xlsx - @Erik Tilt [CodePlex #9731](https://phpexcel.codeplex.com/workitem/9731)
+- HTML & PDF Writer not working with mergeCells (regression since 1.6.5) - @Erik Tilt [CodePlex #9758](https://phpexcel.codeplex.com/workitem/9758)
+- Too wide columns with HTML and PDF writer - @Erik Tilt [CodePlex #9774](https://phpexcel.codeplex.com/workitem/9774)
+- PDF and cyrillic fonts - @MarkBaker [CodePlex #9775](https://phpexcel.codeplex.com/workitem/9775)
+- Percentages not working correctly with HTML and PDF writers (shows 0.25% instead of 25%) - @Erik Tilt [CodePlex #9793](https://phpexcel.codeplex.com/workitem/9793)
+- PHPExcel_Writer_HTML creates extra borders around cell contents using setUseInlineCss(true) - @Erik Tilt [CodePlex #9791](https://phpexcel.codeplex.com/workitem/9791)
+- Problem with text wrap + merged cells in HTML and PDF writer - @Erik Tilt [CodePlex #9784](https://phpexcel.codeplex.com/workitem/9784)
+- Adjacent path separators in include_path causing IOFactory to violate open_basedir restriction - @Erik Tilt [CodePlex #9814](https://phpexcel.codeplex.com/workitem/9814)
+
+## [1.6.6] - 2009-03-02
+
+### General
+
+- Improve support for built-in number formats in PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #9102](https://phpexcel.codeplex.com/workitem/9102)
+- Source files are in both UNIX and DOS formats - changed to UNIX - @Erik Tilt [CodePlex #9281](https://phpexcel.codeplex.com/workitem/9281)
+
+### Features
+
+- Update documentation: Which language to write formulas in? - @MarkBaker [CodePlex #9338](https://phpexcel.codeplex.com/workitem/9338)
+- Ignore DEFCOLWIDTH records with value 8 in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8817](https://phpexcel.codeplex.com/workitem/8817)
+- Support for width, height, offsetX, offsetY for images in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8847](https://phpexcel.codeplex.com/workitem/8847)
+- Disk Caching in specific folder - @MarkBaker [CodePlex #8870](https://phpexcel.codeplex.com/workitem/8870)
+- Added SUMX2MY2, SUMX2PY2, SUMXMY2, MDETERM and MINVERSE Mathematical and Trigonometric Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Added CONVERT Engineering Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Added DB, DDB, DISC, DOLLARDE, DOLLARFR, INTRATE, IPMT, PPMT, PRICEDISC, PRICEMAT and RECEIVED Financial Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Added ACCRINTM, CUMIPMT, CUMPRINC, TBILLEQ, TBILLPRICE, TBILLYIELD, YIELDDISC and YIELDMAT Financial Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Added DOLLAR Text Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Added CORREL, COVAR, FORECAST, INTERCEPT, RSQ, SLOPE and STEYX Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Added PEARSON Statistical Functions as a synonym for CORREL - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Added LINEST, LOGEST (currently only valid for stats = false), TREND and GROWTH Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Added RANK and PERCENTRANK Statistical Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Added ROMAN Mathematical Function (Classic form only) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Update documentation to show example of getCellByColumnAndRow($col, $row) - @MarkBaker [CodePlex #8931](https://phpexcel.codeplex.com/workitem/8931)
+- Implement worksheet, row and cell iterators - @MarkBaker [CodePlex #8770](https://phpexcel.codeplex.com/workitem/8770)
+- Support for arbitrary defined names (named range) - @MarkBaker [CodePlex #9001](https://phpexcel.codeplex.com/workitem/9001)
+- Update formulas when sheet title / named range title changes - @MB, ET [CodePlex #9016](https://phpexcel.codeplex.com/workitem/9016)
+- Ability to read cached calculated value - @MarkBaker [CodePlex #9103](https://phpexcel.codeplex.com/workitem/9103)
+- Support for Excel 1904 calendar date mode (Mac) - @MBaker, ET [CodePlex #8483](https://phpexcel.codeplex.com/workitem/8483)
+- PHPExcel_Writer_Excel5 improvements writing shared strings table - @Erik Tilt [CodePlex #9194](https://phpexcel.codeplex.com/workitem/9194)
+- PHPExcel_Writer_Excel5 iconv fallback when mbstring extension is not enabled - @Erik Tilt [CodePlex #9248](https://phpexcel.codeplex.com/workitem/9248)
+- UTF-8 support in font names in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9253](https://phpexcel.codeplex.com/workitem/9253)
+- Implement value binding architecture - @MarkBaker [CodePlex #9215](https://phpexcel.codeplex.com/workitem/9215)
+- PDF writer not working with UTF-8 - @MarkBaker [CodePlex #6742](https://phpexcel.codeplex.com/workitem/6742)
+
+### Bugfixes
+
+- Eliminate duplicate style entries in multisheet workbook written by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9355](https://phpexcel.codeplex.com/workitem/9355)
+- Redirect to client browser fails due to trailing white space in class definitions - @Erik Tilt [CodePlex #8810](https://phpexcel.codeplex.com/workitem/8810)
+- Spurious column dimension element introduced in blank worksheet after using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8816](https://phpexcel.codeplex.com/workitem/8816)
+- Image gets slightly narrower than expected when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8830](https://phpexcel.codeplex.com/workitem/8830)
+- Image laid over non-visible row gets squeezed in height when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8831](https://phpexcel.codeplex.com/workitem/8831)
+- PHPExcel_Reader_Excel5 fails when there are 10 or more images in the workbook - @Erik Tilt [CodePlex #8860](https://phpexcel.codeplex.com/workitem/8860)
+- Different header/footer images in different sheets not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8909](https://phpexcel.codeplex.com/workitem/8909)
+- Fractional seconds disappear when using PHPExcel_Reader_Excel2007 and PHPExcel_Reader_Excel5 - @MB, ET [CodePlex #8924](https://phpexcel.codeplex.com/workitem/8924)
+- Images not showing in OpenOffice when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7994](https://phpexcel.codeplex.com/workitem/7994)
+- Images not showing on print using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #9047](https://phpexcel.codeplex.com/workitem/9047)
+- PHPExcel_Writer_Excel5 maximum allowed record size 4 bytes too short - @Erik Tilt [CodePlex #9085](https://phpexcel.codeplex.com/workitem/9085)
+- Not numeric strings are formatted as dates and numbers using worksheet's toArray method - @MarkBaker [CodePlex #9119](https://phpexcel.codeplex.com/workitem/9119)
+- Excel5 simple formula parsing error - @Erik Tilt [CodePlex #9132](https://phpexcel.codeplex.com/workitem/9132)
+- Problems writing dates with CSV - @Erik Tilt [CodePlex #9206](https://phpexcel.codeplex.com/workitem/9206)
+- PHPExcel_Reader_Excel5 reader fails with fatal error when reading group shapes - @Erik Tilt [CodePlex #9203](https://phpexcel.codeplex.com/workitem/9203)
+- PHPExcel_Writer_Excel5 fails completely when workbook contains more than 57 colors - @Erik Tilt [CodePlex #9231](https://phpexcel.codeplex.com/workitem/9231)
+- PHPExcel_Writer_PDF not compatible with autoload - @Erik Tilt [CodePlex #9244](https://phpexcel.codeplex.com/workitem/9244)
+- Fatal error: Call to a member function getNestingLevel() on a non-object in PHPExcel/Reader/Excel5.php on line 690 - @Erik Tilt [CodePlex #9250](https://phpexcel.codeplex.com/workitem/9250)
+- Notices when running test 04printing.php on PHP 5.2.8 - @MarkBaker [CodePlex #9246](https://phpexcel.codeplex.com/workitem/9246)
+- insertColumn() spawns creation of spurious RowDimension - @MarkBaker [CodePlex #9294](https://phpexcel.codeplex.com/workitem/9294)
+- Fix declarations for methods in extended Trend classes - @MarkBaker [CodePlex #9296](https://phpexcel.codeplex.com/workitem/9296)
+- Fix to parameters for the FORECAST Statistical Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- PDF writer problems with cell height and text wrapping - @MarkBaker [CodePlex #7083](https://phpexcel.codeplex.com/workitem/7083)
+- Fix test for calculated value in case the returned result is an array - @MarkBaker
+- Column greater than 256 results in corrupt Excel file using PHPExcel_Writer_Excel5 - @Erik Tilt
+- Excel Numberformat 0.00 results in non internal decimal places values in toArray() Method - @MarkBaker [CodePlex #9351](https://phpexcel.codeplex.com/workitem/9351)
+- setAutoSize not taking into account text rotation - @MB,ET [CodePlex #9356](https://phpexcel.codeplex.com/workitem/9356)
+- Call to undefined method PHPExcel_Worksheet_MemoryDrawing::getPath() in PHPExcel/Writer/HTML.php - @Erik Tilt [CodePlex #9372](https://phpexcel.codeplex.com/workitem/9372)
+
+## [1.6.5] - 2009-01-05
+
+### General
+
+- Applied patch 2063 - @MarkBaker
+- Optimise Shared Strings - @MarkBaker
+- Optimise Cell Sorting - @MarkBaker
+- Optimise Style Hashing - @MarkBaker
+- UTF-8 enhancements - @Erik Tilt
+- PHPExcel_Writer_HTML validation errors against strict HTML 4.01 / CSS 2.1 - @Erik Tilt
+- Documented work items 6203 and 8110 in manual - @MarkBaker
+- Restructure package hierachy so classes can be found more easily in auto-generated API (from work item 8468) - @Erik Tilt
+
+### Features
+
+- Redirect output to a client's browser: Update recommendation in documentation - @MarkBaker [CodePlex #8806](https://phpexcel.codeplex.com/workitem/8806)
+- PHPExcel_Reader_Excel5 support for print gridlines - @Erik Tilt [CodePlex #7897](https://phpexcel.codeplex.com/workitem/7897)
+- Screen gridlines support in Excel5 reader/writer - @Erik Tilt [CodePlex #7899](https://phpexcel.codeplex.com/workitem/7899)
+- Option for adding image to spreadsheet from image resource in memory - @MB, ET [CodePlex #7552](https://phpexcel.codeplex.com/workitem/7552)
+- PHPExcel_Reader_Excel5 style support for BIFF5 files (Excel 5.0 - Excel 95) - @Erik Tilt [CodePlex #7862](https://phpexcel.codeplex.com/workitem/7862)
+- PHPExcel_Reader_Excel5 support for user-defined colors and special built-in colors - @Erik Tilt [CodePlex #7918](https://phpexcel.codeplex.com/workitem/7918)
+- Support for freeze panes in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7992](https://phpexcel.codeplex.com/workitem/7992)
+- Support for header and footer margins in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7996](https://phpexcel.codeplex.com/workitem/7996)
+- Support for active sheet index in Excel5 reader/writer - @Erik Tilt [CodePlex #7997](https://phpexcel.codeplex.com/workitem/7997)
+- Freeze panes not read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7991](https://phpexcel.codeplex.com/workitem/7991)
+- Support for screen zoom level (feature request) - @MB, ET [CodePlex #7993](https://phpexcel.codeplex.com/workitem/7993)
+- Support for default style in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8012](https://phpexcel.codeplex.com/workitem/8012)
+- Apple iWork / Numbers.app incompatibility - @MarkBaker [CodePlex #8094](https://phpexcel.codeplex.com/workitem/8094)
+- Support "between rule" in conditional formatting - @MarkBaker [CodePlex #7931](https://phpexcel.codeplex.com/workitem/7931)
+- Comment size, width and height control (feature request) - @MarkBaker [CodePlex #8308](https://phpexcel.codeplex.com/workitem/8308)
+- Improve method for storing MERGEDCELLS records in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8418](https://phpexcel.codeplex.com/workitem/8418)
+- Support for protectCells() in Excel5 reader/writer - @Erik Tilt [CodePlex #8435](https://phpexcel.codeplex.com/workitem/8435)
+- Support for fitToWidth and fitToHeight pagesetup properties in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8472](https://phpexcel.codeplex.com/workitem/8472)
+- Support for setShowSummaryBelow() and setShowSummaryRight() in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8489](https://phpexcel.codeplex.com/workitem/8489)
+- Support for Excel 1904 calendar date mode (Mac) - @MarkBaker [CodePlex #8483](https://phpexcel.codeplex.com/workitem/8483)
+- Excel5 reader: Support for reading images (bitmaps) - @Erik Tilt [CodePlex #7538](https://phpexcel.codeplex.com/workitem/7538)
+- Support for default style in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8787](https://phpexcel.codeplex.com/workitem/8787)
+- Modified calculate() method to return either an array or the first value from the array for those functions that return arrays rather than single values (e.g the MMULT and TRANSPOSE function). This performance can be modified based on the $returnArrayAsType which can be set/retrieved by calling the setArrayReturnType() and getArrayReturnType() methods of the PHPExcel_Calculation class. - @MarkBaker
+
+### Bugfixes
+
+- Added ERROR.TYPE Information Function, MMULT Mathematical and Trigonometry Function, and TRANSPOSE Lookup and Reference Function - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- setPrintGridlines(true) not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7896](https://phpexcel.codeplex.com/workitem/7896)
+- Incorrect mapping of fill patterns in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7907](https://phpexcel.codeplex.com/workitem/7907)
+- setShowGridlines(false) not working with PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7898](https://phpexcel.codeplex.com/workitem/7898)
+- getShowGridlines() gives inverted value when reading sheet with PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7905](https://phpexcel.codeplex.com/workitem/7905)
+- User-defined column width becomes slightly larger after read/write with Excel5 - @Erik Tilt [CodePlex #7944](https://phpexcel.codeplex.com/workitem/7944)
+- Incomplete border style support in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7949](https://phpexcel.codeplex.com/workitem/7949)
+- Conditional formatting "containsText" read/write results in MS Office Excel 2007 crash - @MarkBaker [CodePlex #7928](https://phpexcel.codeplex.com/workitem/7928)
+- All sheets are always selected in output when using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7995](https://phpexcel.codeplex.com/workitem/7995)
+- COLUMN function warning message during plain read/write - @MarkBaker [CodePlex #8013](https://phpexcel.codeplex.com/workitem/8013)
+- setValue(0) results in string data type '0' - @MarkBaker [CodePlex #8155](https://phpexcel.codeplex.com/workitem/8155)
+- Styles not removed when removing rows from sheet - @MarkBaker [CodePlex #8226](https://phpexcel.codeplex.com/workitem/8226)
+- =IF formula causes fatal error during $objWriter->save() in Excel2007 format - @MarkBaker [CodePlex #8301](https://phpexcel.codeplex.com/workitem/8301)
+- Exception thrown reading valid xls file: "Excel file is corrupt. Didn't find CONTINUE record while reading shared strings" - @Erik Tilt [CodePlex #8333](https://phpexcel.codeplex.com/workitem/8333)
+- MS Outlook corrupts files generated by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8320](https://phpexcel.codeplex.com/workitem/8320)
+- Undefined method PHPExcel_Worksheet::setFreezePane() in ReferenceHelper.php on line 271 - @MarkBaker [CodePlex #8351](https://phpexcel.codeplex.com/workitem/8351)
+- Ampersands (&), left and right angles (<, >) in Rich-Text strings leads to corrupt output using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #8401](https://phpexcel.codeplex.com/workitem/8401)
+- Print header and footer not supporting UTF-8 in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8408](https://phpexcel.codeplex.com/workitem/8408)
+- Vertical page breaks not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8463](https://phpexcel.codeplex.com/workitem/8463)
+- Missing support for accounting underline types in PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8476](https://phpexcel.codeplex.com/workitem/8476)
+- Infinite loops when reading corrupt xls file using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8482](https://phpexcel.codeplex.com/workitem/8482)
+- Sheet protection password not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8566](https://phpexcel.codeplex.com/workitem/8566)
+- PHPExcel_Style_NumberFormat::FORMAT_NUMBER ignored by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8596](https://phpexcel.codeplex.com/workitem/8596)
+- PHPExcel_Reader_Excel5 fails a whole when workbook contains a chart - @Erik Tilt [CodePlex #8781](https://phpexcel.codeplex.com/workitem/8781)
+- Occasional loss of column widths using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #8788](https://phpexcel.codeplex.com/workitem/8788)
+- Notices while reading formulas with deleted sheet references using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #8795](https://phpexcel.codeplex.com/workitem/8795)
+- Default style not read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #8807](https://phpexcel.codeplex.com/workitem/8807)
+- Blank rows occupy too much space in file generated by PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #9341](https://phpexcel.codeplex.com/workitem/9341)
+
+## [1.6.4] - 2008-10-27
+
+### Features
+
+- RK record number error in MS developer documentation: 0x007E should be 0x027E - @Erik Tilt [CodePlex #7882](https://phpexcel.codeplex.com/workitem/7882)
+- getHighestColumn() returning "@" for blank worksheet causes corrupt output - @MarkBaker [CodePlex #7878](https://phpexcel.codeplex.com/workitem/7878)
+- Implement ROW and COLUMN Lookup/Reference Functions (when specified with a parameter) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Implement initial work on OFFSET Lookup/Reference Function (returning address rather than value at address) - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Excel5 reader: Page margins - @Erik Tilt [CodePlex #7416](https://phpexcel.codeplex.com/workitem/7416)
+- Excel5 reader: Header & Footer - @Erik Tilt [CodePlex #7417](https://phpexcel.codeplex.com/workitem/7417)
+- Excel5 reader support for page setup (paper size etc.) - @Erik Tilt [CodePlex #7449](https://phpexcel.codeplex.com/workitem/7449)
+- Improve speed and memory consumption of PHPExcel_Writer_CSV - @MarkBaker [CodePlex #7445](https://phpexcel.codeplex.com/workitem/7445)
+- Better recognition of number format in HTML, CSV, and PDF writer - @MarkBaker [CodePlex #7432](https://phpexcel.codeplex.com/workitem/7432)
+- Font support: Superscript and Subscript - @MarkBaker [CodePlex #7485](https://phpexcel.codeplex.com/workitem/7485)
+- Excel5 reader font support: Super- and subscript - @Erik Tilt [CodePlex #7509](https://phpexcel.codeplex.com/workitem/7509)
+- Excel5 reader style support: Text rotation and stacked text - @Erik Tilt [CodePlex #7521](https://phpexcel.codeplex.com/workitem/7521)
+- Excel5 reader: Support for hyperlinks - @Erik Tilt [CodePlex #7530](https://phpexcel.codeplex.com/workitem/7530)
+- Import sheet by request - @MB, ET [CodePlex #7557](https://phpexcel.codeplex.com/workitem/7557)
+- PHPExcel_Reader_Excel5 support for page breaks - @Erik Tilt [CodePlex #7607](https://phpexcel.codeplex.com/workitem/7607)
+- PHPExcel_Reader_Excel5 support for shrink-to-fit - @Erik Tilt [CodePlex #7622](https://phpexcel.codeplex.com/workitem/7622)
+- Support for error types - @MB, ET [CodePlex #7675](https://phpexcel.codeplex.com/workitem/7675)
+- Excel5 reader true formula support - @Erik Tilt [CodePlex #7388](https://phpexcel.codeplex.com/workitem/7388)
+- Support for named ranges (defined names) in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7701](https://phpexcel.codeplex.com/workitem/7701)
+- Support for repeating rows and repeating columns (print titles) in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7781](https://phpexcel.codeplex.com/workitem/7781)
+- Support for print area in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7783](https://phpexcel.codeplex.com/workitem/7783)
+- Excel5 reader and writer support for horizontal and vertical centering of page - @Erik Tilt [CodePlex #7795](https://phpexcel.codeplex.com/workitem/7795)
+- Applied patch 1962 - @MarkBaker
+- Excel5 reader and writer support for hidden cells (formulas) - @Erik Tilt [CodePlex #7866](https://phpexcel.codeplex.com/workitem/7866)
+- Support for indentation in cells (feature request) - @MB, ET [CodePlex #7612](https://phpexcel.codeplex.com/workitem/7612)
+
+### Bugfixes
+
+- Option for reading only specified interval of rows in a sheet - @MB, ET [CodePlex #7828](https://phpexcel.codeplex.com/workitem/7828)
+- PHPExcel_Calculation_Functions::DATETIMENOW() and PHPExcel_Calculation_Functions::DATENOW() to force UTC - @MarkBaker [CodePlex #7367](https://phpexcel.codeplex.com/workitem/7367)
+- Modified PHPExcel_Shared_Date::FormattedPHPToExcel() and PHPExcel_Shared_Date::ExcelToPHP to force datatype for return values - @MarkBaker [CodePlex #7395](https://phpexcel.codeplex.com/workitem/7395)
+- Excel5 reader not producing UTF-8 strings with BIFF5 files - @Erik Tilt [CodePlex #7450](https://phpexcel.codeplex.com/workitem/7450)
+- Array constant in formula gives run-time notice with Excel2007 writer - @MarkBaker [CodePlex #7470](https://phpexcel.codeplex.com/workitem/7470)
+- PHPExcel_Reader_Excel2007 setReadDataOnly(true) returns Rich-Text - @MarkBaker [CodePlex #7494](https://phpexcel.codeplex.com/workitem/7494)
+- PHPExcel_Reader_Excel5 setReadDataOnly(true) returns Rich-Text - @Erik Tilt [CodePlex #7496](https://phpexcel.codeplex.com/workitem/7496)
+- Characters before superscript or subscript losing style - @MarkBaker [CodePlex #7497](https://phpexcel.codeplex.com/workitem/7497)
+- Subscript not working with HTML writer - @MarkBaker [CodePlex #7507](https://phpexcel.codeplex.com/workitem/7507)
+- DefaultColumnDimension not working on first column (A) - @MarkBaker [CodePlex #7508](https://phpexcel.codeplex.com/workitem/7508)
+- Negative numbers are stored as text in PHPExcel_Writer_2007 - @MarkBaker [CodePlex #7527](https://phpexcel.codeplex.com/workitem/7527)
+- Text rotation and stacked text not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7531](https://phpexcel.codeplex.com/workitem/7531)
+- PHPExcel_Shared_Date::isDateTimeFormatCode erroneously says true - @MarkBaker [CodePlex #7536](https://phpexcel.codeplex.com/workitem/7536)
+- Different images with same filename in separate directories become duplicates - @MarkBaker [CodePlex #7559](https://phpexcel.codeplex.com/workitem/7559)
+- PHPExcel_Reader_Excel5 not returning sheet names as UTF-8 using for Excel 95 files - @Erik Tilt [CodePlex #7568](https://phpexcel.codeplex.com/workitem/7568)
+- setAutoSize(true) on empty column gives column width of 10 using PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #7575](https://phpexcel.codeplex.com/workitem/7575)
+- setAutoSize(true) on empty column gives column width of 255 using PHPExcel_Writer_Excel5 - @MB, ET [CodePlex #7573](https://phpexcel.codeplex.com/workitem/7573)
+- Worksheet_Drawing bug - @MarkBaker [CodePlex #7514](https://phpexcel.codeplex.com/workitem/7514)
+- getCalculatedValue() with REPT function causes script to stop - @MarkBaker [CodePlex #7593](https://phpexcel.codeplex.com/workitem/7593)
+- getCalculatedValue() with LEN function causes script to stop - @MarkBaker [CodePlex #7594](https://phpexcel.codeplex.com/workitem/7594)
+- Explicit fit-to-width (page setup) results in fit-to-height becoming 1 - @MarkBaker [CodePlex #7600](https://phpexcel.codeplex.com/workitem/7600)
+- Fit-to-width value of 1 is lost after read/write of Excel2007 spreadsheet - @MarkBaker [CodePlex #7610](https://phpexcel.codeplex.com/workitem/7610)
+- Conditional styles not read properly using PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7516](https://phpexcel.codeplex.com/workitem/7516)
+- PHPExcel_Writer_2007: Default worksheet style works only for first sheet - @MarkBaker [CodePlex #7611](https://phpexcel.codeplex.com/workitem/7611)
+- Cannot Lock Cells using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #6940](https://phpexcel.codeplex.com/workitem/6940)
+- Incorrect cell protection values found when using Excel5 reader - @Erik Tilt [CodePlex #7621](https://phpexcel.codeplex.com/workitem/7621)
+- Default row height not working above highest row using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7623](https://phpexcel.codeplex.com/workitem/7623)
+- Default column width does not get applied when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7637](https://phpexcel.codeplex.com/workitem/7637)
+- Broken support for UTF-8 string formula results in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7642](https://phpexcel.codeplex.com/workitem/7642)
+- UTF-8 sheet names not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7643](https://phpexcel.codeplex.com/workitem/7643)
+- getCalculatedValue() with ISNONTEXT function causes script to stop - @MarkBaker [CodePlex #7631](https://phpexcel.codeplex.com/workitem/7631)
+- Missing BIFF3 functions in PHPExcel_Writer_Excel5: USDOLLAR (YEN), FINDB, SEARCHB, REPLACEB, LEFTB, RIGHTB, MIDB, LENB, ASC, DBCS (JIS) - @Erik Tilt [CodePlex #7652](https://phpexcel.codeplex.com/workitem/7652)
+- Excel5 reader doesn't read numbers correctly in 64-bit systems - @Erik Tilt [CodePlex #7663](https://phpexcel.codeplex.com/workitem/7663)
+- Missing BIFF5 functions in PHPExcel_Writer_Excel5: ISPMT, DATEDIF, DATESTRING, NUMBERSTRING - @Erik Tilt [CodePlex #7667](https://phpexcel.codeplex.com/workitem/7667)
+- Missing BIFF8 functions in PHPExcel_Writer_Excel5: GETPIVOTDATA, HYPERLINK, PHONETIC, AVERAGEA, MAXA, MINA, STDEVPA, VARPA, STDEVA, VARA - @Erik Tilt [CodePlex #7668](https://phpexcel.codeplex.com/workitem/7668)
+- Wrong host value in PHPExcel_Shared_ZipStreamWrapper::stream_open() - @MarkBaker [CodePlex #7657](https://phpexcel.codeplex.com/workitem/7657)
+- PHPExcel_Reader_Excel5 not reading explicitly entered error types in cells - @Erik Tilt [CodePlex #7676](https://phpexcel.codeplex.com/workitem/7676)
+- Boolean and error data types not preserved for formula results in PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7678](https://phpexcel.codeplex.com/workitem/7678)
+- PHPExcel_Reader_Excel2007 ignores cell data type - @MarkBaker [CodePlex #7695](https://phpexcel.codeplex.com/workitem/7695)
+- PHPExcel_Reader_Excel5 ignores cell data type - @Erik Tilt [CodePlex #7712](https://phpexcel.codeplex.com/workitem/7712)
+- PHPExcel_Writer_Excel5 not aware of data type - @Erik Tilt [CodePlex #7587](https://phpexcel.codeplex.com/workitem/7587)
+- Long strings sometimes truncated when using PHPExcel_Reader_Excel5 - @Erik Tilt [CodePlex #7713](https://phpexcel.codeplex.com/workitem/7713)
+- Direct entry of boolean or error type in cell not supported by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7727](https://phpexcel.codeplex.com/workitem/7727)
+- PHPExcel_Reader_Excel2007: Error reading cell with data type string, date number format, and numeric-like cell value - @MarkBaker [CodePlex #7714](https://phpexcel.codeplex.com/workitem/7714)
+- Row and column outlines (group indent level) not showing after using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7735](https://phpexcel.codeplex.com/workitem/7735)
+- Missing UTF-8 support in number format codes for PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7737](https://phpexcel.codeplex.com/workitem/7737)
+- Missing UTF-8 support with PHPExcel_Writer_Excel5 for explicit string in formula - @Erik Tilt [CodePlex #7750](https://phpexcel.codeplex.com/workitem/7750)
+- Problem with class constants in PHPExcel_Style_NumberFormat - @MarkBaker [CodePlex #7726](https://phpexcel.codeplex.com/workitem/7726)
+- Sometimes errors with PHPExcel_Reader_Excel5 reading hyperlinks - @Erik Tilt [CodePlex #7758](https://phpexcel.codeplex.com/workitem/7758)
+- Hyperlink in cell always results in string data type when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7759](https://phpexcel.codeplex.com/workitem/7759)
+- Excel file with blank sheet seen as broken in MS Office Excel 2007 when created by PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7771](https://phpexcel.codeplex.com/workitem/7771)
+- PHPExcel_Reader_Excel5: Incorrect reading of formula with explicit string containing (escaped) double-quote - @Erik Tilt [CodePlex #7785](https://phpexcel.codeplex.com/workitem/7785)
+- getCalculatedValue() fails on formula with sheet name containing (escaped) single-quote - @MarkBaker [CodePlex #7787](https://phpexcel.codeplex.com/workitem/7787)
+- getCalculatedValue() fails on formula with explicit string containing (escaped) double-quote - @MarkBaker [CodePlex #7786](https://phpexcel.codeplex.com/workitem/7786)
+- Problems with simultaneous repeatRowsAtTop and repeatColumnsAtLeft using Excel2007 reader and writer - @MarkBaker [CodePlex #7780](https://phpexcel.codeplex.com/workitem/7780)
+- PHPExcel_Reader_Excel5: Error reading formulas with sheet reference containing special characters - @Erik Tilt [CodePlex #7802](https://phpexcel.codeplex.com/workitem/7802)
+- Off-sheet references sheet!A1 not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7831](https://phpexcel.codeplex.com/workitem/7831)
+- Repeating rows/columns (print titles), print area not working with PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7834](https://phpexcel.codeplex.com/workitem/7834)
+- Formula having datetime number format shows as text when using PHPExcel_Writer_Excel5 - @Erik Tilt [CodePlex #7849](https://phpexcel.codeplex.com/workitem/7849)
+- Cannot set formula to hidden using applyFromArray() - @MarkBaker [CodePlex #7863](https://phpexcel.codeplex.com/workitem/7863)
+- HTML/PDF Writers limited to 26 columns by calculateWorksheetDimension (erroneous comparison in getHighestColumn() method) - @MarkBaker [CodePlex #7805](https://phpexcel.codeplex.com/workitem/7805)
+- Formula returning error type is lost when read by PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #7873](https://phpexcel.codeplex.com/workitem/7873)
+- PHPExcel_Reader_Excel5: Cell style lost for last column in group of blank cells - @Erik Tilt [CodePlex #7883](https://phpexcel.codeplex.com/workitem/7883)
+- Column width sometimes collapses to auto size using Excel2007 reader/writer - @MarkBaker [CodePlex #7886](https://phpexcel.codeplex.com/workitem/7886)
+- Data Validation Formula = 0 crashes Excel - @MarkBaker [CodePlex #9343](https://phpexcel.codeplex.com/workitem/9343)
+
+## [1.6.3] - 2008-08-25
+
+### General
+
+- Modified PHPExcel_Shared_Date::PHPToExcel() to force UTC - @MarkBaker [CodePlex #7367](https://phpexcel.codeplex.com/workitem/7367)
+- Applied patch 1629 - @MarkBaker
+- Applied patch 1644 - @MarkBaker
+- Implement repeatRow and repeatColumn in Excel5 writer - @MarkBaker [CodePlex #6485](https://phpexcel.codeplex.com/workitem/6485)
+
+### Features
+
+- Remove scene3d filter in Excel2007 drawing - @MarkBaker [CodePlex #6838](https://phpexcel.codeplex.com/workitem/6838)
+- Implement CHOOSE and INDEX Lookup/Reference Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Implement CLEAN Text Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Implement YEARFRAC Date/Time Functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Implement 2 options for print/show gridlines - @MarkBaker [CodePlex #6508](https://phpexcel.codeplex.com/workitem/6508)
+- Add VLOOKUP function (contribution) - @MarkBaker [CodePlex #7270](https://phpexcel.codeplex.com/workitem/7270)
+- Implemented: ShrinkToFit - @MarkBaker [CodePlex #7182](https://phpexcel.codeplex.com/workitem/7182)
+- Row heights not updated correctly when inserting new rows - @MarkBaker [CodePlex #7218](https://phpexcel.codeplex.com/workitem/7218)
+- Copy worksheets within the same workbook - @MarkBaker [CodePlex #7157](https://phpexcel.codeplex.com/workitem/7157)
+- Excel5 reader style support: horizontal and vertical alignment plus text wrap - @Erik Tilt [CodePlex #7290](https://phpexcel.codeplex.com/workitem/7290)
+- Excel5 reader support for merged cells - @Erik Tilt [CodePlex #7294](https://phpexcel.codeplex.com/workitem/7294)
+- Excel5 reader: Sheet Protection - @Erik Tilt [CodePlex #7296](https://phpexcel.codeplex.com/workitem/7296)
+- Excel5 reader: Password for sheet protection - @Erik Tilt [CodePlex #7297](https://phpexcel.codeplex.com/workitem/7297)
+- Excel5 reader: Column width - @Erik Tilt [CodePlex #7299](https://phpexcel.codeplex.com/workitem/7299)
+- Excel5 reader: Row height - @Erik Tilt [CodePlex #7301](https://phpexcel.codeplex.com/workitem/7301)
+- Excel5 reader: Font support - @Erik Tilt [CodePlex #7304](https://phpexcel.codeplex.com/workitem/7304)
+- Excel5 reader: support for locked cells - @Erik Tilt [CodePlex #7324](https://phpexcel.codeplex.com/workitem/7324)
+- Excel5 reader style support: Fill (background colors and patterns) - @Erik Tilt [CodePlex #7330](https://phpexcel.codeplex.com/workitem/7330)
+- Excel5 reader style support: Borders (style and color) - @Erik Tilt [CodePlex #7332](https://phpexcel.codeplex.com/workitem/7332)
+- Excel5 reader: Rich-Text support - @Erik Tilt [CodePlex #7346](https://phpexcel.codeplex.com/workitem/7346)
+- Read Excel built-in number formats with Excel 2007 reader - @MarkBaker [CodePlex #7313](https://phpexcel.codeplex.com/workitem/7313)
+- Excel5 reader: Number format support - @Erik Tilt [CodePlex #7317](https://phpexcel.codeplex.com/workitem/7317)
+- Creating a copy of PHPExcel object - @MarkBaker [CodePlex #7362](https://phpexcel.codeplex.com/workitem/7362)
+- Excel5 reader: support for row / column outline (group) - @Erik Tilt [CodePlex #7373](https://phpexcel.codeplex.com/workitem/7373)
+- Implement default row/column sizes - @MarkBaker [CodePlex #7380](https://phpexcel.codeplex.com/workitem/7380)
+- Writer HTML - option to return styles and table separately - @MarkBaker [CodePlex #7364](https://phpexcel.codeplex.com/workitem/7364)
+
+### Bugfixes
+
+- Excel5 reader: Support for remaining built-in number formats - @Erik Tilt [CodePlex #7393](https://phpexcel.codeplex.com/workitem/7393)
+- Fixed rounding in HOUR MINUTE and SECOND Time functions, and improved performance for these - @MarkBaker
+- Fix to TRIM function - @MarkBaker
+- Fixed range validation in TIME Functions.php - @MarkBaker
+- EDATE and EOMONTH functions now return date values based on the returnDateType flag - @MarkBaker
+- Write date values that are the result of a calculation function correctly as Excel serialized dates rather than PHP serialized date values - @MarkBaker
+- Excel2007 reader not always reading boolean correctly - @MarkBaker [CodePlex #6690](https://phpexcel.codeplex.com/workitem/6690)
+- Columns above IZ - @MarkBaker [CodePlex #6275](https://phpexcel.codeplex.com/workitem/6275)
+- Other locale than English causes Excel2007 writer to produce broken xlsx - @MarkBaker [CodePlex #6853](https://phpexcel.codeplex.com/workitem/6853)
+- Typo: Number_fromat in NumberFormat.php - @MarkBaker [CodePlex #7061](https://phpexcel.codeplex.com/workitem/7061)
+- Bug in Worksheet_BaseDrawing setWidth() - @MarkBaker [CodePlex #6865](https://phpexcel.codeplex.com/workitem/6865)
+- PDF writer collapses column width for merged cells - @MarkBaker [CodePlex #6891](https://phpexcel.codeplex.com/workitem/6891)
+- Issues with drawings filenames - @MarkBaker [CodePlex #6867](https://phpexcel.codeplex.com/workitem/6867)
+- fromArray() local variable isn't defined - @MarkBaker [CodePlex #7073](https://phpexcel.codeplex.com/workitem/7073)
+- PHPExcel_Writer_Excel5->setTempDir() not passed to all classes involved in writing to a file - @MarkBaker [CodePlex #7276](https://phpexcel.codeplex.com/workitem/7276)
+- Excel5 reader not handling UTF-8 properly - @MarkBaker [CodePlex #7277](https://phpexcel.codeplex.com/workitem/7277)
+- If you write a 0 value in cell, cell shows as empty - @MarkBaker [CodePlex #7327](https://phpexcel.codeplex.com/workitem/7327)
+- Excel2007 writer: Row height ignored for empty rows - @MarkBaker [CodePlex #7302](https://phpexcel.codeplex.com/workitem/7302)
+- Excel2007 (comments related error) - @MarkBaker [CodePlex #7281](https://phpexcel.codeplex.com/workitem/7281)
+- Column width in other locale - @MarkBaker [CodePlex #7345](https://phpexcel.codeplex.com/workitem/7345)
+- Excel2007 reader not reading underlined Rich-Text - @MarkBaker [CodePlex #7347](https://phpexcel.codeplex.com/workitem/7347)
+- Excel5 reader converting booleans to strings - @Erik Tilt [CodePlex #7357](https://phpexcel.codeplex.com/workitem/7357)
+- Recursive Object Memory Leak - @MarkBaker [CodePlex #7365](https://phpexcel.codeplex.com/workitem/7365)
+- Excel2007 writer ignoring row dimensions without cells - @MarkBaker [CodePlex #7372](https://phpexcel.codeplex.com/workitem/7372)
+- Excel5 reader is converting formatted numbers / dates to strings - @Erik Tilt [CodePlex #7382](https://phpexcel.codeplex.com/workitem/7382)
+
+## [1.6.2] - 2008-06-23
+
+### General
+
+- Document style array values - @MarkBaker [CodePlex #6088](https://phpexcel.codeplex.com/workitem/6088)
+- Applied patch 1195 - @MarkBaker
+- Redirecting output to a client’s web browser - http headers - @MarkBaker [CodePlex #6178](https://phpexcel.codeplex.com/workitem/6178)
+- Improve worksheet garbage collection - @MarkBaker [CodePlex #6187](https://phpexcel.codeplex.com/workitem/6187)
+- Functions that return date values can now be configured to return as Excel serialized date/time, PHP serialized date/time, or a PHP date/time object. - @MarkBaker
+- Functions that explicitly accept dates as parameters now permit values as Excel serialized date/time, PHP serialized date/time, a valid date string, or a PHP date/time object. - @MarkBaker
+- Implement ACOSH, ASINH and ATANH functions for those operating platforms/PHP versions that don't include these functions - @MarkBaker
+- Implement ATAN2 logic reversing the arguments as per Excel - @MarkBaker
+- Additional validation of parameters for COMBIN - @MarkBaker
+
+### Features
+
+- Fixed validation for CEILING and FLOOR when the value and significance parameters have different signs; and allowed default value of 1 or -1 for significance when in GNUMERIC compatibility mode - @MarkBaker
+- Implement ADDRESS, ISLOGICAL, ISTEXT and ISNONTEXT functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Implement COMPLEX, IMAGINARY, IMREAL, IMARGUMENT, IMCONJUGATE, IMABS, IMSUB, IMDIV, IMSUM, IMPRODUCT, IMSQRT, IMEXP, IMLN, IMLOG10, IMLOG2, IMPOWER IMCOS and IMSIN Engineering functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Implement NETWORKDAYS and WORKDAY Date/Time functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+- Make cell column AAA available - @MarkBaker [CodePlex #6100](https://phpexcel.codeplex.com/workitem/6100)
+- Mark particular cell as selected when opening Excel - @MarkBaker [CodePlex #6095](https://phpexcel.codeplex.com/workitem/6095)
+- Multiple sheets in PDF and HTML - @MarkBaker [CodePlex #6120](https://phpexcel.codeplex.com/workitem/6120)
+- Implement PHPExcel_ReaderFactory and PHPExcel_WriterFactory - @MarkBaker [CodePlex #6227](https://phpexcel.codeplex.com/workitem/6227)
+- Set image root of PHPExcel_Writer_HTML - @MarkBaker [CodePlex #6249](https://phpexcel.codeplex.com/workitem/6249)
+- Enable/disable calculation cache - @MarkBaker [CodePlex #6264](https://phpexcel.codeplex.com/workitem/6264)
+- PDF writer and multi-line text - @MarkBaker [CodePlex #6259](https://phpexcel.codeplex.com/workitem/6259)
+- Feature request - setCacheExpirationTime() - @MarkBaker [CodePlex #6350](https://phpexcel.codeplex.com/workitem/6350)
+- Implement late-binding mechanisms to reduce memory footprint - @JB [CodePlex #6370](https://phpexcel.codeplex.com/workitem/6370)
+- Implement shared styles - @JB [CodePlex #6430](https://phpexcel.codeplex.com/workitem/6430)
+- Copy sheet from external Workbook to active Workbook - @MarkBaker [CodePlex #6391](https://phpexcel.codeplex.com/workitem/6391)
+
+### Bugfixes
+
+- Functions in Conditional Formatting - @MarkBaker [CodePlex #6428](https://phpexcel.codeplex.com/workitem/6428)
+- Default Style in Excel5 - @MarkBaker [CodePlex #6096](https://phpexcel.codeplex.com/workitem/6096)
+- Numbers starting with '+' cause Excel 2007 errors - @MarkBaker [CodePlex #6150](https://phpexcel.codeplex.com/workitem/6150)
+- ExcelWriter5 is not PHP5 compatible, using it with E_STRICT results in a bunch of errors (applied patches) - @MarkBaker [CodePlex #6092](https://phpexcel.codeplex.com/workitem/6092)
+- Error Reader Excel2007 line 653 foreach ($relsDrawing->Relationship as $ele) - @MarkBaker [CodePlex #6179](https://phpexcel.codeplex.com/workitem/6179)
+- Worksheet toArray() screws up DATE - @MarkBaker [CodePlex #6229](https://phpexcel.codeplex.com/workitem/6229)
+- References to a Richtext cell in a formula - @MarkBaker [CodePlex #6253](https://phpexcel.codeplex.com/workitem/6253)
+- insertNewColumnBefore Bug - @MarkBaker [CodePlex #6285](https://phpexcel.codeplex.com/workitem/6285)
+- Error reading Excel2007 file with shapes - @MarkBaker [CodePlex #6319](https://phpexcel.codeplex.com/workitem/6319)
+- Determine whether date values need conversion from PHP dates to Excel dates before writing to file, based on the data type (float or integer) - @MarkBaker [CodePlex #6302](https://phpexcel.codeplex.com/workitem/6302)
+- Fixes to DATE function when it is given negative input parameters - @MarkBaker
+- PHPExcel handles empty cells other than Excel - @MarkBaker [CodePlex #6347](https://phpexcel.codeplex.com/workitem/6347)
+- PHPExcel handles 0 and "" as being the same - @MarkBaker [CodePlex #6348](https://phpexcel.codeplex.com/workitem/6348)
+- Problem Using Excel2007 Reader for Spreadsheets containing images - @MarkBaker [CodePlex #6357](https://phpexcel.codeplex.com/workitem/6357)
+- ShowGridLines ignored when reading/writing Excel 2007 - @MarkBaker [CodePlex #6359](https://phpexcel.codeplex.com/workitem/6359)
+- Bug With Word Wrap in Excel 2007 Reader - @MarkBaker [CodePlex #6426](https://phpexcel.codeplex.com/workitem/6426)
+
+## [1.6.1] - 2008-04-28
+
+### General
+
+- Fix documentation printing - @MarkBaker [CodePlex #5532](https://phpexcel.codeplex.com/workitem/5532)
+- Memory usage improvements - @MarkBaker [CodePlex #5586](https://phpexcel.codeplex.com/workitem/5586)
+- Applied patch 990 - @MarkBaker
+
+### Features
+
+- Applied patch 991 - @MarkBaker
+- Implement PHPExcel_Reader_Excel5 - @BM [CodePlex #2841](https://phpexcel.codeplex.com/workitem/2841)
+- Implement "toArray" and "fromArray" method - @MarkBaker [CodePlex #5564](https://phpexcel.codeplex.com/workitem/5564)
+- Read shared formula - @MarkBaker [CodePlex #5665](https://phpexcel.codeplex.com/workitem/5665)
+- Read image twoCellAnchor - @MarkBaker [CodePlex #5681](https://phpexcel.codeplex.com/workitem/5681)
+- &G Image as bg for headerfooter - @MarkBaker [CodePlex #4446](https://phpexcel.codeplex.com/workitem/4446)
+- Implement page layout functionality for Excel5 format - @MarkBaker [CodePlex #5834](https://phpexcel.codeplex.com/workitem/5834)
+
+### Bugfixes
+
+- Feature request: PHPExcel_Writer_PDF - @MarkBaker [CodePlex #6039](https://phpexcel.codeplex.com/workitem/6039)
+- DefinedNames null check - @MarkBaker [CodePlex #5517](https://phpexcel.codeplex.com/workitem/5517)
+- Hyperlinks should not always have trailing slash - @MarkBaker [CodePlex #5463](https://phpexcel.codeplex.com/workitem/5463)
+- Saving Error - Uncaught exception (#REF! named range) - @MarkBaker [CodePlex #5592](https://phpexcel.codeplex.com/workitem/5592)
+- Error when creating Zip file on Linux System (Not Windows) - @MarkBaker [CodePlex #5634](https://phpexcel.codeplex.com/workitem/5634)
+- Time incorrecly formated - @MarkBaker [CodePlex #5876](https://phpexcel.codeplex.com/workitem/5876)
+- Conditional formatting - second rule not applied - @MarkBaker [CodePlex #5914](https://phpexcel.codeplex.com/workitem/5914)
+- PHPExcel_Reader_Excel2007 cannot load PHPExcel_Shared_File - @MarkBaker [CodePlex #5978](https://phpexcel.codeplex.com/workitem/5978)
+- Output redirection to web browser - @MarkBaker [CodePlex #6020](https://phpexcel.codeplex.com/workitem/6020)
+
+## [1.6.0] - 2008-02-14
+
+### Features
+
+- Use PHPExcel datatypes in formula calculation - @MarkBaker [CodePlex #3156](https://phpexcel.codeplex.com/workitem/3156)
+- Center on page when printing - @MarkBaker [CodePlex #5019](https://phpexcel.codeplex.com/workitem/5019)
+- Hyperlink to other spreadsheet - @MarkBaker [CodePlex #5099](https://phpexcel.codeplex.com/workitem/5099)
+- Set the print area of a worksheet - @MarkBaker [CodePlex #5104](https://phpexcel.codeplex.com/workitem/5104)
+- Read "definedNames" property of worksheet - @MarkBaker [CodePlex #5118](https://phpexcel.codeplex.com/workitem/5118)
+- Set default style for all cells - @MarkBaker [CodePlex #5338](https://phpexcel.codeplex.com/workitem/5338)
+- Named Ranges - @MarkBaker [CodePlex #4216](https://phpexcel.codeplex.com/workitem/4216)
+
+### Bugfixes
+
+- Implement worksheet references (Sheet1!A1) - @MarkBaker [CodePlex #5398](https://phpexcel.codeplex.com/workitem/5398)
+- Redirect output to a client's web browser - @MarkBaker [CodePlex #4967](https://phpexcel.codeplex.com/workitem/4967)
+- "File Error: data may have been lost." seen in Excel 2007 and Excel 2003 SP3 when opening XLS file - @MarkBaker [CodePlex #5008](https://phpexcel.codeplex.com/workitem/5008)
+- Bug in style's getHashCode() - @MarkBaker [CodePlex #5165](https://phpexcel.codeplex.com/workitem/5165)
+- PHPExcel_Reader not correctly reading numeric values - @MarkBaker [CodePlex #5165](https://phpexcel.codeplex.com/workitem/5165)
+- Text rotation is read incorrectly - @MarkBaker [CodePlex #5324](https://phpexcel.codeplex.com/workitem/5324)
+- Enclosure " and data " result a bad data : \" instead of "" - @MarkBaker [CodePlex #5326](https://phpexcel.codeplex.com/workitem/5326)
+- Formula parser - IF statement returning array instead of scalar - @MarkBaker [CodePlex #5332](https://phpexcel.codeplex.com/workitem/5332)
+- setFitToWidth(nbpage) & setFitToWidth(nbpage) work partially - @MarkBaker [CodePlex #5351](https://phpexcel.codeplex.com/workitem/5351)
+- Worksheet::setTitle() causes unwanted renaming - @MarkBaker [CodePlex #5361](https://phpexcel.codeplex.com/workitem/5361)
+- Hyperlinks not working. Results in broken xlsx file. - @MarkBaker [CodePlex #5407](https://phpexcel.codeplex.com/workitem/5407)
+
+## [1.5.5] - 2007-12-24
+
+### General
+
+- Grouping Rows - @MarkBaker [CodePlex #4135](https://phpexcel.codeplex.com/workitem/4135)
+
+### Features
+
+- Semi-nightly builds - @MarkBaker [CodePlex #4427](https://phpexcel.codeplex.com/workitem/4427)
+- Implement "date" datatype - @MarkBaker [CodePlex #3155](https://phpexcel.codeplex.com/workitem/3155)
+- Date format not honored in CSV writer - @MarkBaker [CodePlex #4150](https://phpexcel.codeplex.com/workitem/4150)
+- RichText and sharedStrings - @MarkBaker [CodePlex #4199](https://phpexcel.codeplex.com/workitem/4199)
+- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+ - Addition of DATE, DATEDIF, DATEVALUE, DAY, DAYS360- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+ - Addition of AVEDEV, HARMEAN and GEOMEAN
+ - Addition of the BINOMDIST (Non-cumulative only), COUNTBLANK, EXPONDIST, FISHER, FISHERINV, NORMDIST, NORMSDIST, PERMUT, POISSON (Non-cumulative only) and STANDARDIZE Statistical Functions
+ - Addition of the CEILING, COMBIN, EVEN, FACT, FACTDOUBLE, FLOOR, MULTINOMIAL, ODD, ROUNDDOWN, ROUNDUP, SIGN, SQRTPI and SUMSQ Mathematical Functions
+ - Addition of the NORMINV, NORMSINV, CONFIDENCE and SKEW Statistical Functions
+ - Addition of the CRITBINOM, HYPGEOMDIST, KURT, LOGINV, LOGNORMDIST, NEGBINOMDIST and WEIBULL Statistical Functions
+ - Addition of the LARGE, PERCENTILE, QUARTILE, SMALL and TRIMMEAN Statistical Functions
+ - Addition of the BIN2HEX, BIN2OCT, DELTA, ERF, ERFC, GESTEP, HEX2BIN, HEX2DEC, HEX2OCT, OCT2BIN and OCT2HEX Engineering Functions
+ - Addition of the CHIDIST, GAMMADIST and GAMMALN Statistical Functions
+ - Addition of the GCD, LCM, MROUND and SUBTOTAL Mathematical Functions
+ - Addition of the LOWER, PROPER and UPPER Text Functions
+ - Addition of the BETADIST and BETAINV Statistical Functions
+ - Addition of the CHIINV and GAMMAINV Statistical Functions
+ - Addition of the SERIESSUM Mathematical Function
+ - Addition of the CHAR, CODE, FIND, LEN, REPT, SEARCH, T, TRIM Text Functions
+ - Addition of the FALSE and TRUE Boolean Functions
+ - Addition of the TDIST and TINV Statistical Functions
+ - Addition of the EDATE, EOMONTH, YEAR, MONTH, TIME, TIMEVALUE, HOUR, MINUTE, SECOND, WEEKDAY, WEEKNUM, NOW, TODAY and Date/Time Function
+ - Addition of the BESSELI, BESSELJ, BESSELK and BESSELY Engineering Functions
+ - Addition of the SLN and SYD Financial Functions
+ - reworked MODE calculation to handle floating point numbers
+ - Improved error trapping for invalid input values
+ - Fix to SMALL, LARGE, PERCENTILE and TRIMMEAN to eliminate non-numeric values
+ - Added CDF to BINOMDIST and POISSON
+ - Fix to a potential endless loop in CRITBINOM, together with other bugfixes to the algorithm
+ - Fix to SQRTPI so that it will work with a real value parameter rather than just integers
+ - Trap for passing negative values to FACT
+ - Improved accuracy of the NORMDIST cumulative function, and of the ERF and ERFC functions
+ - Replicated Excel data-type and error handling for BIN, DEC, OCT and HEX conversion functions
+ - Replicated Excel data-type and error handling for AND and OR Boolean functions
+ - Bugfix to MROUND
+ - Rework of the DATE, DATEVALUE, DAY, DAYS360 and DATEDIF date/Time functions to use Excel dates rather than straight PHP dates
+ - Rework of the AND, OR Boolean functions to ignore string values
+ - Rework of the BIN2DEC, BIN2HEX, BIN2OCT, DEC2BIN, DEC2HEX, DEC2OCT Engineering functions to handle two's complement
+ - Excel, Gnumeric and OpenOffice Calc compatibility flag for functions
+ - Note, not all functions have yet been written to work with the Gnumeric and OpenOffice Calc compatibility flags
+ - 1900 or 1904 Calendar flag for date functions
+ - Reworked ExcelToPHP date method to handle the Excel 1900 leap year
+ - Note that this will not correctly return values prior to 13-Dec-1901 20:45:52 as this is the minimum value that PHP date serial values can handle. If you need to work with dates prior to this, then an ExcelToPHPObject method has been added which will work correctly with values between Excel's 1900 calendar base date of 1-Jan-1900, and 13-Dec-1901
+ - Addition of ExcelToPHPObject date method to return a PHP DateTime object from an Excel date serial value
+ - PHPToExcel method modified to accept either PHP date serial numbers or PHP DateTime objects
+ - Addition of FormattedPHPToExcel which will accept a date and time broken to into year, month, day, hour, minute, second and return an Excel date serial value- Control characters in Excel 2007 - @MarkBaker [CodePlex #4485](https://phpexcel.codeplex.com/workitem/4485)
+- BaseDrawing::setWidthAndHeight method request - @MarkBaker [CodePlex #4796](https://phpexcel.codeplex.com/workitem/4796)
+- Page Setup -> Print Titles -> Sheet -> 'Rows to repeat at top' - @MarkBaker [CodePlex #4798](https://phpexcel.codeplex.com/workitem/4798)
+
+### Bugfixes
+
+- Comment functionality - @MarkBaker [CodePlex #4433](https://phpexcel.codeplex.com/workitem/4433)
+- Undefined variable in PHPExcel_Writer_Serialized - @MarkBaker [CodePlex #4124](https://phpexcel.codeplex.com/workitem/4124)
+- Notice: Object of class PHPExcel_RichText could not be converted to int - @MarkBaker [CodePlex #4125](https://phpexcel.codeplex.com/workitem/4125)
+- Excel5Writer: utf8 string not converted to utf16 - @MarkBaker [CodePlex #4126](https://phpexcel.codeplex.com/workitem/4126)
+- PHPExcel_RichText and autosize - @MarkBaker [CodePlex #4180](https://phpexcel.codeplex.com/workitem/4180)
+- Excel5Writer produces broken xls files after change mentioned in work item 4126 - @MarkBaker [CodePlex #4574](https://phpexcel.codeplex.com/workitem/4574)
+- Small bug in PHPExcel_Reader_Excel2007 function _readStyle - @MarkBaker [CodePlex #4797](https://phpexcel.codeplex.com/workitem/4797)
+
+## [1.5.0] - 2007-10-23
+
+### Features
+
+- Refactor PHPExcel Drawing - @MarkBaker [CodePlex #3265](https://phpexcel.codeplex.com/workitem/3265)
+- Update Shared/OLE.php to latest version from PEAR - @CS [CodePlex #3079](https://phpexcel.codeplex.com/workitem/3079)
+- Excel2007 vs Excel2003 compatibility pack - @MarkBaker [CodePlex #3217](https://phpexcel.codeplex.com/workitem/3217)
+- Cell protection (lock/unlock) - @MarkBaker [CodePlex #3234](https://phpexcel.codeplex.com/workitem/3234)
+- Create clickable links (hyperlinks) - @MarkBaker [CodePlex #3543](https://phpexcel.codeplex.com/workitem/3543)
+- Additional page setup parameters - @MarkBaker [CodePlex #3241](https://phpexcel.codeplex.com/workitem/3241)
+- Make temporary file path configurable (Excel5) - @MarkBaker [CodePlex #3300](https://phpexcel.codeplex.com/workitem/3300)
+- Small addition to applyFromArray for font - @MarkBaker [CodePlex #3306](https://phpexcel.codeplex.com/workitem/3306)
+
+### Bugfixes
+
+- Better feedback when save of file is not possible - @MarkBaker [CodePlex #3373](https://phpexcel.codeplex.com/workitem/3373)
+- Text Rotation - @MarkBaker [CodePlex #3181](https://phpexcel.codeplex.com/workitem/3181)
+- Small bug in Page Orientation - @MarkBaker [CodePlex #3237](https://phpexcel.codeplex.com/workitem/3237)
+- insertNewColumnBeforeByColumn undefined - @MarkBaker [CodePlex #3812](https://phpexcel.codeplex.com/workitem/3812)
+- Sheet references not working in formula (Excel5 Writer) - @MarkBaker [CodePlex #3893](https://phpexcel.codeplex.com/workitem/3893)
+
+## [1.4.5] - 2007-08-23
+
+### General
+
+- Class file endings - @MarkBaker [CodePlex #3003](https://phpexcel.codeplex.com/workitem/3003)
+- Different calculation engine improvements - @MarkBaker [CodePlex #3081](https://phpexcel.codeplex.com/workitem/3081)
+- Different improvements in PHPExcel_Reader_Excel2007 - @MarkBaker [CodePlex #3082](https://phpexcel.codeplex.com/workitem/3082)
+
+### Features
+
+- Set XML indentation in PHPExcel_Writer_Excel2007 - @MarkBaker [CodePlex #3146](https://phpexcel.codeplex.com/workitem/3146)
+- Optionally store temporary Excel2007 writer data in file instead of memory - @MarkBaker [CodePlex #3159](https://phpexcel.codeplex.com/workitem/3159)
+- Implement show/hide gridlines - @MarkBaker [CodePlex #3063](https://phpexcel.codeplex.com/workitem/3063)
+- Implement option to read only data - @MarkBaker [CodePlex #3064](https://phpexcel.codeplex.com/workitem/3064)
+- Optionally disable formula precalculation - @MarkBaker [CodePlex #3080](https://phpexcel.codeplex.com/workitem/3080)
+- Explicitly set cell datatype - @MarkBaker [CodePlex #3154](https://phpexcel.codeplex.com/workitem/3154)
+
+### Bugfixes
+
+- Implement more Excel calculation functions - @MarkBaker [CodePlex #2346](https://phpexcel.codeplex.com/workitem/2346)
+ - Addition of MINA, MAXA, COUNTA, AVERAGEA, MEDIAN, MODE, DEVSQ, STDEV, STDEVA, STDEVP, STDEVPA, VAR, VARA, VARP and VARPA Excel Functions
+ - Fix to SUM, PRODUCT, QUOTIENT, MIN, MAX, COUNT and AVERAGE functions when cell contains a numeric value in a string datatype, bringing it in line with MS Excel behaviour- File_exists on ZIP fails on some installations - @MarkBaker [CodePlex #2881](https://phpexcel.codeplex.com/workitem/2881)
+- Argument in textRotation should be -90..90 - @MarkBaker [CodePlex #2879](https://phpexcel.codeplex.com/workitem/2879)
+- Excel2007 reader/writer not implementing OpenXML/SpreadsheetML styles 100% correct - @MarkBaker [CodePlex #2883](https://phpexcel.codeplex.com/workitem/2883)
+- Active sheet index not read/saved - @MarkBaker [CodePlex #2513](https://phpexcel.codeplex.com/workitem/2513)
+- Print and print preview of generated XLSX causes Excel2007 to crash - @MarkBaker [CodePlex #2935](https://phpexcel.codeplex.com/workitem/2935)
+- Error in Calculations - COUNT() function - @MarkBaker [CodePlex #2952](https://phpexcel.codeplex.com/workitem/2952)
+- HTML and CSV writer not writing last row - @MarkBaker [CodePlex #3002](https://phpexcel.codeplex.com/workitem/3002)
+- Memory leak in Excel5 writer - @MarkBaker [CodePlex #3017](https://phpexcel.codeplex.com/workitem/3017)
+- Printing (PHPExcel_Writer_Excel5) - @MarkBaker [CodePlex #3044](https://phpexcel.codeplex.com/workitem/3044)
+- Problems reading zip:// - @MarkBaker [CodePlex #3046](https://phpexcel.codeplex.com/workitem/3046)
+- Error reading conditional formatting - @MarkBaker [CodePlex #3047](https://phpexcel.codeplex.com/workitem/3047)
+- Bug in Excel5 writer (storePanes) - @MarkBaker [CodePlex #3067](https://phpexcel.codeplex.com/workitem/3067)
+- Memory leak in PHPExcel_Style_Color - @MarkBaker [CodePlex #3077](https://phpexcel.codeplex.com/workitem/3077)
+
+## [1.4.0] - 2007-07-23
+
+### General
+
+- Coding convention / code cleanup - @MarkBaker [CodePlex #2687](https://phpexcel.codeplex.com/workitem/2687)
+- Use set_include_path in tests - @MarkBaker [CodePlex #2717](https://phpexcel.codeplex.com/workitem/2717)
+
+### Features
+
+- Move PHPExcel_Writer_Excel5 OLE to PHPExcel_Shared_OLE - @MarkBaker [CodePlex #2812](https://phpexcel.codeplex.com/workitem/2812)
+- Hide/Unhide Column or Row - @MarkBaker [CodePlex #2679](https://phpexcel.codeplex.com/workitem/2679)
+- Implement multi-cell styling - @MarkBaker [CodePlex #2271](https://phpexcel.codeplex.com/workitem/2271)
+- Implement CSV file format (reader/writer) - @MarkBaker [CodePlex #2720](https://phpexcel.codeplex.com/workitem/2720)
+
+### Bugfixes
+
+- Implement HTML file format - @MarkBaker [CodePlex #2845](https://phpexcel.codeplex.com/workitem/2845)
+- Active sheet index not read/saved - @MarkBaker [CodePlex #2513](https://phpexcel.codeplex.com/workitem/2513)
+- Freeze Panes with PHPExcel_Writer_Excel5 - @MarkBaker [CodePlex #2678](https://phpexcel.codeplex.com/workitem/2678)
+- OLE.php - @MarkBaker [CodePlex #2680](https://phpexcel.codeplex.com/workitem/2680)
+- Copy and pasting multiple drop-down list cells breaks reader - @MarkBaker [CodePlex #2736](https://phpexcel.codeplex.com/workitem/2736)
+- Function setAutoFilterByColumnAndRow takes wrong arguments - @MarkBaker [CodePlex #2775](https://phpexcel.codeplex.com/workitem/2775)
+- Simplexml_load_file fails on ZipArchive - @MarkBaker [CodePlex #2858](https://phpexcel.codeplex.com/workitem/2858)
+
+## [1.3.5] - 2007-06-27
+
+### Features
+
+- Documentation - @MarkBaker [CodePlex #15](https://phpexcel.codeplex.com/workitem/15)
+- PHPExcel_Writer_Excel5 - @JV
+- PHPExcel_Reader_Excel2007: Image shadows - @JV
+- Data validation - @MarkBaker [CodePlex #2385](https://phpexcel.codeplex.com/workitem/2385)
+
+### Bugfixes
+
+- Implement richtext strings - @MarkBaker
+- Empty relations when adding image to any sheet but the first one - @MarkBaker [CodePlex #2443](https://phpexcel.codeplex.com/workitem/2443)
+- Excel2007 crashes on print preview - @MarkBaker [CodePlex #2536](https://phpexcel.codeplex.com/workitem/2536)
+
+## [1.3.0] - 2007-06-05
+
+### General
+
+- Create PEAR package - @MarkBaker [CodePlex #1942](https://phpexcel.codeplex.com/workitem/1942)
+
+### Features
+
+- Replace *->duplicate() by __clone() - @MarkBaker [CodePlex #2331](https://phpexcel.codeplex.com/workitem/2331)
+- PHPExcel_Reader_Excel2007: Column auto-size, Protection, Merged cells, Wrap text, Page breaks, Auto filter, Images - @JV
+- Implement "freezing" panes - @MarkBaker [CodePlex #245](https://phpexcel.codeplex.com/workitem/245)
+- Cell addressing alternative - @MarkBaker [CodePlex #2273](https://phpexcel.codeplex.com/workitem/2273)
+- Implement cell word-wrap attribute - @MarkBaker [CodePlex #2270](https://phpexcel.codeplex.com/workitem/2270)
+- Auto-size column - @MarkBaker [CodePlex #2282](https://phpexcel.codeplex.com/workitem/2282)
+- Implement formula calculation - @MarkBaker [CodePlex #241](https://phpexcel.codeplex.com/workitem/241)
+
+### Bugfixes
+
+- Insert/remove row/column - @MarkBaker [CodePlex #2375](https://phpexcel.codeplex.com/workitem/2375)
+- PHPExcel_Worksheet::getCell() should not accept absolute coordinates - @MarkBaker [CodePlex #1931](https://phpexcel.codeplex.com/workitem/1931)
+- Cell reference without row number - @MarkBaker [CodePlex #2272](https://phpexcel.codeplex.com/workitem/2272)
+- Styles with same coordinate but different worksheet - @MarkBaker [CodePlex #2276](https://phpexcel.codeplex.com/workitem/2276)
+- PHPExcel_Worksheet->getCellCollection() usort error - @MarkBaker [CodePlex #2290](https://phpexcel.codeplex.com/workitem/2290)
+- Bug in PHPExcel_Cell::stringFromColumnIndex - @SS [CodePlex #2353](https://phpexcel.codeplex.com/workitem/2353)
+- Reader: numFmts can be missing, use cellStyleXfs instead of cellXfs in styles - @JV [CodePlex #2353](https://phpexcel.codeplex.com/workitem/2353)
+
+## [1.2.0] - 2007-04-26
+
+### General
+
+- Stringtable attribute "count" not necessary, provides wrong info to Excel sometimes... - @MarkBaker
+- Updated tests to address more document properties - @MarkBaker
+- Some refactoring in PHPExcel_Writer_Excel2007_Workbook - @MarkBaker
+- New package: PHPExcel_Shared - @MarkBaker
+- Password hashing algorithm implemented in PHPExcel_Shared_PasswordHasher - @MarkBaker
+- Moved pixel conversion functions to PHPExcel_Shared_Drawing - @MarkBaker
+- Switch over to LGPL license - @MarkBaker [CodePlex #244](https://phpexcel.codeplex.com/workitem/244)
+
+### Features
+
+- Include PHPExcel version in file headers - @MarkBaker [CodePlex #5](https://phpexcel.codeplex.com/workitem/5)
+- Autofilter - @MarkBaker [CodePlex #6](https://phpexcel.codeplex.com/workitem/6)
+- Extra document property: keywords - @MarkBaker [CodePlex #7](https://phpexcel.codeplex.com/workitem/7)
+- Extra document property: category - @MarkBaker [CodePlex #8](https://phpexcel.codeplex.com/workitem/8)
+- Document security - @MarkBaker [CodePlex #9](https://phpexcel.codeplex.com/workitem/9)
+- PHPExcel_Writer_Serialized and PHPExcel_Reader_Serialized - @MarkBaker [CodePlex #10](https://phpexcel.codeplex.com/workitem/10)
+- Alternative syntax: Addressing a cell - @MarkBaker [CodePlex #11](https://phpexcel.codeplex.com/workitem/11)
+- Merge cells - @MarkBaker [CodePlex #12](https://phpexcel.codeplex.com/workitem/12)
+
+### Bugfixes
+
+- Protect ranges of cells with a password - @MarkBaker [CodePlex #13](https://phpexcel.codeplex.com/workitem/13)
+- (style/fill/patternFill/fgColor or bgColor can be empty) - @JV [CodePlex #14](https://phpexcel.codeplex.com/workitem/14)
+
+## [1.1.1] - 2007-03-26
+
+### General
+
+- Syntax error in "Classes/PHPExcel/Writer/Excel2007.php" on line 243 - @MarkBaker [CodePlex #1250](https://phpexcel.codeplex.com/workitem/1250)
+- Reader should check if file exists and throws an exception when it doesn't - @MarkBaker [CodePlex #1282](https://phpexcel.codeplex.com/workitem/1282)
+
+## [1.1.0] - 2007-03-22
+
+### Bugfixes
+
+- Style information lost after passing trough Excel2007_Reader - @MarkBaker [CodePlex #836](https://phpexcel.codeplex.com/workitem/836)
+
+### General
+
+- Number of columns > AZ fails fixed in PHPExcel_Cell::columnIndexFromString - @MarkBaker [CodePlex #913](https://phpexcel.codeplex.com/workitem/913)
+
+### Features
+
+- Added a brief file with installation instructions - @MarkBaker
+- Page breaks (horizontal and vertical) - @MarkBaker
+- Image shadows - @MarkBaker
+
+## [1.0.0] - 2007-02-22
+
+### Bugfixes
+
+- PHPExcel->removeSheetByIndex now re-orders sheets after deletion, so no array indexes are lost - @JV
+- PHPExcel_Writer_Excel2007_Worksheet::_writeCols() used direct assignment to $pSheet->getColumnDimension('A')->Width instead of $pSheet->getColumnDimension('A')->setWidth() - @JV
+- DocumentProperties used $this->LastModifiedBy instead of $this->_lastModifiedBy. - @JV
+
+### General
+
+- Only first = should be removed when writing formula in PHPExcel_Writer_Excel2007_Worksheet. - @JV
+- Consistency of method names to camelCase - @JV
+- Updated tests to match consistency changes - @JV
+- Detection of mime-types now with image_type_to_mime_type() - @JV
+- Constants now hold string value used in Excel 2007 - @JV
+
+### Features
+
+- Fixed folder name case (WorkSheet -> Worksheet) - @MarkBaker
+- PHPExcel classes (not the Writer classes) can be duplicated, using a duplicate() method. - @MarkBaker
+- Cell styles can now be duplicated to a range of cells using PHPExcel_Worksheet->duplicateStyle() - @MarkBaker
+- Conditional formatting - @MarkBaker
+- Reader for Excel 2007 (not supporting full specification yet!) - @JV
+
+## [1.0.0 RC] - 2007-01-31
+
+- Project name has been changed to PHPExcel
+- Project homepage is now http://www.codeplex.com/PHPExcel
+- Started versioning at number: PHPExcel 1.0.0 RC
+
+## 2007-01-22
+
+- Fixed some performance issues on large-scale worksheets (mainly loops vs. indexed arrays)
+- Performance on creating StringTable has been increased
+- Performance on writing Excel2007 worksheet has been increased
+
+## 2007-01-18
+
+- Images can now be rotated
+- Fixed bug: When drawings have full path specified, no mime type can be deducted
+- Fixed bug: Only one drawing can be added to a worksheet
+
+## 2007-01-12
+
+- Refactoring of some classes to use ArrayObject instead of array()
+- Cell style now has support for number format (i.e. #,##0)
+- Implemented embedding images
+
+## 2007-01-02
+
+- Cell style now has support for fills, including gradient fills
+- Cell style now has support for fonts
+- Cell style now has support for border colors
+- Cell style now has support for font colors
+- Cell style now has support for alignment
+
+## 2006-12-21
+
+- Support for cell style borders
+- Support for cell styles
+- Refactoring of Excel2007 Writer into multiple classes in package SpreadSheet_Writer_Excel2007
+- Refactoring of all classes, changed public members to public properties using getter/setter
+- Worksheet names are now unique. On duplicate worksheet names, a number is appended.
+- Worksheet now has parent SpreadSheet object
+- Worksheet now has support for page header and footer
+- Worksheet now has support for page margins
+- Worksheet now has support for page setup (only Paper size and Orientation)
+- Worksheet properties now accessible by using getProperties()
+- Worksheet now has support for row and column dimensions (height / width)
+- Exceptions thrown have a more clear description
+
+## Initial version
+
+- Create a Spreadsheet object
+- Add one or more Worksheet objects
+- Add cells to Worksheet objects
+- Export Spreadsheet object to Excel 2007 OpenXML format
+- Each cell supports the following data formats: string, number, formula, boolean.
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/CHANGELOG.md b/htdocs/includes/phpoffice/phpspreadsheet/CHANGELOG.md
new file mode 100644
index 00000000000..897a8e51338
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/CHANGELOG.md
@@ -0,0 +1,471 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com)
+and this project adheres to [Semantic Versioning](https://semver.org).
+
+## [1.12.0] - 2020-04-27
+
+### Added
+
+- Improved the ARABIC function to also handle short-hand roman numerals
+- Added support for the FLOOR.MATH and FLOOR.PRECISE functions [#1351](https://github.com/PHPOffice/PhpSpreadsheet/pull/1351)
+
+### Fixed
+
+- Fix ROUNDUP and ROUNDDOWN for floating-point rounding error [#1404](https://github.com/PHPOffice/PhpSpreadsheet/pull/1404)
+- Fix ROUNDUP and ROUNDDOWN for negative number [#1417](https://github.com/PHPOffice/PhpSpreadsheet/pull/1417)
+- Fix loading styles from vmlDrawings when containing whitespace [#1347](https://github.com/PHPOffice/PhpSpreadsheet/issues/1347)
+- Fix incorrect behavior when removing last row [#1365](https://github.com/PHPOffice/PhpSpreadsheet/pull/1365)
+- MATCH with a static array should return the position of the found value based on the values submitted [#1332](https://github.com/PHPOffice/PhpSpreadsheet/pull/1332)
+- Fix Xlsx Reader's handling of undefined fill color [#1353](https://github.com/PHPOffice/PhpSpreadsheet/pull/1353)
+
+## [1.11.0] - 2020-03-02
+
+### Added
+
+- Added support for the BASE function
+- Added support for the ARABIC function
+- Conditionals - Extend Support for (NOT)CONTAINSBLANKS [#1278](https://github.com/PHPOffice/PhpSpreadsheet/pull/1278)
+
+### Fixed
+
+- Handle Error in Formula Processing Better for Xls [#1267](https://github.com/PHPOffice/PhpSpreadsheet/pull/1267)
+- Handle ConditionalStyle NumberFormat When Reading Xlsx File [#1296](https://github.com/PHPOffice/PhpSpreadsheet/pull/1296)
+- Fix Xlsx Writer's handling of decimal commas [#1282](https://github.com/PHPOffice/PhpSpreadsheet/pull/1282)
+- Fix for issue by removing test code mistakenly left in [#1328](https://github.com/PHPOffice/PhpSpreadsheet/pull/1328)
+- Fix for Xls writer wrong selected cells and active sheet [#1256](https://github.com/PHPOffice/PhpSpreadsheet/pull/1256)
+- Fix active cell when freeze pane is used [#1323](https://github.com/PHPOffice/PhpSpreadsheet/pull/1323)
+- Fix XLSX file loading with autofilter containing '$' [#1326](https://github.com/PHPOffice/PhpSpreadsheet/pull/1326)
+- PHPDoc - Use `@return $this` for fluent methods [#1362](https://github.com/PHPOffice/PhpSpreadsheet/pull/1362)
+
+## [1.10.1] - 2019-12-02
+
+### Changed
+
+- PHP 7.4 compatibility
+
+### Fixed
+
+- FLOOR() function accept negative number and negative significance [#1245](https://github.com/PHPOffice/PhpSpreadsheet/pull/1245)
+- Correct column style even when using rowspan [#1249](https://github.com/PHPOffice/PhpSpreadsheet/pull/1249)
+- Do not confuse defined names and cell refs [#1263](https://github.com/PHPOffice/PhpSpreadsheet/pull/1263)
+- XLSX reader/writer keep decimal for floats with a zero decimal part [#1262](https://github.com/PHPOffice/PhpSpreadsheet/pull/1262)
+- ODS writer prevent invalid numeric value if locale decimal separator is comma [#1268](https://github.com/PHPOffice/PhpSpreadsheet/pull/1268)
+- Xlsx writer actually writes plotVisOnly and dispBlanksAs from chart properties [#1266](https://github.com/PHPOffice/PhpSpreadsheet/pull/1266)
+
+## [1.10.0] - 2019-11-18
+
+### Changed
+
+- Change license from LGPL 2.1 to MIT [#140](https://github.com/PHPOffice/PhpSpreadsheet/issues/140)
+
+### Added
+
+- Implementation of IFNA() logical function
+- Support "showZeros" worksheet option to change how Excel shows and handles "null" values returned from a calculation
+- Allow HTML Reader to accept HTML as a string into an existing spreadsheet [#1212](https://github.com/PHPOffice/PhpSpreadsheet/pull/1212)
+
+### Fixed
+
+- IF implementation properly handles the value `#N/A` [#1165](https://github.com/PHPOffice/PhpSpreadsheet/pull/1165)
+- Formula Parser: Wrong line count for stuff like "MyOtherSheet!A:D" [#1215](https://github.com/PHPOffice/PhpSpreadsheet/issues/1215)
+- Call garbage collector after removing a column to prevent stale cached values
+- Trying to remove a column that doesn't exist deletes the latest column
+- Keep big integer as integer instead of lossely casting to float [#874](https://github.com/PHPOffice/PhpSpreadsheet/pull/874)
+- Fix branch pruning handling of non boolean conditions [#1167](https://github.com/PHPOffice/PhpSpreadsheet/pull/1167)
+- Fix ODS Reader when no DC namespace are defined [#1182](https://github.com/PHPOffice/PhpSpreadsheet/pull/1182)
+- Fixed Functions->ifCondition for allowing <> and empty condition [#1206](https://github.com/PHPOffice/PhpSpreadsheet/pull/1206)
+- Validate XIRR inputs and return correct error values [#1120](https://github.com/PHPOffice/PhpSpreadsheet/issues/1120)
+- Allow to read xlsx files with exotic workbook names like "workbook2.xml" [#1183](https://github.com/PHPOffice/PhpSpreadsheet/pull/1183)
+
+## [1.9.0] - 2019-08-17
+
+### Changed
+
+- Drop support for PHP 5.6 and 7.0, according to https://phpspreadsheet.readthedocs.io/en/latest/#php-version-support
+
+### Added
+
+- When <br> appears in a table cell, set the cell to wrap [#1071](https://github.com/PHPOffice/PhpSpreadsheet/issues/1071) and [#1070](https://github.com/PHPOffice/PhpSpreadsheet/pull/1070)
+- Add MAXIFS, MINIFS, COUNTIFS and Remove MINIF, MAXIF [#1056](https://github.com/PHPOffice/PhpSpreadsheet/issues/1056)
+- HLookup needs an ordered list even if range_lookup is set to false [#1055](https://github.com/PHPOffice/PhpSpreadsheet/issues/1055) and [#1076](https://github.com/PHPOffice/PhpSpreadsheet/pull/1076)
+- Improve performance of IF function calls via ranch pruning to avoid resolution of every branches [#844](https://github.com/PHPOffice/PhpSpreadsheet/pull/844)
+- MATCH function supports `*?~` Excel functionality, when match_type=0 [#1116](https://github.com/PHPOffice/PhpSpreadsheet/issues/1116)
+- Allow HTML Reader to accept HTML as a string [#1136](https://github.com/PHPOffice/PhpSpreadsheet/pull/1136)
+
+### Fixed
+
+- Fix to AVERAGEIF() function when called with a third argument
+- Eliminate duplicate fill none style entries [#1066](https://github.com/PHPOffice/PhpSpreadsheet/issues/1066)
+- Fix number format masks containing literal (non-decimal point) dots [#1079](https://github.com/PHPOffice/PhpSpreadsheet/issues/1079)
+- Fix number format masks containing named colours that were being misinterpreted as date formats; and add support for masks that fully replace the value with a full text string [#1009](https://github.com/PHPOffice/PhpSpreadsheet/issues/1009)
+- Stricter-typed comparison testing in COUNTIF() and COUNTIFS() evaluation [#1046](https://github.com/PHPOffice/PhpSpreadsheet/issues/1046)
+- COUPNUM should not return zero when settlement is in the last period [#1020](https://github.com/PHPOffice/PhpSpreadsheet/issues/1020) and [#1021](https://github.com/PHPOffice/PhpSpreadsheet/pull/1021)
+- Fix handling of named ranges referencing sheets with spaces or "!" in their title
+- Cover `getSheetByName()` with tests for name with quote and spaces [#739](https://github.com/PHPOffice/PhpSpreadsheet/issues/739)
+- Best effort to support invalid colspan values in HTML reader - [#878](https://github.com/PHPOffice/PhpSpreadsheet/pull/878)
+- Fixes incorrect rows deletion [#868](https://github.com/PHPOffice/PhpSpreadsheet/issues/868)
+- MATCH function fix (value search by type, stop search when match_type=-1 and unordered element encountered) [#1116](https://github.com/PHPOffice/PhpSpreadsheet/issues/1116)
+- Fix `getCalculatedValue()` error with more than two INDIRECT [#1115](https://github.com/PHPOffice/PhpSpreadsheet/pull/1115)
+- Writer\Html did not hide columns [#985](https://github.com/PHPOffice/PhpSpreadsheet/pull/985)
+
+## [1.8.2] - 2019-07-08
+
+### Fixed
+
+- Uncaught error when opening ods file and properties aren't defined [#1047](https://github.com/PHPOffice/PhpSpreadsheet/issues/1047)
+- Xlsx Reader Cell datavalidations bug [#1052](https://github.com/PHPOffice/PhpSpreadsheet/pull/1052)
+
+## [1.8.1] - 2019-07-02
+
+### Fixed
+
+- Allow nullable theme for Xlsx Style Reader class [#1043](https://github.com/PHPOffice/PhpSpreadsheet/issues/1043)
+
+## [1.8.0] - 2019-07-01
+
+### Security Fix (CVE-2019-12331)
+
+- Detect double-encoded xml in the Security scanner, and reject as suspicious.
+- This change also broadens the scope of the `libxml_disable_entity_loader` setting when reading XML-based formats, so that it is enabled while the xml is being parsed and not simply while it is loaded.
+ On some versions of PHP, this can cause problems because it is not thread-safe, and can affect other PHP scripts running on the same server. This flag is set to true when instantiating a loader, and back to its original setting when the Reader is no longer in scope, or manually unset.
+- Provide a check to identify whether libxml_disable_entity_loader is thread-safe or not.
+
+ `XmlScanner::threadSafeLibxmlDisableEntityLoaderAvailability()`
+- Provide an option to disable the libxml_disable_entity_loader call through settings. This is not recommended as it reduces the security of the XML-based readers, and should only be used if you understand the consequences and have no other choice.
+
+### Added
+
+- Added support for the SWITCH function [#963](https://github.com/PHPOffice/PhpSpreadsheet/issues/963) and [#983](https://github.com/PHPOffice/PhpSpreadsheet/pull/983)
+- Add accounting number format style [#974](https://github.com/PHPOffice/PhpSpreadsheet/pull/974)
+
+### Fixed
+
+- Whitelist `tsv` extension when opening CSV files [#429](https://github.com/PHPOffice/PhpSpreadsheet/issues/429)
+- Fix a SUMIF warning with some versions of PHP when having different length of arrays provided as input [#873](https://github.com/PHPOffice/PhpSpreadsheet/pull/873)
+- Fix incorrectly handled backslash-escaped space characters in number format
+
+## [1.7.0] - 2019-05-26
+
+- Added support for inline styles in Html reader (borders, alignment, width, height)
+- QuotedText cells no longer treated as formulae if the content begins with a `=`
+- Clean handling for DDE in formulae
+
+### Fixed
+
+- Fix handling for escaped enclosures and new lines in CSV Separator Inference
+- Fix MATCH an error was appearing when comparing strings against 0 (always true)
+- Fix wrong calculation of highest column with specified row [#700](https://github.com/PHPOffice/PhpSpreadsheet/issues/700)
+- Fix VLOOKUP
+- Fix return type hint
+
+## [1.6.0] - 2019-01-02
+
+### Added
+
+- Refactored Matrix Functions to use external Matrix library
+- Possibility to specify custom colors of values for pie and donut charts [#768](https://github.com/PHPOffice/PhpSpreadsheet/pull/768)
+
+### Fixed
+
+- Improve XLSX parsing speed if no readFilter is applied [#772](https://github.com/PHPOffice/PhpSpreadsheet/issues/772)
+- Fix column names if read filter calls in XLSX reader skip columns [#777](https://github.com/PHPOffice/PhpSpreadsheet/pull/777)
+- XLSX reader can now ignore blank cells, using the setReadEmptyCells(false) method. [#810](https://github.com/PHPOffice/PhpSpreadsheet/issues/810)
+- Fix LOOKUP function which was breaking on edge cases [#796](https://github.com/PHPOffice/PhpSpreadsheet/issues/796)
+- Fix VLOOKUP with exact matches [#809](https://github.com/PHPOffice/PhpSpreadsheet/pull/809)
+- Support COUNTIFS multiple arguments [#830](https://github.com/PHPOffice/PhpSpreadsheet/pull/830)
+- Change `libxml_disable_entity_loader()` as shortly as possible [#819](https://github.com/PHPOffice/PhpSpreadsheet/pull/819)
+- Improved memory usage and performance when loading large spreadsheets [#822](https://github.com/PHPOffice/PhpSpreadsheet/pull/822)
+- Improved performance when loading large spreadsheets [#825](https://github.com/PHPOffice/PhpSpreadsheet/pull/825)
+- Improved performance when loading large spreadsheets [#824](https://github.com/PHPOffice/PhpSpreadsheet/pull/824)
+- Fix color from CSS when reading from HTML [#831](https://github.com/PHPOffice/PhpSpreadsheet/pull/831)
+- Fix infinite loop when reading invalid ODS files [#832](https://github.com/PHPOffice/PhpSpreadsheet/pull/832)
+- Fix time format for duration is incorrect [#666](https://github.com/PHPOffice/PhpSpreadsheet/pull/666)
+- Fix iconv unsupported `//IGNORE//TRANSLIT` on IBM i [#791](https://github.com/PHPOffice/PhpSpreadsheet/issues/791)
+
+### Changed
+
+- `master` is the new default branch, `develop` does not exist anymore
+
+## [1.5.2] - 2018-11-25
+
+### Security
+
+- Improvements to the design of the XML Security Scanner [#771](https://github.com/PHPOffice/PhpSpreadsheet/issues/771)
+
+## [1.5.1] - 2018-11-20
+
+### Security
+
+- Fix and improve XXE security scanning for XML-based and HTML Readers [#771](https://github.com/PHPOffice/PhpSpreadsheet/issues/771)
+
+### Added
+
+- Support page margin in mPDF [#750](https://github.com/PHPOffice/PhpSpreadsheet/issues/750)
+
+### Fixed
+
+- Support numeric condition in SUMIF, SUMIFS, AVERAGEIF, COUNTIF, MAXIF and MINIF [#683](https://github.com/PHPOffice/PhpSpreadsheet/issues/683)
+- SUMIFS containing multiple conditions [#704](https://github.com/PHPOffice/PhpSpreadsheet/issues/704)
+- Csv reader avoid notice when the file is empty [#743](https://github.com/PHPOffice/PhpSpreadsheet/pull/743)
+- Fix print area parser for XLSX reader [#734](https://github.com/PHPOffice/PhpSpreadsheet/pull/734)
+- Support overriding `DefaultValueBinder::dataTypeForValue()` without overriding `DefaultValueBinder::bindValue()` [#735](https://github.com/PHPOffice/PhpSpreadsheet/pull/735)
+- Mpdf export can exceed pcre.backtrack_limit [#637](https://github.com/PHPOffice/PhpSpreadsheet/issues/637)
+- Fix index overflow on data values array [#748](https://github.com/PHPOffice/PhpSpreadsheet/pull/748)
+
+## [1.5.0] - 2018-10-21
+
+### Added
+
+- PHP 7.3 support
+- Add the DAYS() function [#594](https://github.com/PHPOffice/PhpSpreadsheet/pull/594)
+
+### Fixed
+
+- Sheet title can contain exclamation mark [#325](https://github.com/PHPOffice/PhpSpreadsheet/issues/325)
+- Xls file cause the exception during open by Xls reader [#402](https://github.com/PHPOffice/PhpSpreadsheet/issues/402)
+- Skip non numeric value in SUMIF [#618](https://github.com/PHPOffice/PhpSpreadsheet/pull/618)
+- OFFSET should allow omitted height and width [#561](https://github.com/PHPOffice/PhpSpreadsheet/issues/561)
+- Correctly determine delimiter when CSV contains line breaks inside enclosures [#716](https://github.com/PHPOffice/PhpSpreadsheet/issues/716)
+
+## [1.4.1] - 2018-09-30
+
+### Fixed
+
+- Remove locale from formatting string [#644](https://github.com/PHPOffice/PhpSpreadsheet/pull/644)
+- Allow iterators to go out of bounds with prev [#587](https://github.com/PHPOffice/PhpSpreadsheet/issues/587)
+- Fix warning when reading xlsx without styles [#631](https://github.com/PHPOffice/PhpSpreadsheet/pull/631)
+- Fix broken sample links on windows due to $baseDir having backslash [#653](https://github.com/PHPOffice/PhpSpreadsheet/pull/653)
+
+## [1.4.0] - 2018-08-06
+
+### Added
+
+- Add excel function EXACT(value1, value2) support [#595](https://github.com/PHPOffice/PhpSpreadsheet/pull/595)
+- Support workbook view attributes for Xlsx format [#523](https://github.com/PHPOffice/PhpSpreadsheet/issues/523)
+- Read and write hyperlink for drawing image [#490](https://github.com/PHPOffice/PhpSpreadsheet/pull/490)
+- Added calculation engine support for the new bitwise functions that were added in MS Excel 2013
+ - BITAND() Returns a Bitwise 'And' of two numbers
+ - BITOR() Returns a Bitwise 'Or' of two number
+ - BITXOR() Returns a Bitwise 'Exclusive Or' of two numbers
+ - BITLSHIFT() Returns a number shifted left by a specified number of bits
+ - BITRSHIFT() Returns a number shifted right by a specified number of bits
+- Added calculation engine support for other new functions that were added in MS Excel 2013 and MS Excel 2016
+ - Text Functions
+ - CONCAT() Synonym for CONCATENATE()
+ - NUMBERVALUE() Converts text to a number, in a locale-independent way
+ - UNICHAR() Synonym for CHAR() in PHPSpreadsheet, which has always used UTF-8 internally
+ - UNIORD() Synonym for ORD() in PHPSpreadsheet, which has always used UTF-8 internally
+ - TEXTJOIN() Joins together two or more text strings, separated by a delimiter
+ - Logical Functions
+ - XOR() Returns a logical Exclusive Or of all arguments
+ - Date/Time Functions
+ - ISOWEEKNUM() Returns the ISO 8601 week number of the year for a given date
+ - Lookup and Reference Functions
+ - FORMULATEXT() Returns a formula as a string
+ - Financial Functions
+ - PDURATION() Calculates the number of periods required for an investment to reach a specified value
+ - RRI() Calculates the interest rate required for an investment to grow to a specified future value
+ - Engineering Functions
+ - ERF.PRECISE() Returns the error function integrated between 0 and a supplied limit
+ - ERFC.PRECISE() Synonym for ERFC
+ - Math and Trig Functions
+ - SEC() Returns the secant of an angle
+ - SECH() Returns the hyperbolic secant of an angle
+ - CSC() Returns the cosecant of an angle
+ - CSCH() Returns the hyperbolic cosecant of an angle
+ - COT() Returns the cotangent of an angle
+ - COTH() Returns the hyperbolic cotangent of an angle
+ - ACOT() Returns the cotangent of an angle
+ - ACOTH() Returns the hyperbolic cotangent of an angle
+- Refactored Complex Engineering Functions to use external complex number library
+- Added calculation engine support for the new complex number functions that were added in MS Excel 2013
+ - IMCOSH() Returns the hyperbolic cosine of a complex number
+ - IMCOT() Returns the cotangent of a complex number
+ - IMCSC() Returns the cosecant of a complex number
+ - IMCSCH() Returns the hyperbolic cosecant of a complex number
+ - IMSEC() Returns the secant of a complex number
+ - IMSECH() Returns the hyperbolic secant of a complex number
+ - IMSINH() Returns the hyperbolic sine of a complex number
+ - IMTAN() Returns the tangent of a complex number
+
+### Fixed
+
+- Fix ISFORMULA() function to work with a cell reference to another worksheet
+- Xlsx reader crashed when reading a file with workbook protection [#553](https://github.com/PHPOffice/PhpSpreadsheet/pull/553)
+- Cell formats with escaped spaces were causing incorrect date formatting [#557](https://github.com/PHPOffice/PhpSpreadsheet/issues/557)
+- Could not open CSV file containing HTML fragment [#564](https://github.com/PHPOffice/PhpSpreadsheet/issues/564)
+- Exclude the vendor folder in migration [#481](https://github.com/PHPOffice/PhpSpreadsheet/issues/481)
+- Chained operations on cell ranges involving borders operated on last cell only [#428](https://github.com/PHPOffice/PhpSpreadsheet/issues/428)
+- Avoid memory exhaustion when cloning worksheet with a drawing [#437](https://github.com/PHPOffice/PhpSpreadsheet/issues/437)
+- Migration tool keep variables containing $PHPExcel untouched [#598](https://github.com/PHPOffice/PhpSpreadsheet/issues/598)
+- Rowspans/colspans were incorrect when adding worksheet using loadIntoExisting [#619](https://github.com/PHPOffice/PhpSpreadsheet/issues/619)
+
+## [1.3.1] - 2018-06-12
+
+### Fixed
+
+- Ranges across Z and AA columns incorrectly threw an exception [#545](https://github.com/PHPOffice/PhpSpreadsheet/issues/545)
+
+## [1.3.0] - 2018-06-10
+
+### Added
+
+- Support to read Xlsm templates with form elements, macros, printer settings, protected elements and back compatibility drawing, and save result without losing important elements of document [#435](https://github.com/PHPOffice/PhpSpreadsheet/issues/435)
+- Expose sheet title maximum length as `Worksheet::SHEET_TITLE_MAXIMUM_LENGTH` [#482](https://github.com/PHPOffice/PhpSpreadsheet/issues/482)
+- Allow escape character to be set in CSV reader [#492](https://github.com/PHPOffice/PhpSpreadsheet/issues/492)
+
+### Fixed
+
+- Subtotal 9 in a group that has other subtotals 9 exclude the totals of the other subtotals in the range [#332](https://github.com/PHPOffice/PhpSpreadsheet/issues/332)
+- `Helper\Html` support UTF-8 HTML input [#444](https://github.com/PHPOffice/PhpSpreadsheet/issues/444)
+- Xlsx loaded an extra empty comment for each real comment [#375](https://github.com/PHPOffice/PhpSpreadsheet/issues/375)
+- Xlsx reader do not read rows and columns filtered out in readFilter at all [#370](https://github.com/PHPOffice/PhpSpreadsheet/issues/370)
+- Make newer Excel versions properly recalculate formulas on document open [#456](https://github.com/PHPOffice/PhpSpreadsheet/issues/456)
+- `Coordinate::extractAllCellReferencesInRange()` throws an exception for an invalid range [#519](https://github.com/PHPOffice/PhpSpreadsheet/issues/519)
+- Fixed parsing of conditionals in COUNTIF functions [#526](https://github.com/PHPOffice/PhpSpreadsheet/issues/526)
+- Corruption errors for saved Xlsx docs with frozen panes [#532](https://github.com/PHPOffice/PhpSpreadsheet/issues/532)
+
+## [1.2.1] - 2018-04-10
+
+### Fixed
+
+- Plain text and richtext mixed in same cell can be read [#442](https://github.com/PHPOffice/PhpSpreadsheet/issues/442)
+
+## [1.2.0] - 2018-03-04
+
+### Added
+
+- HTML writer creates a generator meta tag [#312](https://github.com/PHPOffice/PhpSpreadsheet/issues/312)
+- Support invalid zoom value in XLSX format [#350](https://github.com/PHPOffice/PhpSpreadsheet/pull/350)
+- Support for `_xlfn.` prefixed functions and `ISFORMULA`, `MODE.SNGL`, `STDEV.S`, `STDEV.P` [#390](https://github.com/PHPOffice/PhpSpreadsheet/pull/390)
+
+### Fixed
+
+- Avoid potentially unsupported PSR-16 cache keys [#354](https://github.com/PHPOffice/PhpSpreadsheet/issues/354)
+- Check for MIME type to know if CSV reader can read a file [#167](https://github.com/PHPOffice/PhpSpreadsheet/issues/167)
+- Use proper € symbol for currency format [#379](https://github.com/PHPOffice/PhpSpreadsheet/pull/379)
+- Read printing area correctly when skipping some sheets [#371](https://github.com/PHPOffice/PhpSpreadsheet/issues/371)
+- Avoid incorrectly overwriting calculated value type [#394](https://github.com/PHPOffice/PhpSpreadsheet/issues/394)
+- Select correct cell when calling freezePane [#389](https://github.com/PHPOffice/PhpSpreadsheet/issues/389)
+- `setStrikethrough()` did not set the font [#403](https://github.com/PHPOffice/PhpSpreadsheet/issues/403)
+
+## [1.1.0] - 2018-01-28
+
+### Added
+
+- Support for PHP 7.2
+- Support cell comments in HTML writer and reader [#308](https://github.com/PHPOffice/PhpSpreadsheet/issues/308)
+- Option to stop at a conditional styling, if it matches (only XLSX format) [#292](https://github.com/PHPOffice/PhpSpreadsheet/pull/292)
+- Support for line width for data series when rendering Xlsx [#329](https://github.com/PHPOffice/PhpSpreadsheet/pull/329)
+
+### Fixed
+
+- Better auto-detection of CSV separators [#305](https://github.com/PHPOffice/PhpSpreadsheet/issues/305)
+- Support for shape style ending with `;` [#304](https://github.com/PHPOffice/PhpSpreadsheet/issues/304)
+- Freeze Panes takes wrong coordinates for XLSX [#322](https://github.com/PHPOffice/PhpSpreadsheet/issues/322)
+- `COLUMNS` and `ROWS` functions crashed in some cases [#336](https://github.com/PHPOffice/PhpSpreadsheet/issues/336)
+- Support XML file without styles [#331](https://github.com/PHPOffice/PhpSpreadsheet/pull/331)
+- Cell coordinates which are already a range cause an exception [#319](https://github.com/PHPOffice/PhpSpreadsheet/issues/319)
+
+## [1.0.0] - 2017-12-25
+
+### Added
+
+- Support to write merged cells in ODS format [#287](https://github.com/PHPOffice/PhpSpreadsheet/issues/287)
+- Able to set the `topLeftCell` in freeze panes [#261](https://github.com/PHPOffice/PhpSpreadsheet/pull/261)
+- Support `DateTimeImmutable` as cell value
+- Support migration of prefixed classes
+
+### Fixed
+
+- Can read very small HTML files [#194](https://github.com/PHPOffice/PhpSpreadsheet/issues/194)
+- Written DataValidation was corrupted [#290](https://github.com/PHPOffice/PhpSpreadsheet/issues/290)
+- Date format compatible with both LibreOffice and Excel [#298](https://github.com/PHPOffice/PhpSpreadsheet/issues/298)
+
+### BREAKING CHANGE
+
+- Constant `TYPE_DOUGHTNUTCHART` is now `TYPE_DOUGHNUTCHART`.
+
+## [1.0.0-beta2] - 2017-11-26
+
+### Added
+
+- Support for chart fill color - @CrazyBite [#158](https://github.com/PHPOffice/PhpSpreadsheet/pull/158)
+- Support for read Hyperlink for xml - @GreatHumorist [#223](https://github.com/PHPOffice/PhpSpreadsheet/pull/223)
+- Support for cell value validation according to data validation rules - @SailorMax [#257](https://github.com/PHPOffice/PhpSpreadsheet/pull/257)
+- Support for custom implementation, or configuration, of PDF libraries - @SailorMax [#266](https://github.com/PHPOffice/PhpSpreadsheet/pull/266)
+
+### Changed
+
+- Merge data-validations to reduce written worksheet size - @billblume [#131](https://github.com/PHPOffice/PhpSpreadSheet/issues/131)
+- Throws exception if a XML file is invalid - @GreatHumorist [#222](https://github.com/PHPOffice/PhpSpreadsheet/pull/222)
+- Upgrade to mPDF 7.0+ [#144](https://github.com/PHPOffice/PhpSpreadsheet/issues/144)
+
+### Fixed
+
+- Control characters in cell values are automatically escaped [#212](https://github.com/PHPOffice/PhpSpreadsheet/issues/212)
+- Prevent color changing when copy/pasting xls files written by PhpSpreadsheet to another file - @al-lala [#218](https://github.com/PHPOffice/PhpSpreadsheet/issues/218)
+- Add cell reference automatic when there is no cell reference('r' attribute) in Xlsx file. - @GreatHumorist [#225](https://github.com/PHPOffice/PhpSpreadsheet/pull/225) Refer to [#201](https://github.com/PHPOffice/PhpSpreadsheet/issues/201)
+- `Reader\Xlsx::getFromZipArchive()` function return false if the zip entry could not be located. - @anton-harvey [#268](https://github.com/PHPOffice/PhpSpreadsheet/pull/268)
+
+### BREAKING CHANGE
+
+- Extracted coordinate method to dedicate class [migration guide](./docs/topics/migration-from-PHPExcel.md).
+- Column indexes are based on 1, see the [migration guide](./docs/topics/migration-from-PHPExcel.md).
+- Standardization of array keys used for style, see the [migration guide](./docs/topics/migration-from-PHPExcel.md).
+- Easier usage of PDF writers, and other custom readers and writers, see the [migration guide](./docs/topics/migration-from-PHPExcel.md).
+- Easier usage of chart renderers, see the [migration guide](./docs/topics/migration-from-PHPExcel.md).
+- Rename a few more classes to keep them in their related namespaces:
+ - `CalcEngine` => `Calculation\Engine`
+ - `PhpSpreadsheet\Calculation` => `PhpSpreadsheet\Calculation\Calculation`
+ - `PhpSpreadsheet\Cell` => `PhpSpreadsheet\Cell\Cell`
+ - `PhpSpreadsheet\Chart` => `PhpSpreadsheet\Chart\Chart`
+ - `PhpSpreadsheet\RichText` => `PhpSpreadsheet\RichText\RichText`
+ - `PhpSpreadsheet\Style` => `PhpSpreadsheet\Style\Style`
+ - `PhpSpreadsheet\Worksheet` => `PhpSpreadsheet\Worksheet\Worksheet`
+
+## [1.0.0-beta] - 2017-08-17
+
+### Added
+
+- Initial implementation of SUMIFS() function
+- Additional codepages
+- MemoryDrawing not working in HTML writer [#808](https://github.com/PHPOffice/PHPExcel/issues/808)
+- CSV Reader can auto-detect the separator used in file [#141](https://github.com/PHPOffice/PhpSpreadsheet/pull/141)
+- HTML Reader supports some basic inline styles [#180](https://github.com/PHPOffice/PhpSpreadsheet/pull/180)
+
+### Changed
+
+- Start following [SemVer](https://semver.org) properly.
+
+### Fixed
+
+- Fix to getCell() method when cell reference includes a worksheet reference - @MarkBaker
+- Ignore inlineStr type if formula element exists - @ncrypthic [#570](https://github.com/PHPOffice/PHPExcel/issues/570)
+- Excel 2007 Reader freezes because of conditional formatting - @rentalhost [#575](https://github.com/PHPOffice/PHPExcel/issues/575)
+- Readers will now parse files containing worksheet titles over 31 characters [#176](https://github.com/PHPOffice/PhpSpreadsheet/pull/176)
+
+### General
+
+- Whitespace after toRichTextObject() - @MarkBaker [#554](https://github.com/PHPOffice/PHPExcel/issues/554)
+- Optimize vlookup() sort - @umpirsky [#548](https://github.com/PHPOffice/PHPExcel/issues/548)
+- c:max and c:min elements shall NOT be inside c:orientation elements - @vitalyrepin [#869](https://github.com/PHPOffice/PHPExcel/pull/869)
+- Implement actual timezone adjustment into PHPExcel_Shared_Date::PHPToExcel - @sim642 [#489](https://github.com/PHPOffice/PHPExcel/pull/489)
+
+### BREAKING CHANGE
+
+- Introduction of namespaces for all classes, eg: `PHPExcel_Calculation_Functions` becomes `PhpOffice\PhpSpreadsheet\Calculation\Functions`
+- Some classes were renamed for clarity and/or consistency:
+
+For a comprehensive list of all class changes, and a semi-automated migration path, read the [migration guide](./docs/topics/migration-from-PHPExcel.md).
+
+- Dropped `PHPExcel_Calculation_Functions::VERSION()`. Composer or git should be used to know the version.
+- Dropped `PHPExcel_Settings::setPdfRenderer()` and `PHPExcel_Settings::setPdfRenderer()`. Composer should be used to autoload PDF libs.
+- Dropped support for HHVM
+
+## Previous versions of PHPExcel
+
+The changelog for the project when it was called PHPExcel is [still available](./CHANGELOG.PHPExcel.md).
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/CONTRIBUTING.md b/htdocs/includes/phpoffice/phpspreadsheet/CONTRIBUTING.md
new file mode 100644
index 00000000000..aed13fe2db2
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/CONTRIBUTING.md
@@ -0,0 +1,11 @@
+# Want to contribute?
+
+If you would like to contribute, here are some notes and guidelines:
+
+ - All new development happens on feature/fix branches, and are then merged to the `master` branch once stable; so the `master` branch is always the most up-to-date, working code
+ - Tagged releases are made from the `master` branch
+ - If you are going to be submitting a pull request, please fork from `master`, and submit your pull request back as a fix/feature branch referencing the GitHub issue number
+ - Code style might be automatically fixed by `composer fix`
+ - All code changes must be validated by `composer check`
+ - [Helpful article about forking](https://help.github.com/articles/fork-a-repo/ "Forking a GitHub repository")
+ - [Helpful article about pull requests](https://help.github.com/articles/using-pull-requests/ "Pull Requests")
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/LICENSE b/htdocs/includes/phpoffice/phpspreadsheet/LICENSE
new file mode 100644
index 00000000000..3ec5723dde1
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 PhpSpreadsheet Authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/mkdocs.yml b/htdocs/includes/phpoffice/phpspreadsheet/mkdocs.yml
new file mode 100644
index 00000000000..cf87a142a33
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/mkdocs.yml
@@ -0,0 +1,7 @@
+site_name: PhpSpreadsheet Documentation
+repo_url: https://github.com/PHPOffice/phpspreadsheet
+edit_uri: edit/master/docs/
+
+theme: readthedocs
+extra_css:
+ - extra/extra.css
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/phpunit.xml.dist b/htdocs/includes/phpoffice/phpspreadsheet/phpunit.xml.dist
new file mode 100644
index 00000000000..be3643d8a41
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/phpunit.xml.dist
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ ./tests/PhpSpreadsheetTests
+
+
+
+ ./src
+
+ ./src/PhpSpreadsheet/Shared/JAMA
+ ./src/PhpSpreadsheet/Writer/PDF
+
+
+
+
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/Bootstrap.php b/htdocs/includes/phpoffice/phpspreadsheet/src/Bootstrap.php
new file mode 100644
index 00000000000..f15de26a6c4
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/Bootstrap.php
@@ -0,0 +1,22 @@
+=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d{1,7})';
+ const CALCULATION_REGEXP_CELLREF = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?\b([a-z]{1,3})\$?(\d{1,7})(?![\w.])';
// Named Range of cells
const CALCULATION_REGEXP_NAMEDRANGE = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_A-Z][_A-Z0-9\.]*)';
// Error
@@ -66,6 +66,15 @@ class Calculation
*/
private $calculationCacheEnabled = true;
+ /**
+ * Used to generate unique store keys.
+ *
+ * @var int
+ */
+ private $branchStoreKeyCounter = 0;
+
+ private $branchPruningEnabled = true;
+
/**
* List of operators that can be used within formulae
* The true/false value indicates whether it is a binary operator or a unary operator.
@@ -254,6 +263,11 @@ class Calculation
'functionCall' => [Logical::class, 'logicalAnd'],
'argumentCount' => '1+',
],
+ 'ARABIC' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig::class, 'ARABIC'],
+ 'argumentCount' => '1',
+ ],
'AREAS' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [Functions::class, 'DUMMY'],
@@ -319,6 +333,11 @@ class Calculation
'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '1',
],
+ 'BASE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig::class, 'BASE'],
+ 'argumentCount' => '2,3',
+ ],
'BESSELI' => [
'category' => Category::CATEGORY_ENGINEERING,
'functionCall' => [Engineering::class, 'BESSELI'],
@@ -527,7 +546,7 @@ class Calculation
],
'COUNTIFS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [Statistical::class, 'COUNTIFS'],
'argumentCount' => '2+',
],
'COUPDAYBS' => [
@@ -895,6 +914,16 @@ class Calculation
'functionCall' => [MathTrig::class, 'FLOOR'],
'argumentCount' => '2',
],
+ 'FLOOR.MATH' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig::class, 'FLOORMATH'],
+ 'argumentCount' => '3',
+ ],
+ 'FLOOR.PRECISE' => [
+ 'category' => Category::CATEGORY_MATH_AND_TRIG,
+ 'functionCall' => [MathTrig::class, 'FLOORPRECISE'],
+ 'argumentCount' => '2',
+ ],
'FORECAST' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'FORECAST'],
@@ -1018,6 +1047,16 @@ class Calculation
'functionCall' => [Logical::class, 'IFERROR'],
'argumentCount' => '2',
],
+ 'IFNA' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Logical::class, 'IFNA'],
+ 'argumentCount' => '2',
+ ],
+ 'IFS' => [
+ 'category' => Category::CATEGORY_LOGICAL,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2+',
+ ],
'IMABS' => [
'category' => Category::CATEGORY_ENGINEERING,
'functionCall' => [Engineering::class, 'IMABS'],
@@ -1356,10 +1395,10 @@ class Calculation
'functionCall' => [Statistical::class, 'MAXA'],
'argumentCount' => '1+',
],
- 'MAXIF' => [
+ 'MAXIFS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MAXIF'],
- 'argumentCount' => '2+',
+ 'functionCall' => [Statistical::class, 'MAXIFS'],
+ 'argumentCount' => '3+',
],
'MDETERM' => [
'category' => Category::CATEGORY_MATH_AND_TRIG,
@@ -1401,10 +1440,10 @@ class Calculation
'functionCall' => [Statistical::class, 'MINA'],
'argumentCount' => '1+',
],
- 'MINIF' => [
+ 'MINIFS' => [
'category' => Category::CATEGORY_STATISTICAL,
- 'functionCall' => [Statistical::class, 'MINIF'],
- 'argumentCount' => '2+',
+ 'functionCall' => [Statistical::class, 'MINIFS'],
+ 'argumentCount' => '3+',
],
'MINUTE' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
@@ -1474,7 +1513,12 @@ class Calculation
'NETWORKDAYS' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
'functionCall' => [DateTime::class, 'NETWORKDAYS'],
- 'argumentCount' => '2+',
+ 'argumentCount' => '2-3',
+ ],
+ 'NETWORKDAYS.INTL' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-4',
],
'NOMINAL' => [
'category' => Category::CATEGORY_FINANCIAL,
@@ -2143,7 +2187,12 @@ class Calculation
'WORKDAY' => [
'category' => Category::CATEGORY_DATE_AND_TIME,
'functionCall' => [DateTime::class, 'WORKDAY'],
- 'argumentCount' => '2+',
+ 'argumentCount' => '2-3',
+ ],
+ 'WORKDAY.INTL' => [
+ 'category' => Category::CATEGORY_DATE_AND_TIME,
+ 'functionCall' => [Functions::class, 'DUMMY'],
+ 'argumentCount' => '2-4',
],
'XIRR' => [
'category' => Category::CATEGORY_FINANCIAL,
@@ -2196,7 +2245,7 @@ class Calculation
private static $controlFunctions = [
'MKMATRIX' => [
'argumentCount' => '*',
- 'functionCall' => 'self::mkMatrix',
+ 'functionCall' => [__CLASS__, 'mkMatrix'],
],
];
@@ -2251,6 +2300,7 @@ class Calculation
public function flushInstance()
{
$this->clearCalculationCache();
+ $this->clearBranchStore();
}
/**
@@ -2394,6 +2444,32 @@ class Calculation
}
}
+ /**
+ * Enable/disable calculation cache.
+ *
+ * @param bool $pValue
+ * @param mixed $enabled
+ */
+ public function setBranchPruningEnabled($enabled)
+ {
+ $this->branchPruningEnabled = $enabled;
+ }
+
+ public function enableBranchPruning()
+ {
+ $this->setBranchPruningEnabled(true);
+ }
+
+ public function disableBranchPruning()
+ {
+ $this->setBranchPruningEnabled(false);
+ }
+
+ public function clearBranchStore()
+ {
+ $this->branchStoreKeyCounter = 0;
+ }
+
/**
* Get the currently defined locale code.
*
@@ -2416,7 +2492,7 @@ class Calculation
// Identify our locale and language
$language = $locale = strtolower($locale);
if (strpos($locale, '_') !== false) {
- list($language) = explode('_', $locale);
+ [$language] = explode('_', $locale);
}
if (count(self::$validLocaleLanguages) == 1) {
self::loadLocales();
@@ -2441,9 +2517,9 @@ class Calculation
// Retrieve the list of locale or language specific function names
$localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($localeFunctions as $localeFunction) {
- list($localeFunction) = explode('##', $localeFunction); // Strip out comments
+ [$localeFunction] = explode('##', $localeFunction); // Strip out comments
if (strpos($localeFunction, '=') !== false) {
- list($fName, $lfName) = explode('=', $localeFunction);
+ [$fName, $lfName] = explode('=', $localeFunction);
$fName = trim($fName);
$lfName = trim($lfName);
if ((isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
@@ -2466,9 +2542,9 @@ class Calculation
if (file_exists($configFile)) {
$localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($localeSettings as $localeSetting) {
- list($localeSetting) = explode('##', $localeSetting); // Strip out comments
+ [$localeSetting] = explode('##', $localeSetting); // Strip out comments
if (strpos($localeSetting, '=') !== false) {
- list($settingName, $settingValue) = explode('=', $localeSetting);
+ [$settingName, $settingValue] = explode('=', $localeSetting);
$settingName = strtoupper(trim($settingName));
switch ($settingName) {
case 'ARGUMENTSEPARATOR':
@@ -2772,7 +2848,7 @@ class Calculation
}
self::$returnArrayAsType = $returnArrayAsType;
- if ($result === null) {
+ if ($result === null && $pCell->getWorksheet()->getSheetView()->getShowZeros()) {
return 0;
} elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
return Functions::NAN();
@@ -2823,13 +2899,13 @@ class Calculation
$this->debugLog->clearLog();
$this->cyclicReferenceStack->clear();
+ $resetCache = $this->getCalculationCacheEnabled();
if ($this->spreadsheet !== null && $cellID === null && $pCell === null) {
$cellID = 'A1';
$pCell = $this->spreadsheet->getActiveSheet()->getCell($cellID);
} else {
// Disable calculation cacheing because it only applies to cell calculations, not straight formulae
// But don't actually flush any cache
- $resetCache = $this->getCalculationCacheEnabled();
$this->calculationCacheEnabled = false;
}
@@ -2862,6 +2938,7 @@ class Calculation
if (($this->calculationCacheEnabled) && (isset($this->calculationCache[$cellReference]))) {
$this->debugLog->writeDebugLog('Retrieving value for cell ', $cellReference, ' from cache');
// Return the cached result
+
$cellValue = $this->calculationCache[$cellReference];
return true;
@@ -2977,17 +3054,17 @@ class Calculation
// Examine each of the two operands, and turn them into an array if they aren't one already
// Note that this function should only be called if one or both of the operand is already an array
if (!is_array($operand1)) {
- list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand2);
+ [$matrixRows, $matrixColumns] = self::getMatrixDimensions($operand2);
$operand1 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand1));
$resize = 0;
} elseif (!is_array($operand2)) {
- list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand1);
+ [$matrixRows, $matrixColumns] = self::getMatrixDimensions($operand1);
$operand2 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand2));
$resize = 0;
}
- list($matrix1Rows, $matrix1Columns) = self::getMatrixDimensions($operand1);
- list($matrix2Rows, $matrix2Columns) = self::getMatrixDimensions($operand2);
+ [$matrix1Rows, $matrix1Columns] = self::getMatrixDimensions($operand1);
+ [$matrix2Rows, $matrix2Columns] = self::getMatrixDimensions($operand2);
if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) {
$resize = 1;
}
@@ -3197,7 +3274,7 @@ class Calculation
/**
* @param string $formula
*
- * @return string
+ * @return false|string False indicates an error
*/
private function convertMatrixReferences($formula)
{
@@ -3321,9 +3398,53 @@ class Calculation
// - is a negation or + is a positive operator rather than an operation
$expectingOperand = false; // We use this test in syntax-checking the expression to determine whether an operand
// should be null in a function call
+
+ // IF branch pruning
+ // currently pending storeKey (last item of the storeKeysStack
+ $pendingStoreKey = null;
+ // stores a list of storeKeys (string[])
+ $pendingStoreKeysStack = [];
+ $expectingConditionMap = []; // ['storeKey' => true, ...]
+ $expectingThenMap = []; // ['storeKey' => true, ...]
+ $expectingElseMap = []; // ['storeKey' => true, ...]
+ $parenthesisDepthMap = []; // ['storeKey' => 4, ...]
+
// The guts of the lexical parser
// Loop through the formula extracting each operator and operand in turn
while (true) {
+ // Branch pruning: we adapt the output item to the context (it will
+ // be used to limit its computation)
+ $currentCondition = null;
+ $currentOnlyIf = null;
+ $currentOnlyIfNot = null;
+ $previousStoreKey = null;
+ $pendingStoreKey = end($pendingStoreKeysStack);
+
+ if ($this->branchPruningEnabled) {
+ // this is a condition ?
+ if (isset($expectingConditionMap[$pendingStoreKey]) && $expectingConditionMap[$pendingStoreKey]) {
+ $currentCondition = $pendingStoreKey;
+ $stackDepth = count($pendingStoreKeysStack);
+ if ($stackDepth > 1) { // nested if
+ $previousStoreKey = $pendingStoreKeysStack[$stackDepth - 2];
+ }
+ }
+ if (isset($expectingThenMap[$pendingStoreKey]) && $expectingThenMap[$pendingStoreKey]) {
+ $currentOnlyIf = $pendingStoreKey;
+ } elseif (isset($previousStoreKey)) {
+ if (isset($expectingThenMap[$previousStoreKey]) && $expectingThenMap[$previousStoreKey]) {
+ $currentOnlyIf = $previousStoreKey;
+ }
+ }
+ if (isset($expectingElseMap[$pendingStoreKey]) && $expectingElseMap[$pendingStoreKey]) {
+ $currentOnlyIfNot = $pendingStoreKey;
+ } elseif (isset($previousStoreKey)) {
+ if (isset($expectingElseMap[$previousStoreKey]) && $expectingElseMap[$previousStoreKey]) {
+ $currentOnlyIfNot = $previousStoreKey;
+ }
+ }
+ }
+
$opCharacter = $formula[$index]; // Get the first character of the value at the current index position
if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula[$index + 1]]))) {
$opCharacter .= $formula[++$index];
@@ -3333,10 +3454,12 @@ class Calculation
$isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus?
- $stack->push('Unary Operator', '~'); // Put a negation on the stack
+ // Put a negation on the stack
+ $stack->push('Unary Operator', '~', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
++$index; // and drop the negation symbol
} elseif ($opCharacter == '%' && $expectingOperator) {
- $stack->push('Unary Operator', '%'); // Put a percentage on the stack
+ // Put a percentage on the stack
+ $stack->push('Unary Operator', '%', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
++$index;
} elseif ($opCharacter == '+' && !$expectingOperator) { // Positive (unary plus rather than binary operator plus) can be discarded?
++$index; // Drop the redundant plus symbol
@@ -3349,7 +3472,10 @@ class Calculation
@(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
$output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
}
- $stack->push('Binary Operator', $opCharacter); // Finally put our current operator onto the stack
+
+ // Finally put our current operator onto the stack
+ $stack->push('Binary Operator', $opCharacter, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
+
++$index;
$expectingOperator = false;
} elseif ($opCharacter == ')' && $expectingOperator) { // Are we expecting to close a parenthesis?
@@ -3361,7 +3487,29 @@ class Calculation
$output[] = $o2;
}
$d = $stack->last(2);
- if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $d['value'], $matches)) { // Did this parenthesis just close a function?
+
+ // Branch pruning we decrease the depth whether is it a function
+ // call or a parenthesis
+ if (!empty($pendingStoreKey)) {
+ $parenthesisDepthMap[$pendingStoreKey] -= 1;
+ }
+
+ if (is_array($d) && preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $d['value'], $matches)) { // Did this parenthesis just close a function?
+ if (!empty($pendingStoreKey) && $parenthesisDepthMap[$pendingStoreKey] == -1) {
+ // we are closing an IF(
+ if ($d['value'] != 'IF(') {
+ return $this->raiseFormulaError('Parser bug we should be in an "IF("');
+ }
+ if ($expectingConditionMap[$pendingStoreKey]) {
+ return $this->raiseFormulaError('We should not be expecting a condition');
+ }
+ $expectingThenMap[$pendingStoreKey] = false;
+ $expectingElseMap[$pendingStoreKey] = false;
+ $parenthesisDepthMap[$pendingStoreKey] -= 1;
+ array_pop($pendingStoreKeysStack);
+ unset($pendingStoreKey);
+ }
+
$functionName = $matches[1]; // Get the function name
$d = $stack->pop();
$argumentCount = $d['value']; // See how many arguments there were (argument count is the next value stored on the stack)
@@ -3422,6 +3570,20 @@ class Calculation
}
++$index;
} elseif ($opCharacter == ',') { // Is this the separator for function arguments?
+ if (!empty($pendingStoreKey) &&
+ $parenthesisDepthMap[$pendingStoreKey] == 0
+ ) {
+ // We must go to the IF next argument
+ if ($expectingConditionMap[$pendingStoreKey]) {
+ $expectingConditionMap[$pendingStoreKey] = false;
+ $expectingThenMap[$pendingStoreKey] = true;
+ } elseif ($expectingThenMap[$pendingStoreKey]) {
+ $expectingThenMap[$pendingStoreKey] = false;
+ $expectingElseMap[$pendingStoreKey] = true;
+ } elseif ($expectingElseMap[$pendingStoreKey]) {
+ return $this->raiseFormulaError('Reaching fourth argument of an IF');
+ }
+ }
while (($o2 = $stack->pop()) && $o2['value'] != '(') { // Pop off the stack back to the last (
if ($o2 === null) {
return $this->raiseFormulaError('Formula Error: Unexpected ,');
@@ -3439,13 +3601,19 @@ class Calculation
return $this->raiseFormulaError('Formula Error: Unexpected ,');
}
$d = $stack->pop();
- $stack->push($d['type'], ++$d['value'], $d['reference']); // increment the argument count
- $stack->push('Brace', '('); // put the ( back on, we'll need to pop back to it again
+ $itemStoreKey = $d['storeKey'] ?? null;
+ $itemOnlyIf = $d['onlyIf'] ?? null;
+ $itemOnlyIfNot = $d['onlyIfNot'] ?? null;
+ $stack->push($d['type'], ++$d['value'], $d['reference'], $itemStoreKey, $itemOnlyIf, $itemOnlyIfNot); // increment the argument count
+ $stack->push('Brace', '(', null, $itemStoreKey, $itemOnlyIf, $itemOnlyIfNot); // put the ( back on, we'll need to pop back to it again
$expectingOperator = false;
$expectingOperand = true;
++$index;
} elseif ($opCharacter == '(' && !$expectingOperator) {
- $stack->push('Brace', '(');
+ if (!empty($pendingStoreKey)) { // Branch pruning: we go deeper
+ $parenthesisDepthMap[$pendingStoreKey] += 1;
+ }
+ $stack->push('Brace', '(', null, $currentCondition, $currentOnlyIf, $currentOnlyIf);
++$index;
} elseif ($isOperandOrFunction && !$expectingOperator) { // do we now have a function/variable/number?
$expectingOperator = true;
@@ -3456,13 +3624,28 @@ class Calculation
if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $val, $matches)) {
$val = preg_replace('/\s/u', '', $val);
if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function
- $stack->push('Function', strtoupper($val));
+ $valToUpper = strtoupper($val);
+ // here $matches[1] will contain values like "IF"
+ // and $val "IF("
+ if ($this->branchPruningEnabled && ($valToUpper == 'IF(')) { // we handle a new if
+ $pendingStoreKey = $this->getUnusedBranchStoreKey();
+ $pendingStoreKeysStack[] = $pendingStoreKey;
+ $expectingConditionMap[$pendingStoreKey] = true;
+ $parenthesisDepthMap[$pendingStoreKey] = 0;
+ } else { // this is not a if but we good deeper
+ if (!empty($pendingStoreKey) && array_key_exists($pendingStoreKey, $parenthesisDepthMap)) {
+ $parenthesisDepthMap[$pendingStoreKey] += 1;
+ }
+ }
+
+ $stack->push('Function', $valToUpper, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
+ // tests if the function is closed right after opening
$ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index + $length), $amatch);
if ($ax) {
- $stack->push('Operand Count for Function ' . strtoupper($val) . ')', 0);
+ $stack->push('Operand Count for Function ' . $valToUpper . ')', 0, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
$expectingOperator = true;
} else {
- $stack->push('Operand Count for Function ' . strtoupper($val) . ')', 1);
+ $stack->push('Operand Count for Function ' . $valToUpper . ')', 1, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
$expectingOperator = false;
}
$stack->push('Brace', '(');
@@ -3475,7 +3658,7 @@ class Calculation
// If the last entry on the stack was a : operator, then we have a cell range reference
$testPrevOp = $stack->last(1);
- if ($testPrevOp['value'] == ':') {
+ if ($testPrevOp !== null && $testPrevOp['value'] == ':') {
// If we have a worksheet reference, then we're playing with a 3D reference
if ($matches[2] == '') {
// Otherwise, we 'inherit' the worksheet reference from the start cell reference
@@ -3490,32 +3673,39 @@ class Calculation
}
}
- $output[] = ['type' => 'Cell Reference', 'value' => $val, 'reference' => $val];
+ $outputItem = $stack->getStackItem('Cell Reference', $val, $val, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
+
+ $output[] = $outputItem;
} else { // it's a variable, constant, string, number or boolean
// If the last entry on the stack was a : operator, then we may have a row or column range reference
$testPrevOp = $stack->last(1);
- if ($testPrevOp['value'] == ':') {
+ if ($testPrevOp !== null && $testPrevOp['value'] === ':') {
$startRowColRef = $output[count($output) - 1]['value'];
- list($rangeWS1, $startRowColRef) = Worksheet::extractSheetTitle($startRowColRef, true);
+ [$rangeWS1, $startRowColRef] = Worksheet::extractSheetTitle($startRowColRef, true);
+ $rangeSheetRef = $rangeWS1;
if ($rangeWS1 != '') {
$rangeWS1 .= '!';
}
- list($rangeWS2, $val) = Worksheet::extractSheetTitle($val, true);
+ [$rangeWS2, $val] = Worksheet::extractSheetTitle($val, true);
if ($rangeWS2 != '') {
$rangeWS2 .= '!';
} else {
$rangeWS2 = $rangeWS1;
}
+ $refSheet = $pCellParent;
+ if ($pCellParent !== null && $rangeSheetRef !== $pCellParent->getTitle()) {
+ $refSheet = $pCellParent->getParent()->getSheetByName($rangeSheetRef);
+ }
if ((is_int($startRowColRef)) && (ctype_digit($val)) &&
($startRowColRef <= 1048576) && ($val <= 1048576)) {
// Row range
- $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007
+ $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007
$output[count($output) - 1]['value'] = $rangeWS1 . 'A' . $startRowColRef;
$val = $rangeWS2 . $endRowColRef . $val;
} elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) &&
(strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) {
// Column range
- $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007
+ $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007
$output[count($output) - 1]['value'] = $rangeWS1 . strtoupper($startRowColRef) . '1';
$val = $rangeWS2 . $val . $endRowColRef;
}
@@ -3537,7 +3727,7 @@ class Calculation
} elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
$val = self::$excelConstants[$localeConstant];
}
- $details = ['type' => 'Value', 'value' => $val, 'reference' => null];
+ $details = $stack->getStackItem('Value', $val, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
if ($localeConstant) {
$details['localeValue'] = $localeConstant;
}
@@ -3557,7 +3747,7 @@ class Calculation
} elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) {
return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'");
} else { // I don't even want to know what you did to get here
- return $this->raiseFormulaError('Formula Error: An unexpected error occured');
+ return $this->raiseFormulaError('Formula Error: An unexpected error occurred');
}
// Test for end of formula string
if ($index == strlen($formula)) {
@@ -3640,9 +3830,85 @@ class Calculation
$pCellParent = ($pCell !== null) ? $pCell->getParent() : null;
$stack = new Stack();
+ // Stores branches that have been pruned
+ $fakedForBranchPruning = [];
+ // help us to know when pruning ['branchTestId' => true/false]
+ $branchStore = [];
+
// Loop through each token in turn
foreach ($tokens as $tokenData) {
$token = $tokenData['value'];
+
+ // Branch pruning: skip useless resolutions
+ $storeKey = $tokenData['storeKey'] ?? null;
+ if ($this->branchPruningEnabled && isset($tokenData['onlyIf'])) {
+ $onlyIfStoreKey = $tokenData['onlyIf'];
+ $storeValue = $branchStore[$onlyIfStoreKey] ?? null;
+ $storeValueAsBool = ($storeValue === null) ?
+ true : (bool) Functions::flattenSingleValue($storeValue);
+ if (is_array($storeValue)) {
+ $wrappedItem = end($storeValue);
+ $storeValue = end($wrappedItem);
+ }
+
+ if (isset($storeValue)
+ && (
+ !$storeValueAsBool
+ || Functions::isError($storeValue)
+ || ($storeValue === 'Pruned branch')
+ )
+ ) {
+ // If branching value is not true, we don't need to compute
+ if (!isset($fakedForBranchPruning['onlyIf-' . $onlyIfStoreKey])) {
+ $stack->push('Value', 'Pruned branch (only if ' . $onlyIfStoreKey . ') ' . $token);
+ $fakedForBranchPruning['onlyIf-' . $onlyIfStoreKey] = true;
+ }
+
+ if (isset($storeKey)) {
+ // We are processing an if condition
+ // We cascade the pruning to the depending branches
+ $branchStore[$storeKey] = 'Pruned branch';
+ $fakedForBranchPruning['onlyIfNot-' . $storeKey] = true;
+ $fakedForBranchPruning['onlyIf-' . $storeKey] = true;
+ }
+
+ continue;
+ }
+ }
+
+ if ($this->branchPruningEnabled && isset($tokenData['onlyIfNot'])) {
+ $onlyIfNotStoreKey = $tokenData['onlyIfNot'];
+ $storeValue = $branchStore[$onlyIfNotStoreKey] ?? null;
+ $storeValueAsBool = ($storeValue === null) ?
+ true : (bool) Functions::flattenSingleValue($storeValue);
+ if (is_array($storeValue)) {
+ $wrappedItem = end($storeValue);
+ $storeValue = end($wrappedItem);
+ }
+ if (isset($storeValue)
+ && (
+ $storeValueAsBool
+ || Functions::isError($storeValue)
+ || ($storeValue === 'Pruned branch'))
+ ) {
+ // If branching value is true, we don't need to compute
+ if (!isset($fakedForBranchPruning['onlyIfNot-' . $onlyIfNotStoreKey])) {
+ $stack->push('Value', 'Pruned branch (only if not ' . $onlyIfNotStoreKey . ') ' . $token);
+ $fakedForBranchPruning['onlyIfNot-' . $onlyIfNotStoreKey] = true;
+ }
+
+ if (isset($storeKey)) {
+ // We are processing an if condition
+ // We cascade the pruning to the depending branches
+ $branchStore[$storeKey] = 'Pruned branch';
+ $fakedForBranchPruning['onlyIfNot-' . $storeKey] = true;
+ $fakedForBranchPruning['onlyIf-' . $storeKey] = true;
+ }
+
+ continue;
+ }
+ }
+
// if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
if (isset(self::$binaryOperators[$token])) {
// We must have two operands, error if we don't
@@ -3672,18 +3938,21 @@ class Calculation
case '<=': // Less than or Equal to
case '=': // Equality
case '<>': // Inequality
- $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
+ $result = $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
break;
// Binary Operators
case ':': // Range
if (strpos($operand1Data['reference'], '!') !== false) {
- list($sheet1, $operand1Data['reference']) = Worksheet::extractSheetTitle($operand1Data['reference'], true);
+ [$sheet1, $operand1Data['reference']] = Worksheet::extractSheetTitle($operand1Data['reference'], true);
} else {
$sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : '';
}
- list($sheet2, $operand2Data['reference']) = Worksheet::extractSheetTitle($operand2Data['reference'], true);
+ [$sheet2, $operand2Data['reference']] = Worksheet::extractSheetTitle($operand2Data['reference'], true);
if (empty($sheet2)) {
$sheet2 = $sheet1;
}
@@ -3728,23 +3997,38 @@ class Calculation
break;
case '+': // Addition
- $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'plusEquals', $stack);
+ $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'plusEquals', $stack);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
break;
case '-': // Subtraction
- $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'minusEquals', $stack);
+ $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'minusEquals', $stack);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
break;
case '*': // Multiplication
- $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayTimesEquals', $stack);
+ $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayTimesEquals', $stack);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
break;
case '/': // Division
- $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayRightDivide', $stack);
+ $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'arrayRightDivide', $stack);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
break;
case '^': // Exponential
- $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'power', $stack);
+ $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, 'power', $stack);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
break;
case '&': // Concatenation
@@ -3777,6 +4061,10 @@ class Calculation
$this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
$stack->push('Value', $result);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
+
break;
case '|': // Intersect
$rowIntersect = array_intersect_key($operand1, $operand2);
@@ -3821,6 +4109,9 @@ class Calculation
}
$this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
$stack->push('Value', $result);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
} else {
$this->executeNumericBinaryOperation($multiplier, $arg, '*', 'arrayTimesEquals', $stack);
}
@@ -3894,9 +4185,16 @@ class Calculation
}
}
$stack->push('Value', $cellValue, $cellRef);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $cellValue;
+ }
- // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
+ // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
} elseif (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $token, $matches)) {
+ if ($pCellParent) {
+ $pCell->attach($pCellParent);
+ }
+
$functionName = $matches[1];
$argCount = $stack->pop();
$argCount = $argCount['value'];
@@ -3939,6 +4237,7 @@ class Calculation
}
}
}
+
// Reverse the order of the arguments
krsort($args);
@@ -3963,22 +4262,32 @@ class Calculation
}
unset($arg);
}
+
$result = call_user_func_array($functionCall, $args);
if ($functionName != 'MKMATRIX') {
$this->debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($result));
}
$stack->push('Value', self::wrapResult($result));
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $result;
+ }
}
} else {
// if the token is a number, boolean, string or an Excel error, push it onto the stack
if (isset(self::$excelConstants[strtoupper($token)])) {
$excelConstant = strtoupper($token);
$stack->push('Constant Value', self::$excelConstants[$excelConstant]);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = self::$excelConstants[$excelConstant];
+ }
$this->debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant]));
} elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == '"') || ($token[0] == '#')) {
$stack->push('Value', $token);
- // if the token is a named range, push the named range name onto the stack
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $token;
+ }
+ // if the token is a named range, push the named range name onto the stack
} elseif (preg_match('/^' . self::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $token, $matches)) {
$namedRange = $matches[6];
$this->debugLog->writeDebugLog('Evaluating Named Range ', $namedRange);
@@ -3987,6 +4296,9 @@ class Calculation
$pCell->attach($pCellParent);
$this->debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->showTypeDetails($cellValue));
$stack->push('Named Range', $cellValue, $namedRange);
+ if (isset($storeKey)) {
+ $branchStore[$storeKey] = $cellValue;
+ }
} else {
return $this->raiseFormulaError("undefined variable '$token'");
}
@@ -4048,7 +4360,7 @@ class Calculation
* @param Stack $stack
* @param bool $recursingArrays
*
- * @return bool
+ * @return mixed
*/
private function executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, Stack &$stack, $recursingArrays = false)
{
@@ -4085,7 +4397,7 @@ class Calculation
// And push the result onto the stack
$stack->push('Array', $result);
- return true;
+ return $result;
}
// Simple validate the two operands if they are string values
@@ -4175,7 +4487,7 @@ class Calculation
// And push the result onto the stack
$stack->push('Value', $result);
- return true;
+ return $result;
}
/**
@@ -4201,7 +4513,7 @@ class Calculation
* @param string $matrixFunction
* @param mixed $stack
*
- * @return bool
+ * @return bool|mixed
*/
private function executeNumericBinaryOperation($operand1, $operand2, $operation, $matrixFunction, &$stack)
{
@@ -4279,7 +4591,7 @@ class Calculation
// And push the result onto the stack
$stack->push('Value', $result);
- return true;
+ return $result;
}
// trigger an error, but nicely, if need be
@@ -4312,7 +4624,7 @@ class Calculation
if ($pSheet !== null) {
$pSheetName = $pSheet->getTitle();
if (strpos($pRange, '!') !== false) {
- list($pSheetName, $pRange) = Worksheet::extractSheetTitle($pRange, true);
+ [$pSheetName, $pRange] = Worksheet::extractSheetTitle($pRange, true);
$pSheet = $this->spreadsheet->getSheetByName($pSheetName);
}
@@ -4365,7 +4677,7 @@ class Calculation
if ($pSheet !== null) {
$pSheetName = $pSheet->getTitle();
if (strpos($pRange, '!') !== false) {
- list($pSheetName, $pRange) = Worksheet::extractSheetTitle($pRange, true);
+ [$pSheetName, $pRange] = Worksheet::extractSheetTitle($pRange, true);
$pSheet = $this->spreadsheet->getSheetByName($pSheetName);
}
@@ -4389,7 +4701,7 @@ class Calculation
$aReferences = Coordinate::extractAllCellReferencesInRange($pRange);
if (!isset($aReferences[1])) {
// Single cell (or single column or row) in range
- list($currentCol, $currentRow) = Coordinate::coordinateFromString($aReferences[0]);
+ [$currentCol, $currentRow] = Coordinate::coordinateFromString($aReferences[0]);
if ($pSheet->cellExists($aReferences[0])) {
$returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
} else {
@@ -4399,7 +4711,7 @@ class Calculation
// Extract cell data for all cells in the range
foreach ($aReferences as $reference) {
// Extract range
- list($currentCol, $currentRow) = Coordinate::coordinateFromString($reference);
+ [$currentCol, $currentRow] = Coordinate::coordinateFromString($reference);
if ($pSheet->cellExists($reference)) {
$returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
} else {
@@ -4483,4 +4795,26 @@ class Calculation
return $args;
}
+
+ private function getUnusedBranchStoreKey()
+ {
+ $storeKeyValue = 'storeKey-' . $this->branchStoreKeyCounter;
+ ++$this->branchStoreKeyCounter;
+
+ return $storeKeyValue;
+ }
+
+ private function getTokensAsString($tokens)
+ {
+ $tokensStr = array_map(function ($token) {
+ $value = $token['value'] ?? 'no value';
+ while (is_array($value)) {
+ $value = array_pop($value);
+ }
+
+ return $value;
+ }, $tokens);
+
+ return '[ ' . implode(' | ', $tokensStr) . ' ]';
+ }
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php
index f6d9d8bb4bf..d31b00dd7df 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php
@@ -147,7 +147,7 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float
+ * @return float|string
*/
public static function DAVERAGE($database, $field, $criteria)
{
@@ -452,7 +452,7 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float
+ * @return float|string
*/
public static function DSTDEV($database, $field, $criteria)
{
@@ -493,7 +493,7 @@ class Database
* the column label in which you specify a condition for the
* column.
*
- * @return float
+ * @return float|string
*/
public static function DSTDEVP($database, $field, $criteria)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
index 3c39db2af7f..4f85edeb9c0 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
@@ -16,7 +16,7 @@ class DateTime
*/
public static function isLeapYear($year)
{
- return (($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0);
+ return (($year % 4) === 0) && (($year % 100) !== 0) || (($year % 400) === 0);
}
/**
@@ -156,11 +156,11 @@ class DateTime
$retValue = (float) Date::PHPToExcel(time());
break;
- case Functions::RETURNDATE_PHP_NUMERIC:
+ case Functions::RETURNDATE_UNIX_TIMESTAMP:
$retValue = (int) time();
break;
- case Functions::RETURNDATE_PHP_OBJECT:
+ case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
$retValue = new \DateTime();
break;
@@ -200,11 +200,11 @@ class DateTime
$retValue = (float) $excelDateTime;
break;
- case Functions::RETURNDATE_PHP_NUMERIC:
+ case Functions::RETURNDATE_UNIX_TIMESTAMP:
$retValue = (int) Date::excelToTimestamp($excelDateTime);
break;
- case Functions::RETURNDATE_PHP_OBJECT:
+ case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
$retValue = Date::excelToDateTimeObject($excelDateTime);
break;
@@ -325,9 +325,9 @@ class DateTime
switch (Functions::getReturnDateType()) {
case Functions::RETURNDATE_EXCEL:
return (float) $excelDateValue;
- case Functions::RETURNDATE_PHP_NUMERIC:
+ case Functions::RETURNDATE_UNIX_TIMESTAMP:
return (int) Date::excelToTimestamp($excelDateValue);
- case Functions::RETURNDATE_PHP_OBJECT:
+ case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
return Date::excelToDateTimeObject($excelDateValue);
}
}
@@ -420,9 +420,9 @@ class DateTime
}
return (float) Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
- case Functions::RETURNDATE_PHP_NUMERIC:
+ case Functions::RETURNDATE_UNIX_TIMESTAMP:
return (int) Date::excelToTimestamp(Date::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
- case Functions::RETURNDATE_PHP_OBJECT:
+ case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
$dayAdjust = 0;
if ($hour < 0) {
$dayAdjust = floor($hour / 24);
@@ -472,7 +472,6 @@ class DateTime
*/
public static function DATEVALUE($dateValue = 1)
{
- $dateValueOrig = $dateValue;
$dateValue = trim(Functions::flattenSingleValue($dateValue), '"');
// Strip any ordinals because they're allowed in Excel (English only)
$dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
@@ -492,7 +491,7 @@ class DateTime
$yearFound = true;
}
}
- if ((count($t1) == 1) && (strpos($t, ':') != false)) {
+ if ((count($t1) == 1) && (strpos($t, ':') !== false)) {
// We've been fed a time value without any date
return 0.0;
} elseif (count($t1) == 2) {
@@ -569,9 +568,9 @@ class DateTime
switch (Functions::getReturnDateType()) {
case Functions::RETURNDATE_EXCEL:
return (float) $excelDateValue;
- case Functions::RETURNDATE_PHP_NUMERIC:
+ case Functions::RETURNDATE_UNIX_TIMESTAMP:
return (int) Date::excelToTimestamp($excelDateValue);
- case Functions::RETURNDATE_PHP_OBJECT:
+ case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
return new \DateTime($PHPDateArray['year'] . '-' . $PHPDateArray['month'] . '-' . $PHPDateArray['day'] . ' 00:00:00');
}
}
@@ -631,9 +630,9 @@ class DateTime
switch (Functions::getReturnDateType()) {
case Functions::RETURNDATE_EXCEL:
return (float) $excelDateValue;
- case Functions::RETURNDATE_PHP_NUMERIC:
+ case Functions::RETURNDATE_UNIX_TIMESTAMP:
return (int) $phpDateValue = Date::excelToTimestamp($excelDateValue + 25569) - 3600;
- case Functions::RETURNDATE_PHP_OBJECT:
+ case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
return new \DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
}
}
@@ -683,7 +682,6 @@ class DateTime
$endMonths = $PHPEndDateObject->format('n');
$endYears = $PHPEndDateObject->format('Y');
- $retVal = Functions::NAN();
switch ($unit) {
case 'D':
$retVal = (int) $difference;
@@ -880,6 +878,8 @@ class DateTime
*
* Excel Function:
* YEARFRAC(startDate,endDate[,method])
+ * See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html
+ * for description of algorithm used in Excel
*
* @category Date/Time Functions
*
@@ -894,7 +894,7 @@ class DateTime
* 3 Actual/365
* 4 European 30/360
*
- * @return float fraction of the year
+ * @return float|string fraction of the year, or a string containing an error
*/
public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
{
@@ -908,6 +908,11 @@ class DateTime
if (is_string($endDate = self::getDateValue($endDate))) {
return Functions::VALUE();
}
+ if ($startDate > $endDate) {
+ $temp = $startDate;
+ $startDate = $endDate;
+ $endDate = $temp;
+ }
if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
switch ($method) {
@@ -918,46 +923,43 @@ class DateTime
$startYear = self::YEAR($startDate);
$endYear = self::YEAR($endDate);
$years = $endYear - $startYear + 1;
- $leapDays = 0;
+ $startMonth = self::MONTHOFYEAR($startDate);
+ $startDay = self::DAYOFMONTH($startDate);
+ $endMonth = self::MONTHOFYEAR($endDate);
+ $endDay = self::DAYOFMONTH($endDate);
+ $startMonthDay = 100 * $startMonth + $startDay;
+ $endMonthDay = 100 * $endMonth + $endDay;
if ($years == 1) {
if (self::isLeapYear($endYear)) {
- $startMonth = self::MONTHOFYEAR($startDate);
- $endMonth = self::MONTHOFYEAR($endDate);
- $endDay = self::DAYOFMONTH($endDate);
- if (($startMonth < 3) ||
- (($endMonth * 100 + $endDay) >= (2 * 100 + 29))) {
- $leapDays += 1;
+ $tmpCalcAnnualBasis = 366;
+ } else {
+ $tmpCalcAnnualBasis = 365;
+ }
+ } elseif ($years == 2 && $startMonthDay >= $endMonthDay) {
+ if (self::isLeapYear($startYear)) {
+ if ($startMonthDay <= 229) {
+ $tmpCalcAnnualBasis = 366;
+ } else {
+ $tmpCalcAnnualBasis = 365;
}
+ } elseif (self::isLeapYear($endYear)) {
+ if ($endMonthDay >= 229) {
+ $tmpCalcAnnualBasis = 366;
+ } else {
+ $tmpCalcAnnualBasis = 365;
+ }
+ } else {
+ $tmpCalcAnnualBasis = 365;
}
} else {
+ $tmpCalcAnnualBasis = 0;
for ($year = $startYear; $year <= $endYear; ++$year) {
- if ($year == $startYear) {
- $startMonth = self::MONTHOFYEAR($startDate);
- $startDay = self::DAYOFMONTH($startDate);
- if ($startMonth < 3) {
- $leapDays += (self::isLeapYear($year)) ? 1 : 0;
- }
- } elseif ($year == $endYear) {
- $endMonth = self::MONTHOFYEAR($endDate);
- $endDay = self::DAYOFMONTH($endDate);
- if (($endMonth * 100 + $endDay) >= (2 * 100 + 29)) {
- $leapDays += (self::isLeapYear($year)) ? 1 : 0;
- }
- } else {
- $leapDays += (self::isLeapYear($year)) ? 1 : 0;
- }
+ $tmpCalcAnnualBasis += self::isLeapYear($year) ? 366 : 365;
}
- if ($years == 2) {
- if (($leapDays == 0) && (self::isLeapYear($startYear)) && ($days > 365)) {
- $leapDays = 1;
- } elseif ($days < 366) {
- $years = 1;
- }
- }
- $leapDays /= $years;
+ $tmpCalcAnnualBasis /= $years;
}
- return $days / (365 + $leapDays);
+ return $days / $tmpCalcAnnualBasis;
case 2:
return self::DATEDIF($startDate, $endDate) / 360;
case 3:
@@ -1154,9 +1156,9 @@ class DateTime
switch (Functions::getReturnDateType()) {
case Functions::RETURNDATE_EXCEL:
return (float) $endDate;
- case Functions::RETURNDATE_PHP_NUMERIC:
+ case Functions::RETURNDATE_UNIX_TIMESTAMP:
return (int) Date::excelToTimestamp($endDate);
- case Functions::RETURNDATE_PHP_OBJECT:
+ case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
return Date::excelToDateTimeObject($endDate);
}
}
@@ -1239,7 +1241,7 @@ class DateTime
// Execute function
$PHPDateObject = Date::excelToDateTimeObject($dateValue);
- $DoW = $PHPDateObject->format('w');
+ $DoW = (int) $PHPDateObject->format('w');
$firstDay = 1;
switch ($style) {
@@ -1248,13 +1250,13 @@ class DateTime
break;
case 2:
- if ($DoW == 0) {
+ if ($DoW === 0) {
$DoW = 7;
}
break;
case 3:
- if ($DoW == 0) {
+ if ($DoW === 0) {
$DoW = 7;
}
$firstDay = 0;
@@ -1272,9 +1274,39 @@ class DateTime
}
}
- return (int) $DoW;
+ return $DoW;
}
+ const STARTWEEK_SUNDAY = 1;
+ const STARTWEEK_MONDAY = 2;
+ const STARTWEEK_MONDAY_ALT = 11;
+ const STARTWEEK_TUESDAY = 12;
+ const STARTWEEK_WEDNESDAY = 13;
+ const STARTWEEK_THURSDAY = 14;
+ const STARTWEEK_FRIDAY = 15;
+ const STARTWEEK_SATURDAY = 16;
+ const STARTWEEK_SUNDAY_ALT = 17;
+ const DOW_SUNDAY = 1;
+ const DOW_MONDAY = 2;
+ const DOW_TUESDAY = 3;
+ const DOW_WEDNESDAY = 4;
+ const DOW_THURSDAY = 5;
+ const DOW_FRIDAY = 6;
+ const DOW_SATURDAY = 7;
+ const STARTWEEK_MONDAY_ISO = 21;
+ const METHODARR = [
+ self::STARTWEEK_SUNDAY => self::DOW_SUNDAY,
+ self::DOW_MONDAY,
+ self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY,
+ self::DOW_TUESDAY,
+ self::DOW_WEDNESDAY,
+ self::DOW_THURSDAY,
+ self::DOW_FRIDAY,
+ self::DOW_SATURDAY,
+ self::DOW_SUNDAY,
+ self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO,
+ ];
+
/**
* WEEKNUM.
*
@@ -1293,41 +1325,51 @@ class DateTime
* @param int $method Week begins on Sunday or Monday
* 1 or omitted Week begins on Sunday.
* 2 Week begins on Monday.
+ * 11 Week begins on Monday.
+ * 12 Week begins on Tuesday.
+ * 13 Week begins on Wednesday.
+ * 14 Week begins on Thursday.
+ * 15 Week begins on Friday.
+ * 16 Week begins on Saturday.
+ * 17 Week begins on Sunday.
+ * 21 ISO (Jan. 4 is week 1, begins on Monday).
*
* @return int|string Week Number
*/
- public static function WEEKNUM($dateValue = 1, $method = 1)
+ public static function WEEKNUM($dateValue = 1, $method = self::STARTWEEK_SUNDAY)
{
$dateValue = Functions::flattenSingleValue($dateValue);
$method = Functions::flattenSingleValue($method);
if (!is_numeric($method)) {
return Functions::VALUE();
- } elseif (($method < 1) || ($method > 2)) {
- return Functions::NAN();
}
- $method = floor($method);
+ $method = (int) $method;
+ if (!array_key_exists($method, self::METHODARR)) {
+ return Functions::NaN();
+ }
+ $method = self::METHODARR[$method];
- if ($dateValue === null) {
- $dateValue = 1;
- } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
+ $dateValue = self::getDateValue($dateValue);
+ if (is_string($dateValue)) {
return Functions::VALUE();
- } elseif ($dateValue < 0.0) {
+ }
+ if ($dateValue < 0.0) {
return Functions::NAN();
}
// Execute function
$PHPDateObject = Date::excelToDateTimeObject($dateValue);
+ if ($method == self::STARTWEEK_MONDAY_ISO) {
+ return (int) $PHPDateObject->format('W');
+ }
$dayOfYear = $PHPDateObject->format('z');
$PHPDateObject->modify('-' . $dayOfYear . ' days');
$firstDayOfFirstWeek = $PHPDateObject->format('w');
$daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
- $interval = $dayOfYear - $daysInFirstWeek;
- $weekOfYear = floor($interval / 7) + 1;
-
- if ($daysInFirstWeek) {
- ++$weekOfYear;
- }
+ $daysInFirstWeek += 7 * !$daysInFirstWeek;
+ $endFirstWeek = $daysInFirstWeek - 1;
+ $weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7);
return (int) $weekOfYear;
}
@@ -1591,9 +1633,9 @@ class DateTime
switch (Functions::getReturnDateType()) {
case Functions::RETURNDATE_EXCEL:
return (float) Date::PHPToExcel($PHPDateObject);
- case Functions::RETURNDATE_PHP_NUMERIC:
+ case Functions::RETURNDATE_UNIX_TIMESTAMP:
return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
- case Functions::RETURNDATE_PHP_OBJECT:
+ case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
return $PHPDateObject;
}
}
@@ -1640,9 +1682,9 @@ class DateTime
switch (Functions::getReturnDateType()) {
case Functions::RETURNDATE_EXCEL:
return (float) Date::PHPToExcel($PHPDateObject);
- case Functions::RETURNDATE_PHP_NUMERIC:
+ case Functions::RETURNDATE_UNIX_TIMESTAMP:
return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject));
- case Functions::RETURNDATE_PHP_OBJECT:
+ case Functions::RETURNDATE_PHP_DATETIME_OBJECT:
return $PHPDateObject;
}
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php
index 3f1b48bd74c..c3942b2b2b3 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php
@@ -785,7 +785,7 @@ class Engineering
* If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
* If $ord < 0, BESSELI returns the #NUM! error value.
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function BESSELI($x, $ord)
{
@@ -839,7 +839,7 @@ class Engineering
* If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
* If $ord < 0, BESSELJ returns the #NUM! error value.
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function BESSELJ($x, $ord)
{
@@ -932,7 +932,7 @@ class Engineering
* If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
* If $ord < 0, BESSELK returns the #NUM! error value.
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function BESSELK($x, $ord)
{
@@ -1021,7 +1021,7 @@ class Engineering
* If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
* If $ord < 0, BESSELK returns the #NUM! error value.
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function BESSELY($x, $ord)
{
@@ -2713,7 +2713,7 @@ class Engineering
* @param string $fromUOM the units for value
* @param string $toUOM the units for the result
*
- * @return float
+ * @return float|string
*/
public static function CONVERTUOM($value, $fromUOM, $toUOM)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php
index 3cb6d40a52e..412b7a620e7 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php
@@ -71,7 +71,7 @@ class Financial
* 3 365
* 4 European 360
*
- * @return int
+ * @return int|string Result, or a string containing an error
*/
private static function daysPerYear($year, $basis = 0)
{
@@ -144,7 +144,7 @@ class Financial
* 3 Actual/365
* 4 European 30/360
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function ACCRINT($issue, $firstinterest, $settlement, $rate, $par = 1000, $frequency = 1, $basis = 0)
{
@@ -197,7 +197,7 @@ class Financial
* 3 Actual/365
* 4 European 30/360
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function ACCRINTM($issue, $settlement, $rate, $par = 1000, $basis = 0)
{
@@ -417,7 +417,7 @@ class Financial
return Functions::VALUE();
}
- if (($settlement > $maturity) ||
+ if (($settlement >= $maturity) ||
(!self::isValidFrequency($frequency)) ||
(($basis < 0) || ($basis > 4))) {
return Functions::NAN();
@@ -476,7 +476,7 @@ class Financial
return Functions::VALUE();
}
- if (($settlement > $maturity) ||
+ if (($settlement >= $maturity) ||
(!self::isValidFrequency($frequency)) ||
(($basis < 0) || ($basis > 4))) {
return Functions::NAN();
@@ -550,7 +550,7 @@ class Financial
return Functions::VALUE();
}
- if (($settlement > $maturity) ||
+ if (($settlement >= $maturity) ||
(!self::isValidFrequency($frequency)) ||
(($basis < 0) || ($basis > 4))) {
return Functions::NAN();
@@ -610,7 +610,7 @@ class Financial
return Functions::VALUE();
}
- if (($settlement > $maturity) ||
+ if (($settlement >= $maturity) ||
(!self::isValidFrequency($frequency)) ||
(($basis < 0) || ($basis > 4))) {
return Functions::NAN();
@@ -667,26 +667,22 @@ class Financial
return Functions::VALUE();
}
- if (($settlement > $maturity) ||
+ if (($settlement >= $maturity) ||
(!self::isValidFrequency($frequency)) ||
(($basis < 0) || ($basis > 4))) {
return Functions::NAN();
}
- $settlement = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
- $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis) * 365;
+ $daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
+ $daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis) * $daysPerYear;
switch ($frequency) {
case 1: // annual payments
- return ceil($daysBetweenSettlementAndMaturity / 360);
case 2: // half-yearly
- return ceil($daysBetweenSettlementAndMaturity / 180);
case 4: // quarterly
- return ceil($daysBetweenSettlementAndMaturity / 90);
case 6: // bimonthly
- return ceil($daysBetweenSettlementAndMaturity / 60);
case 12: // monthly
- return ceil($daysBetweenSettlementAndMaturity / 30);
+ return ceil($daysBetweenSettlementAndMaturity / $daysPerYear * $frequency);
}
return Functions::VALUE();
@@ -740,7 +736,7 @@ class Financial
return Functions::VALUE();
}
- if (($settlement > $maturity) ||
+ if (($settlement >= $maturity) ||
(!self::isValidFrequency($frequency)) ||
(($basis < 0) || ($basis > 4))) {
return Functions::NAN();
@@ -1428,7 +1424,7 @@ class Financial
* @param float $finance_rate The interest rate you pay on the money used in the cash flows
* @param float $reinvestment_rate The interest rate you receive on the cash flows as you reinvest them
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function MIRR($values, $finance_rate, $reinvestment_rate)
{
@@ -1470,7 +1466,7 @@ class Financial
* @param float $effect_rate Effective interest rate
* @param int $npery Number of compounding payments per year
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function NOMINAL($effect_rate = 0, $npery = 0)
{
@@ -1497,7 +1493,7 @@ class Financial
* @param float $fv Future Value
* @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0)
{
@@ -1565,7 +1561,7 @@ class Financial
* @param float $pv Present Value
* @param float $fv Future Value
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function PDURATION($rate = 0, $pv = 0, $fv = 0)
{
@@ -1594,7 +1590,7 @@ class Financial
* @param float $fv Future Value
* @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
{
@@ -1629,7 +1625,7 @@ class Financial
* @param float $fv Future Value
* @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
{
@@ -1713,7 +1709,7 @@ class Financial
* 3 Actual/365
* 4 European 30/360
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = 0)
{
@@ -1759,7 +1755,7 @@ class Financial
* 3 Actual/365
* 4 European 30/360
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis = 0)
{
@@ -1817,7 +1813,7 @@ class Financial
* @param float $fv Future Value
* @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0)
{
@@ -1933,7 +1929,7 @@ class Financial
* 3 Actual/365
* 4 European 30/360
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis = 0)
{
@@ -1969,7 +1965,7 @@ class Financial
* @param float $pv Present Value
* @param float $fv Future Value
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function RRI($nper = 0, $pv = 0, $fv = 0)
{
@@ -1996,7 +1992,7 @@ class Financial
* @param mixed $salvage Value at the end of the depreciation
* @param mixed $life Number of periods over which the asset is depreciated
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function SLN($cost, $salvage, $life)
{
@@ -2026,7 +2022,7 @@ class Financial
* @param mixed $life Number of periods over which the asset is depreciated
* @param mixed $period Period
*
- * @return float|string
+ * @return float|string Result, or a string containing an error
*/
public static function SYD($cost, $salvage, $life, $period)
{
@@ -2058,7 +2054,7 @@ class Financial
* The maturity date is the date when the Treasury bill expires.
* @param int $discount The Treasury bill's discount rate
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function TBILLEQ($settlement, $maturity, $discount)
{
@@ -2097,7 +2093,7 @@ class Financial
* The maturity date is the date when the Treasury bill expires.
* @param int $discount The Treasury bill's discount rate
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function TBILLPRICE($settlement, $maturity, $discount)
{
@@ -2152,7 +2148,7 @@ class Financial
* The maturity date is the date when the Treasury bill expires.
* @param int $price The Treasury bill's price per $100 face value
*
- * @return float
+ * @return float|mixed|string
*/
public static function TBILLYIELD($settlement, $maturity, $price)
{
@@ -2187,6 +2183,23 @@ class Financial
return Functions::VALUE();
}
+ /**
+ * XIRR.
+ *
+ * Returns the internal rate of return for a schedule of cash flows that is not necessarily periodic.
+ *
+ * Excel Function:
+ * =XIRR(values,dates,guess)
+ *
+ * @param float[] $values A series of cash flow payments
+ * The series of values must contain at least one positive value & one negative value
+ * @param mixed[] $dates A series of payment dates
+ * The first payment date indicates the beginning of the schedule of payments
+ * All other dates must be later than this date, but they may occur in any order
+ * @param float $guess An optional guess at the expected answer
+ *
+ * @return float|mixed|string
+ */
public static function XIRR($values, $dates, $guess = 0.1)
{
if ((!is_array($values)) && (!is_array($dates))) {
@@ -2199,11 +2212,28 @@ class Financial
return Functions::NAN();
}
+ $datesCount = count($dates);
+ for ($i = 0; $i < $datesCount; ++$i) {
+ $dates[$i] = DateTime::getDateValue($dates[$i]);
+ if (!is_numeric($dates[$i])) {
+ return Functions::VALUE();
+ }
+ }
+ if (min($dates) != $dates[0]) {
+ return Functions::NAN();
+ }
+
// create an initial range, with a root somewhere between 0 and guess
$x1 = 0.0;
$x2 = $guess;
$f1 = self::XNPV($x1, $values, $dates);
+ if (!is_numeric($f1)) {
+ return $f1;
+ }
$f2 = self::XNPV($x2, $values, $dates);
+ if (!is_numeric($f2)) {
+ return $f2;
+ }
for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
if (($f1 * $f2) < 0.0) {
break;
@@ -2214,7 +2244,7 @@ class Financial
}
}
if (($f1 * $f2) > 0.0) {
- return Functions::VALUE();
+ return Functions::NAN();
}
$f = self::XNPV($x1, $values, $dates);
@@ -2251,15 +2281,15 @@ class Financial
* =XNPV(rate,values,dates)
*
* @param float $rate the discount rate to apply to the cash flows
- * @param array of float $values A series of cash flows that corresponds to a schedule of payments in dates.
+ * @param float[] $values A series of cash flows that corresponds to a schedule of payments in dates.
* The first payment is optional and corresponds to a cost or payment that occurs at the beginning of the investment.
* If the first value is a cost or payment, it must be a negative value. All succeeding payments are discounted based on a 365-day year.
* The series of values must contain at least one positive value and one negative value.
- * @param array of mixed $dates A schedule of payment dates that corresponds to the cash flow payments.
+ * @param mixed[] $dates A schedule of payment dates that corresponds to the cash flow payments.
* The first payment date indicates the beginning of the schedule of payments.
* All other dates must be later than this date, but they may occur in any order.
*
- * @return float
+ * @return float|mixed|string
*/
public static function XNPV($rate, $values, $dates)
{
@@ -2277,7 +2307,7 @@ class Financial
return Functions::NAN();
}
if ((min($values) > 0) || (max($values) < 0)) {
- return Functions::VALUE();
+ return Functions::NAN();
}
$xnpv = 0.0;
@@ -2309,7 +2339,7 @@ class Financial
* 3 Actual/365
* 4 European 30/360
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis = 0)
{
@@ -2360,7 +2390,7 @@ class Financial
* 3 Actual/365
* 4 European 30/360
*
- * @return float
+ * @return float|string Result, or a string containing an error
*/
public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis = 0)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
index 0a607c702ab..1862b008a14 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
@@ -17,8 +17,11 @@ class Functions
const COMPATIBILITY_EXCEL = 'Excel';
const COMPATIBILITY_GNUMERIC = 'Gnumeric';
const COMPATIBILITY_OPENOFFICE = 'OpenOfficeCalc';
+
const RETURNDATE_PHP_NUMERIC = 'P';
+ const RETURNDATE_UNIX_TIMESTAMP = 'P';
const RETURNDATE_PHP_OBJECT = 'O';
+ const RETURNDATE_PHP_DATETIME_OBJECT = 'O';
const RETURNDATE_EXCEL = 'E';
/**
@@ -101,16 +104,16 @@ class Functions
*
* @param string $returnDateType Return Date Format
* Permitted values are:
- * Functions::RETURNDATE_PHP_NUMERIC 'P'
- * Functions::RETURNDATE_PHP_OBJECT 'O'
+ * Functions::RETURNDATE_UNIX_TIMESTAMP 'P'
+ * Functions::RETURNDATE_PHP_DATETIME_OBJECT 'O'
* Functions::RETURNDATE_EXCEL 'E'
*
* @return bool Success or failure
*/
public static function setReturnDateType($returnDateType)
{
- if (($returnDateType == self::RETURNDATE_PHP_NUMERIC) ||
- ($returnDateType == self::RETURNDATE_PHP_OBJECT) ||
+ if (($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP) ||
+ ($returnDateType == self::RETURNDATE_PHP_DATETIME_OBJECT) ||
($returnDateType == self::RETURNDATE_EXCEL)
) {
self::$returnDateType = $returnDateType;
@@ -128,8 +131,8 @@ class Functions
*
* @return string Return Date Format
* Possible Return values are:
- * Functions::RETURNDATE_PHP_NUMERIC 'P'
- * Functions::RETURNDATE_PHP_OBJECT 'O'
+ * Functions::RETURNDATE_UNIX_TIMESTAMP 'P'
+ * Functions::RETURNDATE_PHP_DATETIME_OBJECT 'O'
* Functions::RETURNDATE_EXCEL 'E'
*/
public static function getReturnDateType()
@@ -267,25 +270,29 @@ class Functions
public static function ifCondition($condition)
{
$condition = self::flattenSingleValue($condition);
- if (!isset($condition[0]) && !is_numeric($condition)) {
+
+ if ($condition === '') {
$condition = '=""';
}
- if (!in_array($condition[0], ['>', '<', '='])) {
+
+ if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='])) {
if (!is_numeric($condition)) {
$condition = Calculation::wrapResult(strtoupper($condition));
}
- return '=' . $condition;
+ return str_replace('""""', '""', '=' . $condition);
}
preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
- list(, $operator, $operand) = $matches;
+ [, $operator, $operand] = $matches;
- if (!is_numeric($operand)) {
+ if (is_numeric(trim($operand, '"'))) {
+ $operand = trim($operand, '"');
+ } elseif (!is_numeric($operand)) {
$operand = str_replace('"', '""', $operand);
$operand = Calculation::wrapResult(strtoupper($operand));
}
- return $operator . $operand;
+ return str_replace('""""', '""', $operator . $operand);
}
/**
@@ -639,7 +646,7 @@ class Functions
public static function flattenSingleValue($value = '')
{
while (is_array($value)) {
- $value = array_pop($value);
+ $value = array_shift($value);
}
return $value;
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php
index 3e6c5e74177..416d119ac8e 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php
@@ -266,6 +266,10 @@ class Logical
*/
public static function statementIf($condition = true, $returnIfTrue = 0, $returnIfFalse = false)
{
+ if (Functions::isError($condition)) {
+ return $condition;
+ }
+
$condition = ($condition === null) ? true : (bool) Functions::flattenSingleValue($condition);
$returnIfTrue = ($returnIfTrue === null) ? 0 : Functions::flattenSingleValue($returnIfTrue);
$returnIfFalse = ($returnIfFalse === null) ? false : Functions::flattenSingleValue($returnIfFalse);
@@ -347,4 +351,25 @@ class Logical
return self::statementIf(Functions::isError($testValue), $errorpart, $testValue);
}
+
+ /**
+ * IFNA.
+ *
+ * Excel Function:
+ * =IFNA(testValue,napart)
+ *
+ * @category Logical Functions
+ *
+ * @param mixed $testValue Value to check, is also the value returned when not an NA
+ * @param mixed $napart Value to return when testValue is an NA condition
+ *
+ * @return mixed The value of errorpart or testValue determined by error condition
+ */
+ public static function IFNA($testValue = '', $napart = '')
+ {
+ $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
+ $napart = ($napart === null) ? '' : Functions::flattenSingleValue($napart);
+
+ return self::statementIf(Functions::isNa($testValue), $napart, $testValue);
+ }
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
index e6b0f3203f8..48434300b29 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
@@ -98,9 +98,9 @@ class LookupRef
return (int) Coordinate::columnIndexFromString($columnKey);
}
} else {
- list($sheet, $cellAddress) = Worksheet::extractSheetTitle($cellAddress, true);
+ [$sheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) {
- list($startAddress, $endAddress) = explode(':', $cellAddress);
+ [$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = preg_replace('/[^a-z]/i', '', $startAddress);
$endAddress = preg_replace('/[^a-z]/i', '', $endAddress);
$returnValue = [];
@@ -138,7 +138,7 @@ class LookupRef
reset($cellAddress);
$isMatrix = (is_numeric(key($cellAddress)));
- list($columns, $rows) = Calculation::getMatrixDimensions($cellAddress);
+ [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
if ($isMatrix) {
return $rows;
@@ -175,9 +175,9 @@ class LookupRef
}
}
} else {
- list($sheet, $cellAddress) = Worksheet::extractSheetTitle($cellAddress, true);
+ [$sheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) {
- list($startAddress, $endAddress) = explode(':', $cellAddress);
+ [$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = preg_replace('/\D/', '', $startAddress);
$endAddress = preg_replace('/\D/', '', $endAddress);
$returnValue = [];
@@ -187,7 +187,7 @@ class LookupRef
return $returnValue;
}
- list($cellAddress) = explode(':', $cellAddress);
+ [$cellAddress] = explode(':', $cellAddress);
return (int) preg_replace('/\D/', '', $cellAddress);
}
@@ -215,7 +215,7 @@ class LookupRef
reset($cellAddress);
$isMatrix = (is_numeric(key($cellAddress)));
- list($columns, $rows) = Calculation::getMatrixDimensions($cellAddress);
+ [$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
if ($isMatrix) {
return $columns;
@@ -285,7 +285,7 @@ class LookupRef
$cellAddress1 = $cellAddress;
$cellAddress2 = null;
if (strpos($cellAddress, ':') !== false) {
- list($cellAddress1, $cellAddress2) = explode(':', $cellAddress);
+ [$cellAddress1, $cellAddress2] = explode(':', $cellAddress);
}
if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress1, $matches)) ||
@@ -295,7 +295,7 @@ class LookupRef
}
if (strpos($cellAddress, '!') !== false) {
- list($sheetName, $cellAddress) = Worksheet::extractSheetTitle($cellAddress, true);
+ [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
$sheetName = trim($sheetName, "'");
$pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
} else {
@@ -306,7 +306,7 @@ class LookupRef
}
if (strpos($cellAddress, '!') !== false) {
- list($sheetName, $cellAddress) = Worksheet::extractSheetTitle($cellAddress, true);
+ [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
$sheetName = trim($sheetName, "'");
$pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
} else {
@@ -359,16 +359,16 @@ class LookupRef
$sheetName = null;
if (strpos($cellAddress, '!')) {
- list($sheetName, $cellAddress) = Worksheet::extractSheetTitle($cellAddress, true);
+ [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
$sheetName = trim($sheetName, "'");
}
if (strpos($cellAddress, ':')) {
- list($startCell, $endCell) = explode(':', $cellAddress);
+ [$startCell, $endCell] = explode(':', $cellAddress);
} else {
$startCell = $endCell = $cellAddress;
}
- list($startCellColumn, $startCellRow) = Coordinate::coordinateFromString($startCell);
- list($endCellColumn, $endCellRow) = Coordinate::coordinateFromString($endCell);
+ [$startCellColumn, $startCellRow] = Coordinate::coordinateFromString($startCell);
+ [$endCellColumn, $endCellRow] = Coordinate::coordinateFromString($endCell);
$startCellRow += $rows;
$startCellColumn = Coordinate::columnIndexFromString($startCellColumn) - 1;
@@ -464,9 +464,10 @@ class LookupRef
*
* @param mixed $lookupValue The value that you want to match in lookup_array
* @param mixed $lookupArray The range of cells being searched
- * @param mixed $matchType The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below. If match_type is 1 or -1, the list has to be ordered.
+ * @param mixed $matchType The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below.
+ * If match_type is 1 or -1, the list has to be ordered.
*
- * @return int The relative position of the found item
+ * @return int|string The relative position of the found item
*/
public static function MATCH($lookupValue, $lookupArray, $matchType = 1)
{
@@ -474,9 +475,10 @@ class LookupRef
$lookupValue = Functions::flattenSingleValue($lookupValue);
$matchType = ($matchType === null) ? 1 : (int) Functions::flattenSingleValue($matchType);
- $initialLookupValue = $lookupValue;
- // MATCH is not case sensitive
- $lookupValue = StringHelper::strToLower($lookupValue);
+ // MATCH is not case sensitive, so we convert lookup value to be lower cased in case it's string type.
+ if (is_string($lookupValue)) {
+ $lookupValue = StringHelper::strToLower($lookupValue);
+ }
// Lookup_value type has to be number, text, or logical values
if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
@@ -522,16 +524,54 @@ class LookupRef
// find the match
// **
- if ($matchType == 0 || $matchType == 1) {
+ if ($matchType === 0 || $matchType === 1) {
foreach ($lookupArray as $i => $lookupArrayValue) {
- $onlyNumeric = is_numeric($lookupArrayValue) && is_numeric($lookupValue);
- $onlyNumericExactMatch = $onlyNumeric && $lookupArrayValue == $lookupValue;
- $nonOnlyNumericExactMatch = !$onlyNumeric && $lookupArrayValue === $lookupValue;
- $exactMatch = $onlyNumericExactMatch || $nonOnlyNumericExactMatch;
- if (($matchType == 0) && $exactMatch) {
- // exact match
- return $i + 1;
- } elseif (($matchType == 1) && ($lookupArrayValue <= $lookupValue)) {
+ $typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
+ $exactTypeMatch = $typeMatch && $lookupArrayValue === $lookupValue;
+ $nonOnlyNumericExactMatch = !$typeMatch && $lookupArrayValue === $lookupValue;
+ $exactMatch = $exactTypeMatch || $nonOnlyNumericExactMatch;
+
+ if ($matchType === 0) {
+ if ($typeMatch && is_string($lookupValue) && (bool) preg_match('/([\?\*])/', $lookupValue)) {
+ $splitString = $lookupValue;
+ $chars = array_map(function ($i) use ($splitString) {
+ return mb_substr($splitString, $i, 1);
+ }, range(0, mb_strlen($splitString) - 1));
+
+ $length = count($chars);
+ $pattern = '/^';
+ for ($j = 0; $j < $length; ++$j) {
+ if ($chars[$j] === '~') {
+ if (isset($chars[$j + 1])) {
+ if ($chars[$j + 1] === '*') {
+ $pattern .= preg_quote($chars[$j + 1], '/');
+ ++$j;
+ } elseif ($chars[$j + 1] === '?') {
+ $pattern .= preg_quote($chars[$j + 1], '/');
+ ++$j;
+ }
+ } else {
+ $pattern .= preg_quote($chars[$j], '/');
+ }
+ } elseif ($chars[$j] === '*') {
+ $pattern .= '.*';
+ } elseif ($chars[$j] === '?') {
+ $pattern .= '.{1}';
+ } else {
+ $pattern .= preg_quote($chars[$j], '/');
+ }
+ }
+
+ $pattern .= '$/';
+ if ((bool) preg_match($pattern, $lookupArrayValue)) {
+ // exact match
+ return $i + 1;
+ }
+ } elseif ($exactMatch) {
+ // exact match
+ return $i + 1;
+ }
+ } elseif (($matchType === 1) && $typeMatch && ($lookupArrayValue <= $lookupValue)) {
$i = array_search($i, $keySet);
// The current value is the (first) match
@@ -539,26 +579,26 @@ class LookupRef
}
}
} else {
- // matchType = -1
-
- // "Special" case: since the array it's supposed to be ordered in descending order, the
- // Excel algorithm gives up immediately if the first element is smaller than the searched value
- if ($lookupArray[0] < $lookupValue) {
- return Functions::NA();
- }
-
$maxValueKey = null;
// The basic algorithm is:
// Iterate and keep the highest match until the next element is smaller than the searched value.
// Return immediately if perfect match is found
foreach ($lookupArray as $i => $lookupArrayValue) {
- if ($lookupArrayValue == $lookupValue) {
+ $typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
+ $exactTypeMatch = $typeMatch && $lookupArrayValue === $lookupValue;
+ $nonOnlyNumericExactMatch = !$typeMatch && $lookupArrayValue === $lookupValue;
+ $exactMatch = $exactTypeMatch || $nonOnlyNumericExactMatch;
+
+ if ($exactMatch) {
// Another "special" case. If a perfect match is found,
// the algorithm gives up immediately
return $i + 1;
- } elseif ($lookupArrayValue >= $lookupValue) {
+ } elseif ($typeMatch & $lookupArrayValue >= $lookupValue) {
$maxValueKey = $i + 1;
+ } elseif ($typeMatch & $lookupArrayValue < $lookupValue) {
+ //Excel algorithm gives up immediately if the first element is smaller than the searched value
+ break;
}
}
@@ -794,8 +834,10 @@ class LookupRef
$lookupLower = StringHelper::strToLower($lookup_value);
$rowDataLower = StringHelper::strToLower($rowData);
- if (($bothNumeric && $rowData > $lookup_value) ||
- ($bothNotNumeric && $rowDataLower > $lookupLower)) {
+ if ($not_exact_match && (
+ ($bothNumeric && $rowData > $lookup_value) ||
+ ($bothNotNumeric && $rowDataLower > $lookupLower)
+ )) {
break;
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php
index a06f3a384c9..f94c8fcc5e2 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php
@@ -38,6 +38,88 @@ class MathTrig
return ($num - ($num % $n)) / $n;
}
+ /**
+ * ARABIC.
+ *
+ * Converts a Roman numeral to an Arabic numeral.
+ *
+ * Excel Function:
+ * ARABIC(text)
+ *
+ * @category Mathematical and Trigonometric Functions
+ *
+ * @param string $roman
+ *
+ * @return int|string the arabic numberal contrived from the roman numeral
+ */
+ public static function ARABIC($roman)
+ {
+ // An empty string should return 0
+ $roman = substr(trim(strtoupper((string) Functions::flattenSingleValue($roman))), 0, 255);
+ if ($roman === '') {
+ return 0;
+ }
+
+ // Convert the roman numeral to an arabic number
+ $negativeNumber = $roman[0] === '-';
+ if ($negativeNumber) {
+ $roman = substr($roman, 1);
+ }
+
+ try {
+ $arabic = self::calculateArabic(str_split($roman));
+ } catch (\Exception $e) {
+ return Functions::VALUE(); // Invalid character detected
+ }
+
+ if ($negativeNumber) {
+ $arabic *= -1; // The number should be negative
+ }
+
+ return $arabic;
+ }
+
+ /**
+ * Recursively calculate the arabic value of a roman numeral.
+ *
+ * @param array $roman
+ * @param int $sum
+ * @param int $subtract
+ *
+ * @return int
+ */
+ protected static function calculateArabic(array $roman, &$sum = 0, $subtract = 0)
+ {
+ $lookup = [
+ 'M' => 1000,
+ 'D' => 500,
+ 'C' => 100,
+ 'L' => 50,
+ 'X' => 10,
+ 'V' => 5,
+ 'I' => 1,
+ ];
+
+ $numeral = array_shift($roman);
+ if (!isset($lookup[$numeral])) {
+ throw new \Exception('Invalid character detected');
+ }
+
+ $arabic = $lookup[$numeral];
+ if (count($roman) > 0 && isset($lookup[$roman[0]]) && $arabic < $lookup[$roman[0]]) {
+ $subtract += $arabic;
+ } else {
+ $sum += ($arabic - $subtract);
+ $subtract = 0;
+ }
+
+ if (count($roman) > 0) {
+ self::calculateArabic($roman, $sum, $subtract);
+ }
+
+ return $sum;
+ }
+
/**
* ATAN2.
*
@@ -59,7 +141,7 @@ class MathTrig
* @param float $xCoordinate the x-coordinate of the point
* @param float $yCoordinate the y-coordinate of the point
*
- * @return float the inverse tangent of the specified x- and y-coordinates
+ * @return float|string the inverse tangent of the specified x- and y-coordinates, or a string containing an error
*/
public static function ATAN2($xCoordinate = null, $yCoordinate = null)
{
@@ -84,6 +166,49 @@ class MathTrig
return Functions::VALUE();
}
+ /**
+ * BASE.
+ *
+ * Converts a number into a text representation with the given radix (base).
+ *
+ * Excel Function:
+ * BASE(Number, Radix [Min_length])
+ *
+ * @category Mathematical and Trigonometric Functions
+ *
+ * @param float $number
+ * @param float $radix
+ * @param int $minLength
+ *
+ * @return string the text representation with the given radix (base)
+ */
+ public static function BASE($number, $radix, $minLength = null)
+ {
+ $number = Functions::flattenSingleValue($number);
+ $radix = Functions::flattenSingleValue($radix);
+ $minLength = Functions::flattenSingleValue($minLength);
+
+ if (is_numeric($number) && is_numeric($radix) && ($minLength === null || is_numeric($minLength))) {
+ // Truncate to an integer
+ $number = (int) $number;
+ $radix = (int) $radix;
+ $minLength = (int) $minLength;
+
+ if ($number < 0 || $number >= 2 ** 53 || $radix < 2 || $radix > 36) {
+ return Functions::NAN(); // Numeric range constraints
+ }
+
+ $outcome = strtoupper((string) base_convert($number, 10, $radix));
+ if ($minLength !== null) {
+ $outcome = str_pad($outcome, $minLength, '0', STR_PAD_LEFT); // String padding
+ }
+
+ return $outcome;
+ }
+
+ return Functions::VALUE();
+ }
+
/**
* CEILING.
*
@@ -100,7 +225,7 @@ class MathTrig
* @param float $number the number you want to round
* @param float $significance the multiple to which you want to round
*
- * @return float Rounded Number
+ * @return float|string Rounded Number, or a string containing an error
*/
public static function CEILING($number, $significance = null)
{
@@ -139,7 +264,7 @@ class MathTrig
* @param int $numObjs Number of different objects
* @param int $numInSet Number of objects in each combination
*
- * @return int Number of combinations
+ * @return int|string Number of combinations, or a string containing an error
*/
public static function COMBIN($numObjs, $numInSet)
{
@@ -175,7 +300,7 @@ class MathTrig
*
* @param float $number Number to round
*
- * @return int Rounded Number
+ * @return int|string Rounded Number, or a string containing an error
*/
public static function EVEN($number)
{
@@ -209,7 +334,7 @@ class MathTrig
*
* @param float $factVal Factorial Value
*
- * @return int Factorial
+ * @return int|string Factorial, or a string containing an error
*/
public static function FACT($factVal)
{
@@ -220,10 +345,9 @@ class MathTrig
return Functions::NAN();
}
$factLoop = floor($factVal);
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- if ($factVal > $factLoop) {
- return Functions::NAN();
- }
+ if ((Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) &&
+ ($factVal > $factLoop)) {
+ return Functions::NAN();
}
$factorial = 1;
@@ -249,7 +373,7 @@ class MathTrig
*
* @param float $factVal Factorial Value
*
- * @return int Double Factorial
+ * @return int|string Double Factorial, or a string containing an error
*/
public static function FACTDOUBLE($factVal)
{
@@ -285,7 +409,7 @@ class MathTrig
* @param float $number Number to round
* @param float $significance Significance
*
- * @return float Rounded Number
+ * @return float|string Rounded Number, or a string containing an error
*/
public static function FLOOR($number, $significance = null)
{
@@ -302,7 +426,9 @@ class MathTrig
return Functions::DIV0();
} elseif ($number == 0.0) {
return 0.0;
- } elseif (self::SIGN($number) == self::SIGN($significance)) {
+ } elseif (self::SIGN($significance) == 1) {
+ return floor($number / $significance) * $significance;
+ } elseif (self::SIGN($number) == -1 && self::SIGN($significance) == -1) {
return floor($number / $significance) * $significance;
}
@@ -312,6 +438,80 @@ class MathTrig
return Functions::VALUE();
}
+ /**
+ * FLOOR.MATH.
+ *
+ * Round a number down to the nearest integer or to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * FLOOR.MATH(number[,significance[,mode]])
+ *
+ * @category Mathematical and Trigonometric Functions
+ *
+ * @param float $number Number to round
+ * @param float $significance Significance
+ * @param int $mode direction to round negative numbers
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ public static function FLOORMATH($number, $significance = null, $mode = 0)
+ {
+ $number = Functions::flattenSingleValue($number);
+ $significance = Functions::flattenSingleValue($significance);
+ $mode = Functions::flattenSingleValue($mode);
+
+ if (is_numeric($number) && $significance === null) {
+ $significance = $number / abs($number);
+ }
+
+ if (is_numeric($number) && is_numeric($significance) && is_numeric($mode)) {
+ if ($significance == 0.0) {
+ return Functions::DIV0();
+ } elseif ($number == 0.0) {
+ return 0.0;
+ } elseif (self::SIGN($significance) == -1 || (self::SIGN($number) == -1 && !empty($mode))) {
+ return ceil($number / $significance) * $significance;
+ }
+
+ return floor($number / $significance) * $significance;
+ }
+
+ return Functions::VALUE();
+ }
+
+ /**
+ * FLOOR.PRECISE.
+ *
+ * Rounds number down, toward zero, to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * FLOOR.PRECISE(number[,significance])
+ *
+ * @category Mathematical and Trigonometric Functions
+ *
+ * @param float $number Number to round
+ * @param float $significance Significance
+ *
+ * @return float|string Rounded Number, or a string containing an error
+ */
+ public static function FLOORPRECISE($number, $significance = 1)
+ {
+ $number = Functions::flattenSingleValue($number);
+ $significance = Functions::flattenSingleValue($significance);
+
+ if ((is_numeric($number)) && (is_numeric($significance))) {
+ if ($significance == 0.0) {
+ return Functions::DIV0();
+ } elseif ($number == 0.0) {
+ return 0.0;
+ }
+
+ return floor($number / abs($significance)) * abs($significance);
+ }
+
+ return Functions::VALUE();
+ }
+
private static function evaluateGCD($a, $b)
{
return $b ? self::evaluateGCD($b, $a % $b) : $a;
@@ -331,7 +531,7 @@ class MathTrig
*
* @param mixed ...$args Data values
*
- * @return int Greatest Common Divisor
+ * @return int|mixed|string Greatest Common Divisor, or a string containing an error
*/
public static function GCD(...$args)
{
@@ -365,7 +565,7 @@ class MathTrig
*
* @param float $number Number to cast to an integer
*
- * @return int Integer value
+ * @return int|string Integer value, or a string containing an error
*/
public static function INT($number)
{
@@ -398,7 +598,7 @@ class MathTrig
*
* @param mixed ...$args Data values
*
- * @return int Lowest Common Multiplier
+ * @return int|string Lowest Common Multiplier, or a string containing an error
*/
public static function LCM(...$args)
{
@@ -450,7 +650,7 @@ class MathTrig
* @param float $number The positive real number for which you want the logarithm
* @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10.
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function logBase($number = null, $base = 10)
{
@@ -479,7 +679,7 @@ class MathTrig
*
* @param array $matrixValues A matrix of values
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function MDETERM($matrixValues)
{
@@ -531,7 +731,7 @@ class MathTrig
*
* @param array $matrixValues A matrix of values
*
- * @return array
+ * @return array|string The result, or a string containing an error
*/
public static function MINVERSE($matrixValues)
{
@@ -581,7 +781,7 @@ class MathTrig
* @param array $matrixData1 A matrix of values
* @param array $matrixData2 A matrix of values
*
- * @return array
+ * @return array|string The result, or a string containing an error
*/
public static function MMULT($matrixData1, $matrixData2)
{
@@ -643,7 +843,7 @@ class MathTrig
* @param int $a Dividend
* @param int $b Divisor
*
- * @return int Remainder
+ * @return int|string Remainder, or a string containing an error
*/
public static function MOD($a = 1, $b = 1)
{
@@ -669,7 +869,7 @@ class MathTrig
* @param float $number Number to round
* @param int $multiple Multiple to which you want to round $number
*
- * @return float Rounded Number
+ * @return float|string Rounded Number, or a string containing an error
*/
public static function MROUND($number, $multiple)
{
@@ -699,7 +899,7 @@ class MathTrig
*
* @param array of mixed Data Series
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function MULTINOMIAL(...$args)
{
@@ -736,7 +936,7 @@ class MathTrig
*
* @param float $number Number to round
*
- * @return int Rounded Number
+ * @return int|string Rounded Number, or a string containing an error
*/
public static function ODD($number)
{
@@ -771,7 +971,7 @@ class MathTrig
* @param float $x
* @param float $y
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function POWER($x = 0, $y = 2)
{
@@ -930,7 +1130,7 @@ class MathTrig
* @param float $number Number to round
* @param int $digits Number of digits to which you want to round $number
*
- * @return float Rounded Number
+ * @return float|string Rounded Number, or a string containing an error
*/
public static function ROUNDUP($number, $digits)
{
@@ -938,12 +1138,11 @@ class MathTrig
$digits = Functions::flattenSingleValue($digits);
if ((is_numeric($number)) && (is_numeric($digits))) {
- $significance = pow(10, (int) $digits);
if ($number < 0.0) {
- return floor($number * $significance) / $significance;
+ return round($number - 0.5 * pow(0.1, $digits), $digits, PHP_ROUND_HALF_DOWN);
}
- return ceil($number * $significance) / $significance;
+ return round($number + 0.5 * pow(0.1, $digits), $digits, PHP_ROUND_HALF_DOWN);
}
return Functions::VALUE();
@@ -957,7 +1156,7 @@ class MathTrig
* @param float $number Number to round
* @param int $digits Number of digits to which you want to round $number
*
- * @return float Rounded Number
+ * @return float|string Rounded Number, or a string containing an error
*/
public static function ROUNDDOWN($number, $digits)
{
@@ -965,12 +1164,11 @@ class MathTrig
$digits = Functions::flattenSingleValue($digits);
if ((is_numeric($number)) && (is_numeric($digits))) {
- $significance = pow(10, (int) $digits);
if ($number < 0.0) {
- return ceil($number * $significance) / $significance;
+ return round($number + 0.5 * pow(0.1, $digits), $digits, PHP_ROUND_HALF_UP);
}
- return floor($number * $significance) / $significance;
+ return round($number - 0.5 * pow(0.1, $digits), $digits, PHP_ROUND_HALF_UP);
}
return Functions::VALUE();
@@ -986,7 +1184,7 @@ class MathTrig
* @param float $m Step by which to increase $n for each term in the series
* @param array of mixed Data Series
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function SERIESSUM(...$args)
{
@@ -1025,7 +1223,7 @@ class MathTrig
*
* @param float $number Number to round
*
- * @return int sign value
+ * @return int|string sign value, or a string containing an error
*/
public static function SIGN($number)
{
@@ -1052,7 +1250,7 @@ class MathTrig
*
* @param float $number Number
*
- * @return float Square Root of Number * Pi
+ * @return float|string Square Root of Number * Pi, or a string containing an error
*/
public static function SQRTPI($number)
{
@@ -1074,7 +1272,7 @@ class MathTrig
return array_filter(
$args,
function ($index) use ($cellReference) {
- list(, $row, $column) = explode('.', $index);
+ [, $row, $column] = explode('.', $index);
return $cellReference->getWorksheet()->getRowDimension($row)->getVisible() &&
$cellReference->getWorksheet()->getColumnDimension($column)->getVisible();
@@ -1088,7 +1286,7 @@ class MathTrig
return array_filter(
$args,
function ($index) use ($cellReference) {
- list(, $row, $column) = explode('.', $index);
+ [, $row, $column] = explode('.', $index);
if ($cellReference->getWorksheet()->cellExists($column . $row)) {
//take this cell out if it contains the SUBTOTAL or AGGREGATE functions in a formula
$isFormula = $cellReference->getWorksheet()->getCell($column . $row)->isFormula();
@@ -1116,7 +1314,7 @@ class MathTrig
* in hidden rows or columns
* @param array of mixed Data Series
*
- * @return float
+ * @return float|string
*/
public static function SUBTOTAL(...$args)
{
@@ -1306,7 +1504,7 @@ class MathTrig
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function SUMPRODUCT(...$args)
{
@@ -1451,7 +1649,7 @@ class MathTrig
* @param float $value
* @param int $digits
*
- * @return float Truncated value
+ * @return float|string Truncated value, or a string containing an error
*/
public static function TRUNC($value = 0, $digits = 0)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php
index 395b46ae7ca..b1c7fb020ef 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php
@@ -518,6 +518,44 @@ class Statistical
return Functions::NULL();
}
+ /**
+ * MS Excel does not count Booleans if passed as cell values, but they are counted if passed as literals.
+ * OpenOffice Calc always counts Booleans.
+ * Gnumeric never counts Booleans.
+ *
+ * @param mixed $arg
+ * @param mixed $k
+ *
+ * @return int|mixed
+ */
+ private static function testAcceptedBoolean($arg, $k)
+ {
+ if ((is_bool($arg)) &&
+ ((!Functions::isCellValue($k) && (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_EXCEL)) ||
+ (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE))) {
+ $arg = (int) $arg;
+ }
+
+ return $arg;
+ }
+
+ /**
+ * @param mixed $arg
+ * @param mixed $k
+ *
+ * @return bool
+ */
+ private static function isAcceptedCountable($arg, $k)
+ {
+ if (((is_numeric($arg)) && (!is_string($arg))) ||
+ ((is_numeric($arg)) && (!Functions::isCellValue($k)) &&
+ (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_GNUMERIC))) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
* AVEDEV.
*
@@ -531,43 +569,43 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function AVEDEV(...$args)
{
$aArgs = Functions::flattenArrayIndexed($args);
// Return value
- $returnValue = null;
+ $returnValue = 0;
- $aMean = self::AVERAGE($aArgs);
- if ($aMean != Functions::DIV0()) {
- $aCount = 0;
- foreach ($aArgs as $k => $arg) {
- if ((is_bool($arg)) &&
- ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
- $arg = (int) $arg;
- }
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = abs($arg - $aMean);
- } else {
- $returnValue += abs($arg - $aMean);
- }
- ++$aCount;
- }
- }
-
- // Return
- if ($aCount == 0) {
- return Functions::DIV0();
- }
-
- return $returnValue / $aCount;
+ $aMean = self::AVERAGE(...$args);
+ if ($aMean === Functions::DIV0()) {
+ return Functions::NAN();
+ } elseif ($aMean === Functions::VALUE()) {
+ return Functions::VALUE();
}
- return Functions::NAN();
+ $aCount = 0;
+ foreach ($aArgs as $k => $arg) {
+ $arg = self::testAcceptedBoolean($arg, $k);
+ // Is it a numeric value?
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
+ return Functions::VALUE();
+ }
+ if (self::isAcceptedCountable($arg, $k)) {
+ $returnValue += abs($arg - $aMean);
+ ++$aCount;
+ }
+ }
+
+ // Return
+ if ($aCount === 0) {
+ return Functions::DIV0();
+ }
+
+ return $returnValue / $aCount;
}
/**
@@ -582,7 +620,7 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function AVERAGE(...$args)
{
@@ -590,17 +628,15 @@ class Statistical
// Loop through arguments
foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
- if ((is_bool($arg)) &&
- ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
- $arg = (int) $arg;
- }
+ $arg = self::testAcceptedBoolean($arg, $k);
// Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = $arg;
- } else {
- $returnValue += $arg;
- }
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
+ return Functions::VALUE();
+ }
+ if (self::isAcceptedCountable($arg, $k)) {
+ $returnValue += $arg;
++$aCount;
}
}
@@ -625,7 +661,7 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function AVERAGEA(...$args)
{
@@ -643,11 +679,7 @@ class Statistical
} elseif (is_string($arg)) {
$arg = 0;
}
- if ($returnValue === null) {
- $returnValue = $arg;
- } else {
- $returnValue += $arg;
- }
+ $returnValue += $arg;
++$aCount;
}
}
@@ -674,7 +706,7 @@ class Statistical
* @param string $condition the criteria that defines which cells will be checked
* @param mixed[] $averageArgs Data values
*
- * @return float
+ * @return float|string
*/
public static function AVERAGEIF($aArgs, $condition, $averageArgs = [])
{
@@ -686,18 +718,23 @@ class Statistical
$averageArgs = $aArgs;
}
$condition = Functions::ifCondition($condition);
+ $conditionIsNumeric = strpos($condition, '"') === false;
+
// Loop through arguments
$aCount = 0;
foreach ($aArgs as $key => $arg) {
if (!is_numeric($arg)) {
+ if ($conditionIsNumeric) {
+ continue;
+ }
$arg = Calculation::wrapResult(strtoupper($arg));
+ } elseif (!$conditionIsNumeric) {
+ continue;
}
$testCondition = '=' . $arg . $condition;
if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- if (($returnValue === null) || ($arg > $returnValue)) {
- $returnValue += $arg;
- ++$aCount;
- }
+ $returnValue += $averageArgs[$key];
+ ++$aCount;
}
}
@@ -719,7 +756,7 @@ class Statistical
* @param mixed $rMin
* @param mixed $rMax
*
- * @return float
+ * @return float|string
*/
public static function BETADIST($value, $alpha, $beta, $rMin = 0, $rMax = 1)
{
@@ -758,7 +795,7 @@ class Statistical
* @param float $rMin Minimum value
* @param float $rMax Maximum value
*
- * @return float
+ * @return float|string
*/
public static function BETAINV($probability, $alpha, $beta, $rMin = 0, $rMax = 1)
{
@@ -816,17 +853,17 @@ class Statistical
* @param float $probability Probability of success on each trial
* @param bool $cumulative
*
- * @return float
- *
- * @todo Cumulative distribution function
+ * @return float|string
*/
public static function BINOMDIST($value, $trials, $probability, $cumulative)
{
- $value = floor(Functions::flattenSingleValue($value));
- $trials = floor(Functions::flattenSingleValue($trials));
+ $value = Functions::flattenSingleValue($value);
+ $trials = Functions::flattenSingleValue($trials);
$probability = Functions::flattenSingleValue($probability);
if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
+ $value = floor($value);
+ $trials = floor($trials);
if (($value < 0) || ($value > $trials)) {
return Functions::NAN();
}
@@ -858,14 +895,15 @@ class Statistical
* @param float $value Value for the function
* @param float $degrees degrees of freedom
*
- * @return float
+ * @return float|string
*/
public static function CHIDIST($value, $degrees)
{
$value = Functions::flattenSingleValue($value);
- $degrees = floor(Functions::flattenSingleValue($degrees));
+ $degrees = Functions::flattenSingleValue($degrees);
if ((is_numeric($value)) && (is_numeric($degrees))) {
+ $degrees = floor($degrees);
if ($degrees < 1) {
return Functions::NAN();
}
@@ -891,14 +929,16 @@ class Statistical
* @param float $probability Probability for the function
* @param float $degrees degrees of freedom
*
- * @return float
+ * @return float|string
*/
public static function CHIINV($probability, $degrees)
{
$probability = Functions::flattenSingleValue($probability);
- $degrees = floor(Functions::flattenSingleValue($degrees));
+ $degrees = Functions::flattenSingleValue($degrees);
if ((is_numeric($probability)) && (is_numeric($degrees))) {
+ $degrees = floor($degrees);
+
$xLo = 100;
$xHi = 0;
@@ -908,7 +948,7 @@ class Statistical
while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
// Apply Newton-Raphson step
- $result = self::CHIDIST($x, $degrees);
+ $result = 1 - (self::incompleteGamma($degrees / 2, $x / 2) / self::gamma($degrees / 2));
$error = $result - $probability;
if ($error == 0.0) {
$dx = 0;
@@ -950,15 +990,16 @@ class Statistical
* @param float $stdDev Standard Deviation
* @param float $size
*
- * @return float
+ * @return float|string
*/
public static function CONFIDENCE($alpha, $stdDev, $size)
{
$alpha = Functions::flattenSingleValue($alpha);
$stdDev = Functions::flattenSingleValue($stdDev);
- $size = floor(Functions::flattenSingleValue($size));
+ $size = Functions::flattenSingleValue($size);
if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
+ $size = floor($size);
if (($alpha <= 0) || ($alpha >= 1)) {
return Functions::NAN();
}
@@ -980,7 +1021,7 @@ class Statistical
* @param mixed $yValues array of mixed Data Series Y
* @param null|mixed $xValues array of mixed Data Series X
*
- * @return float
+ * @return float|string
*/
public static function CORREL($yValues, $xValues = null)
{
@@ -1025,12 +1066,11 @@ class Statistical
// Loop through arguments
$aArgs = Functions::flattenArrayIndexed($args);
foreach ($aArgs as $k => $arg) {
- if ((is_bool($arg)) &&
- ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
- $arg = (int) $arg;
- }
+ $arg = self::testAcceptedBoolean($arg, $k);
// Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
+ // Strings containing numeric values are only counted if they are string literals (not cell values)
+ // and then only in MS Excel and in Open Office, not in Gnumeric
+ if (self::isAcceptedCountable($arg, $k)) {
++$returnValue;
}
}
@@ -1057,10 +1097,10 @@ class Statistical
$returnValue = 0;
// Loop through arguments
- $aArgs = Functions::flattenArray($args);
- foreach ($aArgs as $arg) {
- // Is it a numeric, boolean or string value?
- if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
+ $aArgs = Functions::flattenArrayIndexed($args);
+ foreach ($aArgs as $k => $arg) {
+ // Nulls are counted if literals, but not if cell values
+ if ($arg !== null || (!Functions::isCellValue($k))) {
++$returnValue;
}
}
@@ -1119,10 +1159,16 @@ class Statistical
$aArgs = Functions::flattenArray($aArgs);
$condition = Functions::ifCondition($condition);
+ $conditionIsNumeric = strpos($condition, '"') === false;
// Loop through arguments
foreach ($aArgs as $arg) {
if (!is_numeric($arg)) {
+ if ($conditionIsNumeric) {
+ continue;
+ }
$arg = Calculation::wrapResult(strtoupper($arg));
+ } elseif (!$conditionIsNumeric) {
+ continue;
}
$testCondition = '=' . $arg . $condition;
if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
@@ -1134,6 +1180,78 @@ class Statistical
return $returnValue;
}
+ /**
+ * COUNTIFS.
+ *
+ * Counts the number of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * COUNTIFS(criteria_range1, criteria1, [criteria_range2, criteria2]…)
+ *
+ * @category Statistical Functions
+ *
+ * @param mixed $args Criterias
+ *
+ * @return int
+ */
+ public static function COUNTIFS(...$args)
+ {
+ $arrayList = $args;
+
+ // Return value
+ $returnValue = 0;
+
+ if (empty($arrayList)) {
+ return $returnValue;
+ }
+
+ $aArgsArray = [];
+ $conditions = [];
+
+ while (count($arrayList) > 0) {
+ $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
+ $conditions[] = Functions::ifCondition(array_shift($arrayList));
+ }
+
+ // Loop through each arg and see if arguments and conditions are true
+ foreach (array_keys($aArgsArray[0]) as $index) {
+ $valid = true;
+
+ foreach ($conditions as $cidx => $condition) {
+ $conditionIsNumeric = strpos($condition, '"') === false;
+ $arg = $aArgsArray[$cidx][$index];
+
+ // Loop through arguments
+ if (!is_numeric($arg)) {
+ if ($conditionIsNumeric) {
+ $valid = false;
+
+ break; // if false found, don't need to check other conditions
+ }
+ $arg = Calculation::wrapResult(strtoupper($arg));
+ } elseif (!$conditionIsNumeric) {
+ $valid = false;
+
+ break; // if false found, don't need to check other conditions
+ }
+ $testCondition = '=' . $arg . $condition;
+ if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ // Is not a value within our criteria
+ $valid = false;
+
+ break; // if false found, don't need to check other conditions
+ }
+ }
+
+ if ($valid) {
+ ++$returnValue;
+ }
+ }
+
+ // Return
+ return $returnValue;
+ }
+
/**
* COVAR.
*
@@ -1142,7 +1260,7 @@ class Statistical
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues array of mixed Data Series X
*
- * @return float
+ * @return float|string
*/
public static function COVAR($yValues, $xValues)
{
@@ -1175,7 +1293,7 @@ class Statistical
* @param float $probability probability of a success on each trial
* @param float $alpha criterion value
*
- * @return int
+ * @return int|string
*
* @todo Warning. This implementation differs from the algorithm detailed on the MS
* web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
@@ -1189,19 +1307,23 @@ class Statistical
$alpha = Functions::flattenSingleValue($alpha);
if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
+ $trials = (int) $trials;
if ($trials < 0) {
return Functions::NAN();
- } elseif (($probability < 0) || ($probability > 1)) {
+ } elseif (($probability < 0.0) || ($probability > 1.0)) {
return Functions::NAN();
- } elseif (($alpha < 0) || ($alpha > 1)) {
+ } elseif (($alpha < 0.0) || ($alpha > 1.0)) {
return Functions::NAN();
- } elseif ($alpha <= 0.5) {
+ }
+
+ if ($alpha <= 0.5) {
$t = sqrt(log(1 / ($alpha * $alpha)));
$trialsApprox = 0 - ($t + (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t));
} else {
$t = sqrt(log(1 / pow(1 - $alpha, 2)));
$trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t);
}
+
$Guess = floor($trials * $probability + $trialsApprox * sqrt($trials * $probability * (1 - $probability)));
if ($Guess < 0) {
$Guess = 0;
@@ -1298,7 +1420,7 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function DEVSQ(...$args)
{
@@ -1349,7 +1471,7 @@ class Statistical
* @param float $lambda The parameter value
* @param bool $cumulative
*
- * @return float
+ * @return float|string
*/
public static function EXPONDIST($value, $lambda, $cumulative)
{
@@ -1382,7 +1504,7 @@ class Statistical
*
* @param float $value
*
- * @return float
+ * @return float|string
*/
public static function FISHER($value)
{
@@ -1408,7 +1530,7 @@ class Statistical
*
* @param float $value
*
- * @return float
+ * @return float|string
*/
public static function FISHERINV($value)
{
@@ -1430,7 +1552,7 @@ class Statistical
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues of mixed Data Series X
*
- * @return float
+ * @return bool|float|string
*/
public static function FORECAST($xValue, $yValues, $xValues)
{
@@ -1464,7 +1586,7 @@ class Statistical
* @param float $b Parameter to the distribution
* @param bool $cumulative
*
- * @return float
+ * @return float|string
*/
public static function GAMMADIST($value, $a, $b, $cumulative)
{
@@ -1497,7 +1619,7 @@ class Statistical
* @param float $alpha Parameter to the distribution
* @param float $beta Parameter to the distribution
*
- * @return float
+ * @return float|string
*/
public static function GAMMAINV($probability, $alpha, $beta)
{
@@ -1558,7 +1680,7 @@ class Statistical
*
* @param float $value
*
- * @return float
+ * @return float|string
*/
public static function GAMMALN($value)
{
@@ -1589,7 +1711,7 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function GEOMEAN(...$args)
{
@@ -1609,7 +1731,7 @@ class Statistical
/**
* GROWTH.
*
- * Returns values along a predicted emponential Trend
+ * Returns values along a predicted exponential Trend
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
@@ -1651,12 +1773,12 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function HARMEAN(...$args)
{
// Return value
- $returnValue = Functions::NA();
+ $returnValue = 0;
// Loop through arguments
$aArgs = Functions::flattenArray($args);
@@ -1670,11 +1792,7 @@ class Statistical
if ($arg <= 0) {
return Functions::NAN();
}
- if ($returnValue === null) {
- $returnValue = (1 / $arg);
- } else {
- $returnValue += (1 / $arg);
- }
+ $returnValue += (1 / $arg);
++$aCount;
}
}
@@ -1684,7 +1802,7 @@ class Statistical
return 1 / ($returnValue / $aCount);
}
- return $returnValue;
+ return Functions::NA();
}
/**
@@ -1698,7 +1816,7 @@ class Statistical
* @param float $populationSuccesses Number of successes in the population
* @param float $populationNumber Population size
*
- * @return float
+ * @return float|string
*/
public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber)
{
@@ -1734,7 +1852,7 @@ class Statistical
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
- * @return float
+ * @return float|string
*/
public static function INTERCEPT($yValues, $xValues)
{
@@ -1765,7 +1883,7 @@ class Statistical
*
* @param array ...$args Data Series
*
- * @return float
+ * @return float|string
*/
public static function KURT(...$args)
{
@@ -1811,7 +1929,7 @@ class Statistical
* @param mixed $args Data values
* @param int $entry Position (ordered from the largest) in the array or range of data to return
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function LARGE(...$args)
{
@@ -1852,7 +1970,7 @@ class Statistical
* @param bool $const a logical value specifying whether to force the intersect to equal 0
* @param bool $stats a logical value specifying whether to return additional regression statistics
*
- * @return array
+ * @return array|int|string The result, or a string containing an error
*/
public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
{
@@ -1911,7 +2029,7 @@ class Statistical
* @param bool $const a logical value specifying whether to force the intersect to equal 0
* @param bool $stats a logical value specifying whether to return additional regression statistics
*
- * @return array
+ * @return array|int|string The result, or a string containing an error
*/
public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
{
@@ -1974,7 +2092,7 @@ class Statistical
* @param float $mean
* @param float $stdDev
*
- * @return float
+ * @return float|string The result, or a string containing an error
*
* @todo Try implementing P J Acklam's refinement algorithm for greater
* accuracy if I can get my head round the mathematics
@@ -2007,7 +2125,7 @@ class Statistical
* @param float $mean
* @param float $stdDev
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function LOGNORMDIST($value, $mean, $stdDev)
{
@@ -2105,44 +2223,61 @@ class Statistical
}
/**
- * MAXIF.
+ * MAXIFS.
*
* Counts the maximum value within a range of cells that contain numbers within the list of arguments
*
* Excel Function:
- * MAXIF(value1[,value2[, ...]],condition)
+ * MAXIFS(max_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
*
- * @category Mathematical and Trigonometric Functions
+ * @category Statistical Functions
*
- * @param mixed $aArgs Data values
- * @param string $condition the criteria that defines which cells will be checked
- * @param mixed $sumArgs
+ * @param mixed $args Data range and criterias
*
* @return float
*/
- public static function MAXIF($aArgs, $condition, $sumArgs = [])
+ public static function MAXIFS(...$args)
{
+ $arrayList = $args;
+
+ // Return value
$returnValue = null;
- $aArgs = Functions::flattenArray($aArgs);
- $sumArgs = Functions::flattenArray($sumArgs);
- if (empty($sumArgs)) {
- $sumArgs = $aArgs;
+ $maxArgs = Functions::flattenArray(array_shift($arrayList));
+ $aArgsArray = [];
+ $conditions = [];
+
+ while (count($arrayList) > 0) {
+ $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
+ $conditions[] = Functions::ifCondition(array_shift($arrayList));
}
- $condition = Functions::ifCondition($condition);
- // Loop through arguments
- foreach ($aArgs as $key => $arg) {
- if (!is_numeric($arg)) {
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
- $testCondition = '=' . $arg . $condition;
- if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- if (($returnValue === null) || ($arg > $returnValue)) {
- $returnValue = $arg;
+
+ // Loop through each arg and see if arguments and conditions are true
+ foreach ($maxArgs as $index => $value) {
+ $valid = true;
+
+ foreach ($conditions as $cidx => $condition) {
+ $arg = $aArgsArray[$cidx][$index];
+
+ // Loop through arguments
+ if (!is_numeric($arg)) {
+ $arg = Calculation::wrapResult(strtoupper($arg));
}
+ $testCondition = '=' . $arg . $condition;
+ if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ // Is not a value within our criteria
+ $valid = false;
+
+ break; // if false found, don't need to check other conditions
+ }
+ }
+
+ if ($valid) {
+ $returnValue = $returnValue === null ? $value : max($value, $returnValue);
}
}
+ // Return
return $returnValue;
}
@@ -2158,7 +2293,7 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function MEDIAN(...$args)
{
@@ -2268,44 +2403,61 @@ class Statistical
}
/**
- * MINIF.
+ * MINIFS.
*
* Returns the minimum value within a range of cells that contain numbers within the list of arguments
*
* Excel Function:
- * MINIF(value1[,value2[, ...]],condition)
+ * MINIFS(min_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
*
- * @category Mathematical and Trigonometric Functions
+ * @category Statistical Functions
*
- * @param mixed $aArgs Data values
- * @param string $condition the criteria that defines which cells will be checked
- * @param mixed $sumArgs
+ * @param mixed $args Data range and criterias
*
* @return float
*/
- public static function MINIF($aArgs, $condition, $sumArgs = [])
+ public static function MINIFS(...$args)
{
+ $arrayList = $args;
+
+ // Return value
$returnValue = null;
- $aArgs = Functions::flattenArray($aArgs);
- $sumArgs = Functions::flattenArray($sumArgs);
- if (empty($sumArgs)) {
- $sumArgs = $aArgs;
+ $minArgs = Functions::flattenArray(array_shift($arrayList));
+ $aArgsArray = [];
+ $conditions = [];
+
+ while (count($arrayList) > 0) {
+ $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
+ $conditions[] = Functions::ifCondition(array_shift($arrayList));
}
- $condition = Functions::ifCondition($condition);
- // Loop through arguments
- foreach ($aArgs as $key => $arg) {
- if (!is_numeric($arg)) {
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
- $testCondition = '=' . $arg . $condition;
- if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- if (($returnValue === null) || ($arg < $returnValue)) {
- $returnValue = $arg;
+
+ // Loop through each arg and see if arguments and conditions are true
+ foreach ($minArgs as $index => $value) {
+ $valid = true;
+
+ foreach ($conditions as $cidx => $condition) {
+ $arg = $aArgsArray[$cidx][$index];
+
+ // Loop through arguments
+ if (!is_numeric($arg)) {
+ $arg = Calculation::wrapResult(strtoupper($arg));
}
+ $testCondition = '=' . $arg . $condition;
+ if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ // Is not a value within our criteria
+ $valid = false;
+
+ break; // if false found, don't need to check other conditions
+ }
+ }
+
+ if ($valid) {
+ $returnValue = $returnValue === null ? $value : min($value, $returnValue);
}
}
+ // Return
return $returnValue;
}
@@ -2359,7 +2511,7 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function MODE(...$args)
{
@@ -2396,7 +2548,7 @@ class Statistical
* @param float $successes Threshold number of Successes
* @param float $probability Probability of success on each trial
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function NEGBINOMDIST($failures, $successes, $probability)
{
@@ -2434,7 +2586,7 @@ class Statistical
* @param float $stdDev Standard Deviation
* @param bool $cumulative
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function NORMDIST($value, $mean, $stdDev, $cumulative)
{
@@ -2467,7 +2619,7 @@ class Statistical
* @param float $mean Mean Value
* @param float $stdDev Standard Deviation
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function NORMINV($probability, $mean, $stdDev)
{
@@ -2498,7 +2650,7 @@ class Statistical
*
* @param float $value
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function NORMSDIST($value)
{
@@ -2514,7 +2666,7 @@ class Statistical
*
* @param float $value
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function NORMSINV($value)
{
@@ -2534,7 +2686,7 @@ class Statistical
* @param mixed $args Data values
* @param float $entry Percentile value in the range 0..1, inclusive.
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function PERCENTILE(...$args)
{
@@ -2632,7 +2784,7 @@ class Statistical
* @param int $numObjs Number of different objects
* @param int $numInSet Number of objects in each permutation
*
- * @return int Number of permutations
+ * @return int|string Number of permutations, or a string containing an error
*/
public static function PERMUT($numObjs, $numInSet)
{
@@ -2662,7 +2814,7 @@ class Statistical
* @param float $mean Mean Value
* @param bool $cumulative
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function POISSON($value, $mean, $cumulative)
{
@@ -2704,7 +2856,7 @@ class Statistical
* @param mixed $args Data values
* @param int $entry Quartile value in the range 1..3, inclusive.
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function QUARTILE(...$args)
{
@@ -2734,7 +2886,7 @@ class Statistical
* @param float[] $valueSet An array of, or a reference to, a list of numbers
* @param int $order Order to sort the values in the value set
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function RANK($value, $valueSet, $order = 0)
{
@@ -2769,7 +2921,7 @@ class Statistical
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function RSQ($yValues, $xValues)
{
@@ -2800,7 +2952,7 @@ class Statistical
*
* @param array ...$args Data Series
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function SKEW(...$args)
{
@@ -2837,7 +2989,7 @@ class Statistical
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function SLOPE($yValues, $xValues)
{
@@ -2872,7 +3024,7 @@ class Statistical
* @param mixed $args Data values
* @param int $entry Position (ordered from the smallest) in the array or range of data to return
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function SMALL(...$args)
{
@@ -2911,7 +3063,7 @@ class Statistical
* @param float $mean Mean Value
* @param float $stdDev Standard Deviation
*
- * @return float Standardized value
+ * @return float|string Standardized value, or a string containing an error
*/
public static function STANDARDIZE($value, $mean, $stdDev)
{
@@ -2943,7 +3095,7 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function STDEV(...$args)
{
@@ -2992,7 +3144,7 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function STDEVA(...$args)
{
@@ -3044,7 +3196,7 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function STDEVP(...$args)
{
@@ -3091,7 +3243,7 @@ class Statistical
*
* @param mixed ...$args Data values
*
- * @return float
+ * @return float|string
*/
public static function STDEVPA(...$args)
{
@@ -3139,7 +3291,7 @@ class Statistical
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
- * @return float
+ * @return float|string
*/
public static function STEYX($yValues, $xValues)
{
@@ -3169,7 +3321,7 @@ class Statistical
* @param float $degrees degrees of freedom
* @param float $tails number of tails (1 or 2)
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function TDIST($value, $degrees, $tails)
{
@@ -3233,7 +3385,7 @@ class Statistical
* @param float $probability Probability for the function
* @param float $degrees degrees of freedom
*
- * @return float
+ * @return float|string The result, or a string containing an error
*/
public static function TINV($probability, $degrees)
{
@@ -3330,7 +3482,7 @@ class Statistical
* @param mixed $args Data values
* @param float $discard Percentage to discard
*
- * @return float
+ * @return float|string
*/
public static function TRIMMEAN(...$args)
{
@@ -3603,7 +3755,7 @@ class Statistical
* @param float $m0 Alpha Parameter
* @param float $sigma Beta Parameter
*
- * @return float
+ * @return float|string
*/
public static function ZTEST($dataSet, $m0, $sigma = null)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php
index e30b2ff7e89..bbb03926ca4 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php
@@ -52,7 +52,7 @@ class TextData
return ($stringValue) ? Calculation::getTRUE() : Calculation::getFALSE();
}
- if (self::$invalidChars == null) {
+ if (self::$invalidChars === null) {
self::$invalidChars = range(chr(0), chr(31));
}
@@ -84,6 +84,15 @@ class TextData
return null;
}
+ private static function convertBooleanValue($value)
+ {
+ if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
+ return (int) $value;
+ }
+
+ return ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
+ }
+
/**
* ASCIICODE.
*
@@ -98,11 +107,7 @@ class TextData
}
$characters = Functions::flattenSingleValue($characters);
if (is_bool($characters)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $characters = (int) $characters;
- } else {
- $characters = ($characters) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
+ $characters = self::convertBooleanValue($characters);
}
$character = $characters;
@@ -126,11 +131,7 @@ class TextData
$aArgs = Functions::flattenArray($args);
foreach ($aArgs as $arg) {
if (is_bool($arg)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $arg = (int) $arg;
- } else {
- $arg = ($arg) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
+ $arg = self::convertBooleanValue($arg);
}
$returnValue .= $arg;
}
@@ -197,7 +198,7 @@ class TextData
}
if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) {
- if (StringHelper::countCharacters($needle) == 0) {
+ if (StringHelper::countCharacters($needle) === 0) {
return $offset;
}
@@ -232,7 +233,7 @@ class TextData
}
if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) {
- if (StringHelper::countCharacters($needle) == 0) {
+ if (StringHelper::countCharacters($needle) === 0) {
return $offset;
}
@@ -265,14 +266,19 @@ class TextData
if (!is_numeric($value) || !is_numeric($decimals)) {
return Functions::NAN();
}
- $decimals = floor($decimals);
+ $decimals = (int) floor($decimals);
$valueResult = round($value, $decimals);
if ($decimals < 0) {
$decimals = 0;
}
if (!$no_commas) {
- $valueResult = number_format($valueResult, $decimals);
+ $valueResult = number_format(
+ $valueResult,
+ $decimals,
+ StringHelper::getDecimalSeparator(),
+ StringHelper::getThousandsSeparator()
+ );
}
return (string) $valueResult;
@@ -659,11 +665,7 @@ class TextData
if ($ignoreEmpty && trim($arg) == '') {
unset($aArgs[$key]);
} elseif (is_bool($arg)) {
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
- $arg = (int) $arg;
- } else {
- $arg = ($arg) ? Calculation::getTRUE() : Calculation::getFALSE();
- }
+ $arg = self::convertBooleanValue($arg);
}
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php
index b8dccf95c2f..341a017979c 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php
@@ -36,14 +36,24 @@ class Stack
* @param mixed $type
* @param mixed $value
* @param mixed $reference
+ * @param null|string $storeKey will store the result under this alias
+ * @param null|string $onlyIf will only run computation if the matching
+ * store key is true
+ * @param null|string $onlyIfNot will only run computation if the matching
+ * store key is false
*/
- public function push($type, $value, $reference = null)
- {
- $this->stack[$this->count++] = [
- 'type' => $type,
- 'value' => $value,
- 'reference' => $reference,
- ];
+ public function push(
+ $type,
+ $value,
+ $reference = null,
+ $storeKey = null,
+ $onlyIf = null,
+ $onlyIfNot = null
+ ) {
+ $stackItem = $this->getStackItem($type, $value, $reference, $storeKey, $onlyIf, $onlyIfNot);
+
+ $this->stack[$this->count++] = $stackItem;
+
if ($type == 'Function') {
$localeFunction = Calculation::localeFunc($value);
if ($localeFunction != $value) {
@@ -52,6 +62,35 @@ class Stack
}
}
+ public function getStackItem(
+ $type,
+ $value,
+ $reference = null,
+ $storeKey = null,
+ $onlyIf = null,
+ $onlyIfNot = null
+ ) {
+ $stackItem = [
+ 'type' => $type,
+ 'value' => $value,
+ 'reference' => $reference,
+ ];
+
+ if (isset($storeKey)) {
+ $stackItem['storeKey'] = $storeKey;
+ }
+
+ if (isset($onlyIf)) {
+ $stackItem['onlyIf'] = $onlyIf;
+ }
+
+ if (isset($onlyIfNot)) {
+ $stackItem['onlyIfNot'] = $onlyIfNot;
+ }
+
+ return $stackItem;
+ }
+
/**
* Pop the last entry from the stack.
*
@@ -90,4 +129,21 @@ class Stack
$this->stack = [];
$this->count = 0;
}
+
+ public function __toString()
+ {
+ $str = 'Stack: ';
+ foreach ($this->stack as $index => $item) {
+ if ($index > $this->count - 1) {
+ break;
+ }
+ $value = $item['value'] ?? 'no value';
+ while (is_array($value)) {
+ $value = array_pop($value);
+ }
+ $str .= $value . ' |> ';
+ }
+
+ return $str;
+ }
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt
index 4a5cd265ba8..77fd4ee0e4f 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt
@@ -9,6 +9,7 @@ ADDRESS
AMORDEGRC
AMORLINC
AND
+ARABIC
AREAS
ASC
ASIN
@@ -22,6 +23,7 @@ AVERAGEA
AVERAGEIF
AVERAGEIFS
BAHTTEXT
+BASE
BESSELI
BESSELJ
BESSELK
@@ -137,6 +139,8 @@ FISHER
FISHERINV
FIXED
FLOOR
+FLOOR.MATH
+FLOOR.PRECISE
FORECAST
FREQUENCY
FTEST
@@ -224,6 +228,7 @@ LOWER
MATCH
MAX
MAXA
+MAXIFS
MDETERM
MDURATION
MEDIAN
@@ -231,6 +236,7 @@ MID
MIDB
MIN
MINA
+MINIFS
MINUTE
MINVERSE
MIRR
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php
index 4052c23cb97..1d28f247e70 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php
@@ -118,7 +118,7 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
// Check for time without seconds e.g. '9:45', '09:45'
if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d$/', $value)) {
// Convert value to number
- list($h, $m) = explode(':', $value);
+ [$h, $m] = explode(':', $value);
$days = $h / 24 + $m / 1440;
$cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
// Set style
@@ -131,7 +131,7 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
// Check for time with seconds '9:45:59', '09:45:59'
if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$/', $value)) {
// Convert value to number
- list($h, $m, $s) = explode(':', $value);
+ [$h, $m, $s] = explode(':', $value);
$days = $h / 24 + $m / 1440 + $s / 86400;
// Convert value to number
$cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
index 813eee4a091..e618436ed23 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
@@ -67,7 +67,7 @@ class Cell
/**
* Update the cell into the cell collection.
*
- * @return self
+ * @return $this
*/
public function updateInCollection()
{
@@ -177,7 +177,7 @@ class Cell
*
* @throws Exception
*
- * @return Cell
+ * @return $this
*/
public function setValue($pValue)
{
@@ -217,7 +217,10 @@ class Cell
break;
case DataType::TYPE_NUMERIC:
- $this->value = (float) $pValue;
+ if (is_string($pValue) && !is_numeric($pValue)) {
+ throw new Exception('Invalid numeric value for datatype Numeric');
+ }
+ $this->value = 0 + $pValue;
break;
case DataType::TYPE_FORMULA:
@@ -263,7 +266,7 @@ class Cell
// We don't yet handle array returns
if (is_array($result)) {
while (is_array($result)) {
- $result = array_pop($result);
+ $result = array_shift($result);
}
}
} catch (Exception $ex) {
@@ -511,7 +514,7 @@ class Cell
{
if ($mergeRange = $this->getMergeRange()) {
$mergeRange = Coordinate::splitRange($mergeRange);
- list($startCell) = $mergeRange[0];
+ [$startCell] = $mergeRange[0];
if ($this->getCoordinate() === $startCell) {
return true;
}
@@ -569,7 +572,7 @@ class Cell
*/
public function isInRange($pRange)
{
- list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($pRange);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange);
// Translate properties
$myColumn = Coordinate::columnIndexFromString($this->getColumn());
@@ -669,7 +672,7 @@ class Cell
*
* @param mixed $pAttributes
*
- * @return Cell
+ * @return $this
*/
public function setFormulaAttributes($pAttributes)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php
index 5e20ec6d9dc..cc0543f6ab4 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php
@@ -71,7 +71,7 @@ abstract class Coordinate
}
// Split out any worksheet name from the reference
- list($worksheet, $pCoordinateString) = Worksheet::extractSheetTitle($pCoordinateString, true);
+ [$worksheet, $pCoordinateString] = Worksheet::extractSheetTitle($pCoordinateString, true);
if ($worksheet > '') {
$worksheet .= '!';
}
@@ -102,13 +102,13 @@ abstract class Coordinate
}
// Split out any worksheet name from the coordinate
- list($worksheet, $pCoordinateString) = Worksheet::extractSheetTitle($pCoordinateString, true);
+ [$worksheet, $pCoordinateString] = Worksheet::extractSheetTitle($pCoordinateString, true);
if ($worksheet > '') {
$worksheet .= '!';
}
// Create absolute coordinate
- list($column, $row) = self::coordinateFromString($pCoordinateString);
+ [$column, $row] = self::coordinateFromString($pCoordinateString);
$column = ltrim($column, '$');
$row = ltrim($row, '$');
@@ -187,7 +187,7 @@ abstract class Coordinate
if (strpos($pRange, ':') === false) {
$rangeA = $rangeB = $pRange;
} else {
- list($rangeA, $rangeB) = explode(':', $pRange);
+ [$rangeA, $rangeB] = explode(':', $pRange);
}
// Calculate range outer borders
@@ -211,7 +211,7 @@ abstract class Coordinate
public static function rangeDimension($pRange)
{
// Calculate range outer borders
- list($rangeStart, $rangeEnd) = self::rangeBoundaries($pRange);
+ [$rangeStart, $rangeEnd] = self::rangeBoundaries($pRange);
return [($rangeEnd[0] - $rangeStart[0] + 1), ($rangeEnd[1] - $rangeStart[1] + 1)];
}
@@ -238,7 +238,7 @@ abstract class Coordinate
if (strpos($pRange, ':') === false) {
$rangeA = $rangeB = $pRange;
} else {
- list($rangeA, $rangeB) = explode(':', $pRange);
+ [$rangeA, $rangeB] = explode(':', $pRange);
}
return [self::coordinateFromString($rangeA), self::coordinateFromString($rangeB)];
@@ -376,9 +376,9 @@ abstract class Coordinate
}
// Range...
- list($rangeStart, $rangeEnd) = $range;
- list($startColumn, $startRow) = self::coordinateFromString($rangeStart);
- list($endColumn, $endRow) = self::coordinateFromString($rangeEnd);
+ [$rangeStart, $rangeEnd] = $range;
+ [$startColumn, $startRow] = self::coordinateFromString($rangeStart);
+ [$endColumn, $endRow] = self::coordinateFromString($rangeEnd);
$startColumnIndex = self::columnIndexFromString($startColumn);
$endColumnIndex = self::columnIndexFromString($endColumn);
++$endColumnIndex;
@@ -432,7 +432,7 @@ abstract class Coordinate
continue;
}
- list($column, $row) = self::coordinateFromString($coord);
+ [$column, $row] = self::coordinateFromString($coord);
$row = (int) (ltrim($row, '$'));
$hashCode = $column . '-' . (is_object($value) ? $value->getHashCode() : $value);
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php
index a041ea0e0ab..dfeb024c928 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php
@@ -142,7 +142,7 @@ class DataValidation
*
* @param string $value
*
- * @return DataValidation
+ * @return $this
*/
public function setFormula1($value)
{
@@ -166,7 +166,7 @@ class DataValidation
*
* @param string $value
*
- * @return DataValidation
+ * @return $this
*/
public function setFormula2($value)
{
@@ -190,7 +190,7 @@ class DataValidation
*
* @param string $value
*
- * @return DataValidation
+ * @return $this
*/
public function setType($value)
{
@@ -214,7 +214,7 @@ class DataValidation
*
* @param string $value see self::STYLE_*
*
- * @return DataValidation
+ * @return $this
*/
public function setErrorStyle($value)
{
@@ -238,7 +238,7 @@ class DataValidation
*
* @param string $value
*
- * @return DataValidation
+ * @return $this
*/
public function setOperator($value)
{
@@ -262,7 +262,7 @@ class DataValidation
*
* @param bool $value
*
- * @return DataValidation
+ * @return $this
*/
public function setAllowBlank($value)
{
@@ -286,7 +286,7 @@ class DataValidation
*
* @param bool $value
*
- * @return DataValidation
+ * @return $this
*/
public function setShowDropDown($value)
{
@@ -310,7 +310,7 @@ class DataValidation
*
* @param bool $value
*
- * @return DataValidation
+ * @return $this
*/
public function setShowInputMessage($value)
{
@@ -334,7 +334,7 @@ class DataValidation
*
* @param bool $value
*
- * @return DataValidation
+ * @return $this
*/
public function setShowErrorMessage($value)
{
@@ -358,7 +358,7 @@ class DataValidation
*
* @param string $value
*
- * @return DataValidation
+ * @return $this
*/
public function setErrorTitle($value)
{
@@ -382,7 +382,7 @@ class DataValidation
*
* @param string $value
*
- * @return DataValidation
+ * @return $this
*/
public function setError($value)
{
@@ -406,7 +406,7 @@ class DataValidation
*
* @param string $value
*
- * @return DataValidation
+ * @return $this
*/
public function setPromptTitle($value)
{
@@ -430,7 +430,7 @@ class DataValidation
*
* @param string $value
*
- * @return DataValidation
+ * @return $this
*/
public function setPrompt($value)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
index 0e6433a4ec8..cd05cf8bdf9 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
@@ -51,16 +51,16 @@ class DefaultValueBinder implements IValueBinder
// Match the value against a few data types
if ($pValue === null) {
return DataType::TYPE_NULL;
+ } elseif (is_float($pValue) || is_int($pValue)) {
+ return DataType::TYPE_NUMERIC;
+ } elseif (is_bool($pValue)) {
+ return DataType::TYPE_BOOL;
} elseif ($pValue === '') {
return DataType::TYPE_STRING;
} elseif ($pValue instanceof RichText) {
return DataType::TYPE_INLINE;
- } elseif ($pValue[0] === '=' && strlen($pValue) > 1) {
+ } elseif (is_string($pValue) && $pValue[0] === '=' && strlen($pValue) > 1) {
return DataType::TYPE_FORMULA;
- } elseif (is_bool($pValue)) {
- return DataType::TYPE_BOOL;
- } elseif (is_float($pValue) || is_int($pValue)) {
- return DataType::TYPE_NUMERIC;
} elseif (preg_match('/^[\+\-]?(\d+\\.?\d*|\d*\\.?\d+)([Ee][\-\+]?[0-2]?\d{1,3})?$/', $pValue)) {
$tValue = ltrim($pValue, '+-');
if (is_string($pValue) && $tValue[0] === '0' && strlen($tValue) > 1 && $tValue[1] !== '.') {
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php
index e17c20d9be0..003d51014d9 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php
@@ -46,7 +46,7 @@ class Hyperlink
*
* @param string $value
*
- * @return Hyperlink
+ * @return $this
*/
public function setUrl($value)
{
@@ -70,7 +70,7 @@ class Hyperlink
*
* @param string $value
*
- * @return Hyperlink
+ * @return $this
*/
public function setTooltip($value)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php
index 3d1dd22262d..66242e3409c 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php
@@ -354,7 +354,7 @@ class Axis extends Properties
*
* @param int $shadow_presets
*
- * @return Axis
+ * @return $this
*/
private function setShadowPresetsProperties($shadow_presets)
{
@@ -370,7 +370,7 @@ class Axis extends Properties
* @param array $properties_map
* @param mixed &$reference
*
- * @return Axis
+ * @return $this
*/
private function setShadowProperiesMapValues(array $properties_map, &$reference = null)
{
@@ -402,7 +402,7 @@ class Axis extends Properties
* @param int $alpha
* @param string $type
*
- * @return Axis
+ * @return $this
*/
private function setShadowColor($color, $alpha, $type)
{
@@ -416,7 +416,7 @@ class Axis extends Properties
*
* @param float $blur
*
- * @return Axis
+ * @return $this
*/
private function setShadowBlur($blur)
{
@@ -432,7 +432,7 @@ class Axis extends Properties
*
* @param int $angle
*
- * @return Axis
+ * @return $this
*/
private function setShadowAngle($angle)
{
@@ -448,7 +448,7 @@ class Axis extends Properties
*
* @param float $distance
*
- * @return Axis
+ * @return $this
*/
private function setShadowDistance($distance)
{
@@ -506,7 +506,7 @@ class Axis extends Properties
*
* @param float $size
*
- * @return Axis
+ * @return $this
*/
private function setGlowSize($size)
{
@@ -524,7 +524,7 @@ class Axis extends Properties
* @param int $alpha
* @param string $type
*
- * @return Axis
+ * @return $this
*/
private function setGlowColor($color, $alpha, $type)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php
index 3d09a041d57..59c9ed5d67e 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php
@@ -156,7 +156,7 @@ class Chart
* @param null|GridLines $majorGridlines
* @param null|GridLines $minorGridlines
*/
- public function __construct($name, Title $title = null, Legend $legend = null, PlotArea $plotArea = null, $plotVisibleOnly = true, $displayBlanksAs = '0', Title $xAxisLabel = null, Title $yAxisLabel = null, Axis $xAxis = null, Axis $yAxis = null, GridLines $majorGridlines = null, GridLines $minorGridlines = null)
+ public function __construct($name, Title $title = null, Legend $legend = null, PlotArea $plotArea = null, $plotVisibleOnly = true, $displayBlanksAs = 'gap', Title $xAxisLabel = null, Title $yAxisLabel = null, Axis $xAxis = null, Axis $yAxis = null, GridLines $majorGridlines = null, GridLines $minorGridlines = null)
{
$this->name = $name;
$this->title = $title;
@@ -197,7 +197,7 @@ class Chart
*
* @param Worksheet $pValue
*
- * @return Chart
+ * @return $this
*/
public function setWorksheet(Worksheet $pValue = null)
{
@@ -221,7 +221,7 @@ class Chart
*
* @param Title $title
*
- * @return Chart
+ * @return $this
*/
public function setTitle(Title $title)
{
@@ -245,7 +245,7 @@ class Chart
*
* @param Legend $legend
*
- * @return Chart
+ * @return $this
*/
public function setLegend(Legend $legend)
{
@@ -269,7 +269,7 @@ class Chart
*
* @param Title $label
*
- * @return Chart
+ * @return $this
*/
public function setXAxisLabel(Title $label)
{
@@ -293,7 +293,7 @@ class Chart
*
* @param Title $label
*
- * @return Chart
+ * @return $this
*/
public function setYAxisLabel(Title $label)
{
@@ -327,7 +327,7 @@ class Chart
*
* @param bool $plotVisibleOnly
*
- * @return Chart
+ * @return $this
*/
public function setPlotVisibleOnly($plotVisibleOnly)
{
@@ -351,7 +351,7 @@ class Chart
*
* @param string $displayBlanksAs
*
- * @return Chart
+ * @return $this
*/
public function setDisplayBlanksAs($displayBlanksAs)
{
@@ -423,7 +423,7 @@ class Chart
* @param int $xOffset
* @param int $yOffset
*
- * @return Chart
+ * @return $this
*/
public function setTopLeftPosition($cell, $xOffset = null, $yOffset = null)
{
@@ -467,7 +467,7 @@ class Chart
*
* @param string $cell
*
- * @return Chart
+ * @return $this
*/
public function setTopLeftCell($cell)
{
@@ -482,7 +482,7 @@ class Chart
* @param int $xOffset
* @param int $yOffset
*
- * @return Chart
+ * @return $this
*/
public function setTopLeftOffset($xOffset, $yOffset)
{
@@ -541,7 +541,7 @@ class Chart
* @param int $xOffset
* @param int $yOffset
*
- * @return Chart
+ * @return $this
*/
public function setBottomRightPosition($cell, $xOffset = null, $yOffset = null)
{
@@ -593,7 +593,7 @@ class Chart
* @param int $xOffset
* @param int $yOffset
*
- * @return Chart
+ * @return $this
*/
public function setBottomRightOffset($xOffset, $yOffset)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php
index 8056bbeea27..c20efabe808 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php
@@ -157,7 +157,7 @@ class DataSeries
*
* @param string $plotType
*
- * @return DataSeries
+ * @return $this
*/
public function setPlotType($plotType)
{
@@ -181,7 +181,7 @@ class DataSeries
*
* @param string $groupingType
*
- * @return DataSeries
+ * @return $this
*/
public function setPlotGrouping($groupingType)
{
@@ -205,7 +205,7 @@ class DataSeries
*
* @param string $plotDirection
*
- * @return DataSeries
+ * @return $this
*/
public function setPlotDirection($plotDirection)
{
@@ -297,7 +297,7 @@ class DataSeries
*
* @param null|string $plotStyle
*
- * @return DataSeries
+ * @return $this
*/
public function setPlotStyle($plotStyle)
{
@@ -360,7 +360,7 @@ class DataSeries
*
* @param bool $smoothLine
*
- * @return DataSeries
+ * @return $this
*/
public function setSmoothLine($smoothLine)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php
index e82b7efecfb..ec40cb84248 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php
@@ -117,7 +117,7 @@ class DataSeriesValues
*
* @throws Exception
*
- * @return DataSeriesValues
+ * @return $this
*/
public function setDataType($dataType)
{
@@ -144,7 +144,7 @@ class DataSeriesValues
*
* @param string $dataSource
*
- * @return DataSeriesValues
+ * @return $this
*/
public function setDataSource($dataSource)
{
@@ -168,7 +168,7 @@ class DataSeriesValues
*
* @param string $marker
*
- * @return DataSeriesValues
+ * @return $this
*/
public function setPointMarker($marker)
{
@@ -192,7 +192,7 @@ class DataSeriesValues
*
* @param string $formatCode
*
- * @return DataSeriesValues
+ * @return $this
*/
public function setFormatCode($formatCode)
{
@@ -275,7 +275,7 @@ class DataSeriesValues
*
* @param int $width
*
- * @return DataSeriesValues
+ * @return $this
*/
public function setLineWidth($width)
{
@@ -346,7 +346,7 @@ class DataSeriesValues
*
* @param array $dataValues
*
- * @return DataSeriesValues
+ * @return $this
*/
public function setDataValues($dataValues)
{
@@ -370,13 +370,13 @@ class DataSeriesValues
if ($flatten) {
$this->dataValues = Functions::flattenArray($newDataValues);
foreach ($this->dataValues as &$dataValue) {
- if ((!empty($dataValue)) && ($dataValue[0] == '#')) {
+ if (is_string($dataValue) && !empty($dataValue) && $dataValue[0] == '#') {
$dataValue = 0.0;
}
}
unset($dataValue);
} else {
- list($worksheet, $cellRange) = Worksheet::extractSheetTitle($this->dataSource, true);
+ [$worksheet, $cellRange] = Worksheet::extractSheetTitle($this->dataSource, true);
$dimensions = Coordinate::rangeDimension(str_replace('$', '', $cellRange));
if (($dimensions[0] == 1) || ($dimensions[1] == 1)) {
$this->dataValues = Functions::flattenArray($newDataValues);
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php
index 8cc83e5540c..b07fcae595a 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php
@@ -91,7 +91,7 @@ class GridLines extends Properties
/**
* Change Object State to True.
*
- * @return GridLines
+ * @return $this
*/
private function activateObject()
{
@@ -229,7 +229,7 @@ class GridLines extends Properties
*
* @param float $size
*
- * @return GridLines
+ * @return $this
*/
private function setGlowSize($size)
{
@@ -245,7 +245,7 @@ class GridLines extends Properties
* @param int $alpha
* @param string $type
*
- * @return GridLines
+ * @return $this
*/
private function setGlowColor($color, $alpha, $type)
{
@@ -305,7 +305,7 @@ class GridLines extends Properties
*
* @param int $shadow_presets
*
- * @return GridLines
+ * @return $this
*/
private function setShadowPresetsProperties($shadow_presets)
{
@@ -321,7 +321,7 @@ class GridLines extends Properties
* @param array $properties_map
* @param mixed &$reference
*
- * @return GridLines
+ * @return $this
*/
private function setShadowProperiesMapValues(array $properties_map, &$reference = null)
{
@@ -353,7 +353,7 @@ class GridLines extends Properties
* @param int $alpha
* @param string $type
*
- * @return GridLines
+ * @return $this
*/
private function setShadowColor($color, $alpha, $type)
{
@@ -375,7 +375,7 @@ class GridLines extends Properties
*
* @param float $blur
*
- * @return GridLines
+ * @return $this
*/
private function setShadowBlur($blur)
{
@@ -391,7 +391,7 @@ class GridLines extends Properties
*
* @param int $angle
*
- * @return GridLines
+ * @return $this
*/
private function setShadowAngle($angle)
{
@@ -407,7 +407,7 @@ class GridLines extends Properties
*
* @param float $distance
*
- * @return GridLines
+ * @return $this
*/
private function setShadowDistance($distance)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php
index a8a96d2f1cb..3e989c6d588 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php
@@ -153,7 +153,7 @@ class Layout
*
* @param string $value
*
- * @return Layout
+ * @return $this
*/
public function setLayoutTarget($value)
{
@@ -177,7 +177,7 @@ class Layout
*
* @param string $value
*
- * @return Layout
+ * @return $this
*/
public function setXMode($value)
{
@@ -201,7 +201,7 @@ class Layout
*
* @param string $value
*
- * @return Layout
+ * @return $this
*/
public function setYMode($value)
{
@@ -225,7 +225,7 @@ class Layout
*
* @param float $value
*
- * @return Layout
+ * @return $this
*/
public function setXPosition($value)
{
@@ -249,7 +249,7 @@ class Layout
*
* @param float $value
*
- * @return Layout
+ * @return $this
*/
public function setYPosition($value)
{
@@ -273,7 +273,7 @@ class Layout
*
* @param float $value
*
- * @return Layout
+ * @return $this
*/
public function setWidth($value)
{
@@ -297,7 +297,7 @@ class Layout
*
* @param float $value
*
- * @return Layout
+ * @return $this
*/
public function setHeight($value)
{
@@ -322,7 +322,7 @@ class Layout
*
* @param bool $value Show legend key
*
- * @return Layout
+ * @return $this
*/
public function setShowLegendKey($value)
{
@@ -347,7 +347,7 @@ class Layout
*
* @param bool $value Show val
*
- * @return Layout
+ * @return $this
*/
public function setShowVal($value)
{
@@ -372,7 +372,7 @@ class Layout
*
* @param bool $value Show cat name
*
- * @return Layout
+ * @return $this
*/
public function setShowCatName($value)
{
@@ -397,7 +397,7 @@ class Layout
*
* @param bool $value Show series name
*
- * @return Layout
+ * @return $this
*/
public function setShowSerName($value)
{
@@ -422,7 +422,7 @@ class Layout
*
* @param bool $value Show percentage
*
- * @return Layout
+ * @return $this
*/
public function setShowPercent($value)
{
@@ -447,7 +447,7 @@ class Layout
*
* @param bool $value Show bubble size
*
- * @return Layout
+ * @return $this
*/
public function setShowBubbleSize($value)
{
@@ -472,7 +472,7 @@ class Layout
*
* @param bool $value Show leader lines
*
- * @return Layout
+ * @return $this
*/
public function setShowLeaderLines($value)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php
index b98c638df76..9da4aa3297a 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php
@@ -94,7 +94,7 @@ class PlotArea
*
* @param DataSeries[] $plotSeries
*
- * @return PlotArea
+ * @return $this
*/
public function setPlotSeries(array $plotSeries)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php
index c0445d49f9a..9dcab049d2e 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php
@@ -442,7 +442,7 @@ class JpGraph implements IRenderer
$seriesPlot->link->SetColor(self::$colourSet[self::$plotColour]);
} elseif ($scatterStyle == 'smoothMarker') {
$spline = new \Spline($dataValuesY, $dataValuesX);
- list($splineDataY, $splineDataX) = $spline->Get(count($dataValuesX) * self::$width / 20);
+ [$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * self::$width / 20);
$lplot = new \LinePlot($splineDataX, $splineDataY);
$lplot->SetColor(self::$colourSet[self::$plotColour]);
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php
index f53c6b37bdd..650bcdc1521 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php
@@ -45,7 +45,7 @@ class Title
*
* @param string $caption
*
- * @return Title
+ * @return $this
*/
public function setCaption($caption)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php
index 46d3cf7e14e..7f34c2315c5 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php
@@ -16,8 +16,6 @@ abstract class CellsFactory
* */
public static function getInstance(Worksheet $parent)
{
- $instance = new Cells($parent, Settings::getCache());
-
- return $instance;
+ return new Cells($parent, Settings::getCache());
}
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php
index 1b5ab1fd2c1..8041ddaf3e8 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php
@@ -96,7 +96,7 @@ class Comment implements IComparable
*
* @param string $author
*
- * @return Comment
+ * @return $this
*/
public function setAuthor($author)
{
@@ -120,7 +120,7 @@ class Comment implements IComparable
*
* @param RichText $pValue
*
- * @return Comment
+ * @return $this
*/
public function setText(RichText $pValue)
{
@@ -144,7 +144,7 @@ class Comment implements IComparable
*
* @param string $width
*
- * @return Comment
+ * @return $this
*/
public function setWidth($width)
{
@@ -168,7 +168,7 @@ class Comment implements IComparable
*
* @param string $value
*
- * @return Comment
+ * @return $this
*/
public function setHeight($value)
{
@@ -192,7 +192,7 @@ class Comment implements IComparable
*
* @param string $value
*
- * @return Comment
+ * @return $this
*/
public function setMarginLeft($value)
{
@@ -216,7 +216,7 @@ class Comment implements IComparable
*
* @param string $value
*
- * @return Comment
+ * @return $this
*/
public function setMarginTop($value)
{
@@ -240,7 +240,7 @@ class Comment implements IComparable
*
* @param bool $value
*
- * @return Comment
+ * @return $this
*/
public function setVisible($value)
{
@@ -264,7 +264,7 @@ class Comment implements IComparable
*
* @param string $alignment see Style\Alignment::HORIZONTAL_*
*
- * @return Comment
+ * @return $this
*/
public function setAlignment($alignment)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
index 1a432db0497..58fd2ef60cb 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
@@ -122,7 +122,7 @@ class Properties
*
* @param string $creator
*
- * @return Properties
+ * @return $this
*/
public function setCreator($creator)
{
@@ -146,7 +146,7 @@ class Properties
*
* @param string $pValue
*
- * @return Properties
+ * @return $this
*/
public function setLastModifiedBy($pValue)
{
@@ -170,7 +170,7 @@ class Properties
*
* @param int|string $time
*
- * @return Properties
+ * @return $this
*/
public function setCreated($time)
{
@@ -204,7 +204,7 @@ class Properties
*
* @param int|string $time
*
- * @return Properties
+ * @return $this
*/
public function setModified($time)
{
@@ -238,7 +238,7 @@ class Properties
*
* @param string $title
*
- * @return Properties
+ * @return $this
*/
public function setTitle($title)
{
@@ -262,7 +262,7 @@ class Properties
*
* @param string $description
*
- * @return Properties
+ * @return $this
*/
public function setDescription($description)
{
@@ -286,7 +286,7 @@ class Properties
*
* @param string $subject
*
- * @return Properties
+ * @return $this
*/
public function setSubject($subject)
{
@@ -310,7 +310,7 @@ class Properties
*
* @param string $keywords
*
- * @return Properties
+ * @return $this
*/
public function setKeywords($keywords)
{
@@ -334,7 +334,7 @@ class Properties
*
* @param string $category
*
- * @return Properties
+ * @return $this
*/
public function setCategory($category)
{
@@ -358,7 +358,7 @@ class Properties
*
* @param string $company
*
- * @return Properties
+ * @return $this
*/
public function setCompany($company)
{
@@ -382,7 +382,7 @@ class Properties
*
* @param string $manager
*
- * @return Properties
+ * @return $this
*/
public function setManager($manager)
{
@@ -453,7 +453,7 @@ class Properties
* 'd' : Date/Time
* 'b' : Boolean
*
- * @return Properties
+ * @return $this
*/
public function setCustomProperty($propertyName, $propertyValue = '', $propertyType = null)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php
index 1682678c2e7..cef3db8c44c 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php
@@ -75,7 +75,7 @@ class Security
*
* @param bool $pValue
*
- * @return Security
+ * @return $this
*/
public function setLockRevision($pValue)
{
@@ -99,7 +99,7 @@ class Security
*
* @param bool $pValue
*
- * @return Security
+ * @return $this
*/
public function setLockStructure($pValue)
{
@@ -123,7 +123,7 @@ class Security
*
* @param bool $pValue
*
- * @return Security
+ * @return $this
*/
public function setLockWindows($pValue)
{
@@ -148,7 +148,7 @@ class Security
* @param string $pValue
* @param bool $pAlreadyHashed If the password has already been hashed, set this to true
*
- * @return Security
+ * @return $this
*/
public function setRevisionsPassword($pValue, $pAlreadyHashed = false)
{
@@ -176,7 +176,7 @@ class Security
* @param string $pValue
* @param bool $pAlreadyHashed If the password has already been hashed, set this to true
*
- * @return Security
+ * @return $this
*/
public function setWorkbookPassword($pValue, $pAlreadyHashed = false)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php
new file mode 100644
index 00000000000..de6f313f49c
--- /dev/null
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DocumentGenerator.php
@@ -0,0 +1,111 @@
+ $category) {
+ $result .= "\n";
+ $result .= "## {$categoryConstant}\n";
+ $result .= "\n";
+ $lengths = [20, 42];
+ $result .= self::tableRow($lengths, ['Excel Function', 'PhpSpreadsheet Function']) . "\n";
+ $result .= self::tableRow($lengths, null) . "\n";
+ foreach ($phpSpreadsheetFunctions as $excelFunction => $functionInfo) {
+ if ($category === $functionInfo['category']) {
+ $phpFunction = self::getPhpSpreadsheetFunctionText($functionInfo['functionCall']);
+ $result .= self::tableRow($lengths, [$excelFunction, $phpFunction]) . "\n";
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @throws ReflectionException
+ *
+ * @return array
+ */
+ private static function getCategories(): array
+ {
+ return (new ReflectionClass(Category::class))->getConstants();
+ }
+
+ private static function tableRow(array $lengths, array $values = null): string
+ {
+ $result = '';
+ foreach (array_map(null, $lengths, $values ?? []) as $i => [$length, $value]) {
+ $pad = $value === null ? '-' : ' ';
+ if ($i > 0) {
+ $result .= '|' . $pad;
+ }
+ $result .= str_pad($value ?? '', $length, $pad);
+ }
+
+ return rtrim($result, ' ');
+ }
+
+ private static function getPhpSpreadsheetFunctionText($functionCall): string
+ {
+ if (is_string($functionCall)) {
+ return $functionCall;
+ }
+ if ($functionCall === [Functions::class, 'DUMMY']) {
+ return '**Not yet Implemented**';
+ }
+ if (is_array($functionCall)) {
+ return "\\{$functionCall[0]}::{$functionCall[1]}";
+ }
+
+ throw new UnexpectedValueException(
+ '$functionCall is of type ' . gettype($functionCall) . '. string or array expected'
+ );
+ }
+
+ /**
+ * @param array[] $phpSpreadsheetFunctions
+ *
+ * @throws ReflectionException
+ *
+ * @return string
+ */
+ public static function generateFunctionListByName(array $phpSpreadsheetFunctions): string
+ {
+ $categoryConstants = array_flip(self::getCategories());
+ $result = "# Function list by name\n";
+ $lastAlphabet = null;
+ foreach ($phpSpreadsheetFunctions as $excelFunction => $functionInfo) {
+ $lengths = [20, 31, 42];
+ if ($lastAlphabet !== $excelFunction[0]) {
+ $lastAlphabet = $excelFunction[0];
+ $result .= "\n";
+ $result .= "## {$lastAlphabet}\n";
+ $result .= "\n";
+ $result .= self::tableRow($lengths, ['Excel Function', 'Category', 'PhpSpreadsheet Function']) . "\n";
+ $result .= self::tableRow($lengths, null) . "\n";
+ }
+ $category = $categoryConstants[$functionInfo['category']];
+ $phpFunction = self::getPhpSpreadsheetFunctionText($functionInfo['functionCall']);
+ $result .= self::tableRow($lengths, [$excelFunction, $category, $phpFunction]) . "\n";
+ }
+
+ return $result;
+ }
+}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php
index a116334a4ef..4266ea546ad 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php
@@ -52,9 +52,8 @@ abstract class IOFactory
// Instantiate writer
$className = self::$writers[$writerType];
- $writer = new $className($spreadsheet);
- return $writer;
+ return new $className($spreadsheet);
}
/**
@@ -74,9 +73,8 @@ abstract class IOFactory
// Instantiate reader
$className = self::$readers[$readerType];
- $reader = new $className();
- return $reader;
+ return new $className();
}
/**
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php
index 1f94d5a4ed2..e539b7c5304 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php
@@ -82,7 +82,7 @@ class NamedRange
*
* @param string $value
*
- * @return NamedRange
+ * @return $this
*/
public function setName($value)
{
@@ -123,7 +123,7 @@ class NamedRange
*
* @param Worksheet $value
*
- * @return NamedRange
+ * @return $this
*/
public function setWorksheet(Worksheet $value = null)
{
@@ -149,7 +149,7 @@ class NamedRange
*
* @param string $value
*
- * @return NamedRange
+ * @return $this
*/
public function setRange($value)
{
@@ -175,7 +175,7 @@ class NamedRange
*
* @param bool $value
*
- * @return NamedRange
+ * @return $this
*/
public function setLocalOnly($value)
{
@@ -200,7 +200,7 @@ class NamedRange
*
* @param null|Worksheet $value
*
- * @return NamedRange
+ * @return $this
*/
public function setScope(Worksheet $value = null)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
index 81ca1a8cf3e..47134098818 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
@@ -70,7 +70,7 @@ class Csv extends BaseReader
*
* @param string $pValue Input encoding, eg: 'UTF-8'
*
- * @return Csv
+ * @return $this
*/
public function setInputEncoding($pValue)
{
@@ -175,9 +175,8 @@ class Csv extends BaseReader
}
}
foreach ($potentialDelimiters as $delimiter) {
- $counts[$delimiter][] = isset($countLine[$delimiter])
- ? $countLine[$delimiter]
- : 0;
+ $counts[$delimiter][] = $countLine[$delimiter]
+ ?? 0;
}
}
@@ -416,7 +415,7 @@ class Csv extends BaseReader
*
* @param string $delimiter Delimiter, eg: ','
*
- * @return CSV
+ * @return $this
*/
public function setDelimiter($delimiter)
{
@@ -440,7 +439,7 @@ class Csv extends BaseReader
*
* @param string $enclosure Enclosure, defaults to "
*
- * @return CSV
+ * @return $this
*/
public function setEnclosure($enclosure)
{
@@ -467,7 +466,7 @@ class Csv extends BaseReader
*
* @param int $pValue Sheet index
*
- * @return CSV
+ * @return $this
*/
public function setSheetIndex($pValue)
{
@@ -481,7 +480,7 @@ class Csv extends BaseReader
*
* @param bool $contiguous
*
- * @return Csv
+ * @return $this
*/
public function setContiguous($contiguous)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php
index 7445ef1bf25..44ab701d133 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php
@@ -267,7 +267,7 @@ class Gnumeric extends BaseReader
break;
case 'user-defined':
- list(, $attrName) = explode(':', $attributes['name']);
+ [, $attrName] = explode(':', $attributes['name']);
switch ($attrName) {
case 'publisher':
$docProps->setCompany(trim($propertyValue));
@@ -879,7 +879,7 @@ class Gnumeric extends BaseReader
private static function parseGnumericColour($gnmColour)
{
- list($gnmR, $gnmG, $gnmB) = explode(':', $gnmColour);
+ [$gnmR, $gnmG, $gnmB] = explode(':', $gnmColour);
$gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2);
$gnmG = substr(str_pad($gnmG, 4, '0', STR_PAD_RIGHT), 0, 2);
$gnmB = substr(str_pad($gnmB, 4, '0', STR_PAD_RIGHT), 0, 2);
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
index 2e823f03bb5..a255cfd9eb8 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
@@ -223,7 +223,7 @@ class Html extends BaseReader
*
* @param string $pValue Input encoding, eg: 'ANSI'
*
- * @return Html
+ * @return $this
*/
public function setInputEncoding($pValue)
{
@@ -374,8 +374,9 @@ class Html extends BaseReader
// no break
case 'br':
if ($this->tableLevel > 0) {
- // If we're inside a table, replace with a \n
+ // If we're inside a table, replace with a \n and set the cell to wrap
$cellContent .= "\n";
+ $sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
} else {
// Otherwise flush our existing content and move the row cursor on
$this->flushCell($sheet, $column, $row, $cellContent);
@@ -489,22 +490,22 @@ class Html extends BaseReader
case 'td':
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
- // apply inline style
- $this->applyInlineStyle($sheet, $row, $column, $attributeArray);
-
while (isset($this->rowspan[$column . $row])) {
++$column;
}
+ // apply inline style
+ $this->applyInlineStyle($sheet, $row, $column, $attributeArray);
+
$this->flushCell($sheet, $column, $row, $cellContent);
if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
//create merging rowspan and colspan
$columnTo = $column;
- for ($i = 0; $i < $attributeArray['colspan'] - 1; ++$i) {
+ for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
++$columnTo;
}
- $range = $column . $row . ':' . $columnTo . ($row + $attributeArray['rowspan'] - 1);
+ $range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1);
foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
$this->rowspan[$value] = true;
}
@@ -512,7 +513,7 @@ class Html extends BaseReader
$column = $columnTo;
} elseif (isset($attributeArray['rowspan'])) {
//create merging rowspan
- $range = $column . $row . ':' . $column . ($row + $attributeArray['rowspan'] - 1);
+ $range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1);
foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
$this->rowspan[$value] = true;
}
@@ -520,7 +521,7 @@ class Html extends BaseReader
} elseif (isset($attributeArray['colspan'])) {
//create merging colspan
$columnTo = $column;
- for ($i = 0; $i < $attributeArray['colspan'] - 1; ++$i) {
+ for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
++$columnTo;
}
$sheet->mergeCells($column . $row . ':' . $columnTo . $row);
@@ -591,28 +592,63 @@ class Html extends BaseReader
throw new Exception($pFilename . ' is an Invalid HTML file.');
}
- // Create new sheet
- while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
- $spreadsheet->createSheet();
- }
- $spreadsheet->setActiveSheetIndex($this->sheetIndex);
-
- // Create a new DOM object
+ // Create a new DOM object
$dom = new DOMDocument();
- // Reload the HTML file into the DOM object
+ // Reload the HTML file into the DOM object
$loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scanFile($pFilename), 'HTML-ENTITIES', 'UTF-8'));
if ($loaded === false) {
throw new Exception('Failed to load ' . $pFilename . ' as a DOM Document');
}
- // Discard white space
- $dom->preserveWhiteSpace = false;
+ return $this->loadDocument($dom, $spreadsheet);
+ }
+
+ /**
+ * Spreadsheet from content.
+ *
+ * @param string $content
+ * @param null|Spreadsheet $spreadsheet
+ *
+ * @return Spreadsheet
+ */
+ public function loadFromString($content, ?Spreadsheet $spreadsheet = null): Spreadsheet
+ {
+ // Create a new DOM object
+ $dom = new DOMDocument();
+ // Reload the HTML file into the DOM object
+ $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scan($content), 'HTML-ENTITIES', 'UTF-8'));
+ if ($loaded === false) {
+ throw new Exception('Failed to load content as a DOM Document');
+ }
+
+ return $this->loadDocument($dom, $spreadsheet ?? new Spreadsheet());
+ }
+
+ /**
+ * Loads PhpSpreadsheet from DOMDocument into PhpSpreadsheet instance.
+ *
+ * @param DOMDocument $document
+ * @param Spreadsheet $spreadsheet
+ *
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ *
+ * @return Spreadsheet
+ */
+ private function loadDocument(DOMDocument $document, Spreadsheet $spreadsheet): Spreadsheet
+ {
+ while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
+ $spreadsheet->createSheet();
+ }
+ $spreadsheet->setActiveSheetIndex($this->sheetIndex);
+
+ // Discard white space
+ $document->preserveWhiteSpace = false;
$row = 0;
$column = 'A';
$content = '';
$this->rowspan = [];
- $this->processDomElement($dom, $spreadsheet->getActiveSheet(), $row, $column, $content);
+ $this->processDomElement($document, $spreadsheet->getActiveSheet(), $row, $column, $content);
// Return
return $spreadsheet;
@@ -633,7 +669,7 @@ class Html extends BaseReader
*
* @param int $pValue Sheet index
*
- * @return HTML
+ * @return $this
*/
public function setSheetIndex($pValue)
{
@@ -919,7 +955,7 @@ class Html extends BaseReader
*/
private function setBorderStyle(Style $cellStyle, $styleValue, $type)
{
- list(, $borderStyle, $color) = explode(' ', $styleValue);
+ [, $borderStyle, $color] = explode(' ', $styleValue);
$cellStyle->applyFromArray([
'borders' => [
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php
index 51462c2003e..5fff07aa383 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php
@@ -490,7 +490,7 @@ class Ods extends BaseReader
$dateObj = new DateTime($value, $GMT);
$dateObj->setTimeZone($timezoneObj);
- list($year, $month, $day, $hour, $minute, $second) = explode(
+ [$year, $month, $day, $hour, $minute, $second] = explode(
' ',
$dateObj->format('Y m d H i s')
);
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
index 8b6122c56e7..c5c7caf8439 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
@@ -19,12 +19,11 @@ class Properties
$docProps = $this->spreadsheet->getProperties();
$officeProperty = $xml->children($namespacesMeta['office']);
foreach ($officeProperty as $officePropertyData) {
- /** @var \SimpleXMLElement $officePropertyData */
- $officePropertiesDC = (object) [];
+ // @var \SimpleXMLElement $officePropertyData
if (isset($namespacesMeta['dc'])) {
$officePropertiesDC = $officePropertyData->children($namespacesMeta['dc']);
+ $this->setCoreProperties($docProps, $officePropertiesDC);
}
- $this->setCoreProperties($docProps, $officePropertiesDC);
$officePropertyMeta = (object) [];
if (isset($namespacesMeta['dc'])) {
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
index c1013c2eb22..9912e9376bd 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
@@ -83,7 +83,7 @@ class Slk extends BaseReader
*
* @param string $pValue Input encoding, eg: 'ANSI'
*
- * @return Slk
+ * @return $this
*/
public function setInputEncoding($pValue)
{
@@ -384,7 +384,7 @@ class Slk extends BaseReader
break;
case 'W':
- list($startCol, $endCol, $columnWidth) = explode(' ', substr($rowDatum, 1));
+ [$startCol, $endCol, $columnWidth] = explode(' ', substr($rowDatum, 1));
break;
case 'S':
@@ -485,7 +485,7 @@ class Slk extends BaseReader
*
* @param int $pValue Sheet index
*
- * @return Slk
+ * @return $this
*/
public function setSheetIndex($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
index bd2c9c1db5b..313d7216ba5 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
@@ -1089,8 +1089,8 @@ class Xls extends BaseReader
}
// calculate the width and height of the shape
- list($startColumn, $startRow) = Coordinate::coordinateFromString($spContainer->getStartCoordinates());
- list($endColumn, $endRow) = Coordinate::coordinateFromString($spContainer->getEndCoordinates());
+ [$startColumn, $startRow] = Coordinate::coordinateFromString($spContainer->getStartCoordinates());
+ [$endColumn, $endRow] = Coordinate::coordinateFromString($spContainer->getEndCoordinates());
$startOffsetX = $spContainer->getStartOffsetX();
$startOffsetY = $spContainer->getStartOffsetY();
@@ -1175,7 +1175,7 @@ class Xls extends BaseReader
// treat SHAREDFMLA records
if ($this->version == self::XLS_BIFF8) {
foreach ($this->sharedFormulaParts as $cell => $baseCell) {
- list($column, $row) = Coordinate::coordinateFromString($cell);
+ [$column, $row] = Coordinate::coordinateFromString($cell);
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
$formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
$this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
@@ -1251,8 +1251,8 @@ class Xls extends BaseReader
$coordinateStrings = explode(':', $extractedRange);
if (count($coordinateStrings) == 2) {
- list($firstColumn, $firstRow) = Coordinate::coordinateFromString($coordinateStrings[0]);
- list($lastColumn, $lastRow) = Coordinate::coordinateFromString($coordinateStrings[1]);
+ [$firstColumn, $firstRow] = Coordinate::coordinateFromString($coordinateStrings[0]);
+ [$lastColumn, $lastRow] = Coordinate::coordinateFromString($coordinateStrings[1]);
if ($firstColumn == 'A' and $lastColumn == 'IV') {
// then we have repeating rows
@@ -3825,7 +3825,7 @@ class Xls extends BaseReader
}
}
- if (!$this->readDataOnly && !$emptyCell) {
+ if (!$this->readDataOnly && !$emptyCell && isset($this->mapCellXfIndex[$xfIndex])) {
// add style information
$cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
@@ -5279,12 +5279,10 @@ class Xls extends BaseReader
$nextIdentifier = self::getUInt2d($this->data, $this->pos);
} while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
- $splicedData = [
+ return [
'recordData' => $data,
'spliceOffsets' => $spliceOffsets,
];
-
- return $splicedData;
}
/**
@@ -5355,12 +5353,12 @@ class Xls extends BaseReader
$formulaStrings = [];
foreach ($tokens as $token) {
// initialize spaces
- $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen
- $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen
- $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis
- $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis
- $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis
- $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis
+ $space0 = $space0 ?? ''; // spaces before next token, not tParen
+ $space1 = $space1 ?? ''; // carriage returns before next token, not tParen
+ $space2 = $space2 ?? ''; // spaces before opening parenthesis
+ $space3 = $space3 ?? ''; // carriage returns before opening parenthesis
+ $space4 = $space4 ?? ''; // spaces before closing parenthesis
+ $space5 = $space5 ?? ''; // carriage returns before closing parenthesis
switch ($token['name']) {
case 'tAdd': // addition
@@ -7145,7 +7143,7 @@ class Xls extends BaseReader
*/
private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
{
- list($baseCol, $baseRow) = Coordinate::coordinateFromString($baseCell);
+ [$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell);
$baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
// offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
@@ -7328,7 +7326,7 @@ class Xls extends BaseReader
*/
private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
{
- list($baseCol, $baseRow) = Coordinate::coordinateFromString($baseCell);
+ [$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell);
$baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
// TODO: if cell range is just a single cell, should this funciton
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
index 5372fef9c13..566e9fbbf08 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
@@ -77,34 +77,17 @@ class Xlsx extends BaseReader
{
File::assertFile($pFilename);
- $xl = false;
- // Load file
+ $result = false;
$zip = new ZipArchive();
- if ($zip->open($pFilename) === true) {
- // check if it is an OOXML archive
- $rels = simplexml_load_string(
- $this->securityScanner->scan(
- $this->getFromZipArchive($zip, '_rels/.rels')
- ),
- 'SimpleXMLElement',
- Settings::getLibXmlLoaderOptions()
- );
- if ($rels !== false) {
- foreach ($rels->Relationship as $rel) {
- switch ($rel['Type']) {
- case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
- if (basename($rel['Target']) == 'workbook.xml') {
- $xl = true;
- }
- break;
- }
- }
- }
+ if ($zip->open($pFilename) === true) {
+ $workbookBasename = $this->getWorkbookBaseName($zip);
+ $result = !empty($workbookBasename);
+
$zip->close();
}
- return $xl;
+ return $result;
}
/**
@@ -357,8 +340,9 @@ class Xlsx extends BaseReader
// Read the theme first, because we need the colour scheme when reading the styles
//~ http://schemas.openxmlformats.org/package/2006/relationships"
+ $workbookBasename = $this->getWorkbookBaseName($zip);
$wbRels = simplexml_load_string(
- $this->securityScanner->scan($this->getFromZipArchive($zip, 'xl/_rels/workbook.xml.rels')),
+ $this->securityScanner->scan($this->getFromZipArchive($zip, "xl/_rels/${workbookBasename}.rels")),
'SimpleXMLElement',
Settings::getLibXmlLoaderOptions()
);
@@ -445,18 +429,20 @@ class Xlsx extends BaseReader
$sharedStrings = [];
$xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings']"));
- //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
- $xmlStrings = simplexml_load_string(
- $this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
- 'SimpleXMLElement',
- Settings::getLibXmlLoaderOptions()
- );
- if (isset($xmlStrings, $xmlStrings->si)) {
- foreach ($xmlStrings->si as $val) {
- if (isset($val->t)) {
- $sharedStrings[] = StringHelper::controlCharacterOOXML2PHP((string) $val->t);
- } elseif (isset($val->r)) {
- $sharedStrings[] = $this->parseRichText($val);
+ if ($xpath) {
+ //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
+ $xmlStrings = simplexml_load_string(
+ $this->securityScanner->scan($this->getFromZipArchive($zip, "$dir/$xpath[Target]")),
+ 'SimpleXMLElement',
+ Settings::getLibXmlLoaderOptions()
+ );
+ if (isset($xmlStrings, $xmlStrings->si)) {
+ foreach ($xmlStrings->si as $val) {
+ if (isset($val->t)) {
+ $sharedStrings[] = StringHelper::controlCharacterOOXML2PHP((string) $val->t);
+ } elseif (isset($val->r)) {
+ $sharedStrings[] = $this->parseRichText($val);
+ }
}
}
}
@@ -743,15 +729,6 @@ class Xlsx extends BaseReader
// read empty cells or the cells are not empty
if ($this->readEmptyCells || ($value !== null && $value !== '')) {
- // Check for numeric values
- if (is_numeric($value) && $cellDataType != 's') {
- if ($value == (int) $value) {
- $value = (int) $value;
- } elseif ($value == (float) $value) {
- $value = (float) $value;
- }
- }
-
// Rich text?
if ($value instanceof RichText && $this->readDataOnly) {
$value = $value->getPlainText();
@@ -1352,7 +1329,7 @@ class Xlsx extends BaseReader
$rangeSets = preg_split("/('?(?:.*?)'?(?:![A-Z0-9]+:[A-Z0-9]+)),?/", $extractedRange, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$newRangeSets = [];
foreach ($rangeSets as $rangeSet) {
- list($sheetName, $rangeSet) = Worksheet::extractSheetTitle($rangeSet, true);
+ [$sheetName, $rangeSet] = Worksheet::extractSheetTitle($rangeSet, true);
if (strpos($rangeSet, ':') === false) {
$rangeSet = $rangeSet . ':' . $rangeSet;
}
@@ -1426,7 +1403,7 @@ class Xlsx extends BaseReader
$locatedSheet = $excel->getSheetByName($extractedSheetName);
// Modify range
- list($worksheetName, $extractedRange) = Worksheet::extractSheetTitle($extractedRange, true);
+ [$worksheetName, $extractedRange] = Worksheet::extractSheetTitle($extractedRange, true);
}
if ($locatedSheet !== null) {
@@ -1437,7 +1414,7 @@ class Xlsx extends BaseReader
}
}
- if ((!$this->readDataOnly) || (!empty($this->loadSheetsOnly))) {
+ if ((!$this->readDataOnly || !empty($this->loadSheetsOnly)) && isset($xmlWorkbook->bookViews->workbookView)) {
$workbookView = $xmlWorkbook->bookViews->workbookView;
// active sheet index
@@ -1643,8 +1620,6 @@ class Xlsx extends BaseReader
$docStyle->getFill()->setFillType($patternType);
if ($style->fill->patternFill->fgColor) {
$docStyle->getFill()->getStartColor()->setARGB(self::readColor($style->fill->patternFill->fgColor, true));
- } else {
- $docStyle->getFill()->getStartColor()->setARGB('FF000000');
}
if ($style->fill->patternFill->bgColor) {
$docStyle->getFill()->getEndColor()->setARGB(self::readColor($style->fill->patternFill->bgColor, true));
@@ -1841,7 +1816,7 @@ class Xlsx extends BaseReader
private static function getArrayItem($array, $key = 0)
{
- return isset($array[$key]) ? $array[$key] : null;
+ return $array[$key] ?? null;
}
private static function dirAdd($base, $add)
@@ -1851,7 +1826,7 @@ class Xlsx extends BaseReader
private static function toCSSArray($style)
{
- $style = trim(str_replace(["\r", "\n"], '', $style), ';');
+ $style = self::stripWhiteSpaceFromStyleString($style);
$temp = explode(';', $style);
$style = [];
@@ -1880,6 +1855,11 @@ class Xlsx extends BaseReader
return $style;
}
+ public static function stripWhiteSpaceFromStyleString($string)
+ {
+ return trim(str_replace(["\r", "\n", ' '], '', $string), ';');
+ }
+
private static function boolean($value)
{
if (is_object($value)) {
@@ -2026,4 +2006,38 @@ class Xlsx extends BaseReader
return (bool) $xsdBoolean;
}
+
+ /**
+ * @param ZipArchive $zip Opened zip archive
+ *
+ * @return string basename of the used excel workbook
+ */
+ private function getWorkbookBaseName(ZipArchive $zip)
+ {
+ $workbookBasename = '';
+
+ // check if it is an OOXML archive
+ $rels = simplexml_load_string(
+ $this->securityScanner->scan(
+ $this->getFromZipArchive($zip, '_rels/.rels')
+ ),
+ 'SimpleXMLElement',
+ Settings::getLibXmlLoaderOptions()
+ );
+ if ($rels !== false) {
+ foreach ($rels->Relationship as $rel) {
+ switch ($rel['Type']) {
+ case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument':
+ $basename = basename($rel['Target']);
+ if (preg_match('/workbook.*\.xml/', $basename)) {
+ $workbookBasename = $basename;
+ }
+
+ break;
+ }
+ }
+ }
+
+ return $workbookBasename;
+ }
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
index 6929758dfe9..69d5f69e0a4 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
@@ -20,7 +20,8 @@ class AutoFilter
public function load()
{
- $autoFilterRange = (string) $this->worksheetXml->autoFilter['ref'];
+ // Remove all "$" in the auto filter range
+ $autoFilterRange = preg_replace('/\$/', '', $this->worksheetXml->autoFilter['ref']);
if (strpos($autoFilterRange, ':') !== false) {
$this->readAutoFilter($autoFilterRange, $this->worksheetXml);
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
index b3de5d1cde6..722b77952a4 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
@@ -36,6 +36,8 @@ class ConditionalStyles
if (((string) $cfRule['type'] == Conditional::CONDITION_NONE
|| (string) $cfRule['type'] == Conditional::CONDITION_CELLIS
|| (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT
+ || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSBLANKS
+ || (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS
|| (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION)
&& isset($this->dxfs[(int) ($cfRule['dxfId'])])) {
$conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
index bc6bba2cdb2..bf8e57d807a 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
@@ -86,6 +86,6 @@ class Properties
private static function getArrayItem(array $array, $key = 0)
{
- return isset($array[$key]) ? $array[$key] : null;
+ return $array[$key] ?? null;
}
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
index 2caaec3108e..88c01ead373 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
@@ -24,6 +24,7 @@ class SheetViews extends BaseParserClass
$this->gridLines();
$this->headers();
$this->direction();
+ $this->showZeros();
if (isset($this->sheetViewXml->pane)) {
$this->pane();
@@ -92,6 +93,15 @@ class SheetViews extends BaseParserClass
}
}
+ private function showZeros()
+ {
+ if (isset($this->sheetViewXml['showZeros'])) {
+ $this->worksheet->getSheetView()->setShowZeros(
+ self::boolean((string) $this->sheetViewXml['showZeros'])
+ );
+ }
+ }
+
private function pane()
{
$xSplit = 0;
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
index c06564a3d11..40106258c40 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
@@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Style\Borders;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Style\Protection;
use PhpOffice\PhpSpreadsheet\Style\Style;
@@ -71,6 +72,17 @@ class Styles extends BaseParserClass
}
}
+ private static function readNumberFormat(NumberFormat $numfmtStyle, \SimpleXMLElement $numfmtStyleXml)
+ {
+ if ($numfmtStyleXml->count() === 0) {
+ return;
+ }
+ $numfmt = $numfmtStyleXml->attributes();
+ if ($numfmt->count() > 0 && isset($numfmt['formatCode'])) {
+ $numfmtStyle->setFormatCode((string) $numfmt['formatCode']);
+ }
+ }
+
private static function readFillStyle(Fill $fillStyle, \SimpleXMLElement $fillStyleXml)
{
if ($fillStyleXml->gradientFill) {
@@ -149,7 +161,11 @@ class Styles extends BaseParserClass
private function readStyle(Style $docStyle, $style)
{
- $docStyle->getNumberFormat()->setFormatCode($style->numFmt);
+ if ($style->numFmt instanceof \SimpleXMLElement) {
+ self::readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
+ } else {
+ $docStyle->getNumberFormat()->setFormatCode($style->numFmt);
+ }
if (isset($style->font)) {
self::readFontStyle($docStyle->getFont(), $style->font);
@@ -163,7 +179,7 @@ class Styles extends BaseParserClass
self::readBorderStyle($docStyle->getBorders(), $style->border);
}
- if (isset($style->alignment)) {
+ if (isset($style->alignment->alignment)) {
self::readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
}
@@ -260,6 +276,6 @@ class Styles extends BaseParserClass
private static function getArrayItem($array, $key = 0)
{
- return isset($array[$key]) ? $array[$key] : null;
+ return $array[$key] ?? null;
}
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
index 8ab7a9c3954..b4fffa4fb8b 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
@@ -285,9 +285,8 @@ class Xml extends BaseReader
{
$pixels = ($widthUnits / 256) * 7;
$offsetWidthUnits = $widthUnits % 256;
- $pixels += round($offsetWidthUnits / (256 / 7));
- return $pixels;
+ return $pixels + round($offsetWidthUnits / (256 / 7));
}
protected static function hex2str($hex)
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
index 13dbe042a75..143e80d8ce9 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
@@ -82,11 +82,8 @@ class ReferenceHelper
*/
public static function cellSort($a, $b)
{
- // TODO Scrutinizer doesn't like sscanf($a, '%[A-Z]%d', $ac, $ar), and we can't use short list() syntax
- // [$ac, $ar] = sscanf($a, '%[A-Z]%d') while retaining PHP 5.6 support.
- // Switch when we drop support for 5.6
- list($ac, $ar) = sscanf($a, '%[A-Z]%d');
- list($bc, $br) = sscanf($b, '%[A-Z]%d');
+ [$ac, $ar] = sscanf($a, '%[A-Z]%d');
+ [$bc, $br] = sscanf($b, '%[A-Z]%d');
if ($ar === $br) {
return strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
@@ -106,11 +103,8 @@ class ReferenceHelper
*/
public static function cellReverseSort($a, $b)
{
- // TODO Scrutinizer doesn't like sscanf($a, '%[A-Z]%d', $ac, $ar), and we can't use short list() syntax
- // [$ac, $ar] = sscanf($a, '%[A-Z]%d') while retaining PHP 5.6 support.
- // Switch when we drop support for 5.6
- list($ac, $ar) = sscanf($a, '%[A-Z]%d');
- list($bc, $br) = sscanf($b, '%[A-Z]%d');
+ [$ac, $ar] = sscanf($a, '%[A-Z]%d');
+ [$bc, $br] = sscanf($b, '%[A-Z]%d');
if ($ar === $br) {
return 1 - strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
@@ -132,7 +126,7 @@ class ReferenceHelper
*/
private static function cellAddressInDeleteRange($cellAddress, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)
{
- list($cellColumn, $cellRow) = Coordinate::coordinateFromString($cellAddress);
+ [$cellColumn, $cellRow] = Coordinate::coordinateFromString($cellAddress);
$cellColumnIndex = Coordinate::columnIndexFromString($cellColumn);
// Is cell within the range of rows/columns if we're deleting
if ($pNumRows < 0 &&
@@ -319,7 +313,7 @@ class ReferenceHelper
if (!empty($aColumnDimensions)) {
foreach ($aColumnDimensions as $objColumnDimension) {
$newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows);
- list($newReference) = Coordinate::coordinateFromString($newReference);
+ [$newReference] = Coordinate::coordinateFromString($newReference);
if ($objColumnDimension->getColumnIndex() != $newReference) {
$objColumnDimension->setColumnIndex($newReference);
}
@@ -344,7 +338,7 @@ class ReferenceHelper
if (!empty($aRowDimensions)) {
foreach ($aRowDimensions as $objRowDimension) {
$newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows);
- list(, $newReference) = Coordinate::coordinateFromString($newReference);
+ [, $newReference] = Coordinate::coordinateFromString($newReference);
if ($objRowDimension->getRowIndex() != $newReference) {
$objRowDimension->setRowIndex($newReference);
}
@@ -378,7 +372,7 @@ class ReferenceHelper
$allCoordinates = $pSheet->getCoordinates();
// Get coordinate of $pBefore
- list($beforeColumn, $beforeRow) = Coordinate::coordinateFromString($pBefore);
+ [$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($pBefore);
$beforeColumnIndex = Coordinate::columnIndexFromString($beforeColumn);
// Clear cells if we are removing columns or rows
@@ -539,7 +533,7 @@ class ReferenceHelper
$row = 0;
sscanf($pBefore, '%[A-Z]%d', $column, $row);
$columnIndex = Coordinate::columnIndexFromString($column);
- list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($autoFilterRange);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($autoFilterRange);
if ($columnIndex <= $rangeEnd[0]) {
if ($pNumCols < 0) {
// If we're actually deleting any columns that fall within the autofilter range,
@@ -707,7 +701,7 @@ class ReferenceHelper
if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
$toString = ($match[2] > '') ? $match[2] . '!' : '';
$toString .= $modified3 . ':' . $modified4;
- list($column, $row) = Coordinate::coordinateFromString($match[3]);
+ [$column, $row] = Coordinate::coordinateFromString($match[3]);
// Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
$column = Coordinate::columnIndexFromString(trim($column, '$')) + 100000;
$row = trim($row, '$') + 10000000;
@@ -733,7 +727,7 @@ class ReferenceHelper
if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
$toString = ($match[2] > '') ? $match[2] . '!' : '';
$toString .= $modified3;
- list($column, $row) = Coordinate::coordinateFromString($match[3]);
+ [$column, $row] = Coordinate::coordinateFromString($match[3]);
// Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
$column = Coordinate::columnIndexFromString(trim($column, '$')) + 100000;
$row = trim($row, '$') + 10000000;
@@ -881,10 +875,10 @@ class ReferenceHelper
}
// Get coordinate of $pBefore
- list($beforeColumn, $beforeRow) = Coordinate::coordinateFromString($pBefore);
+ [$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($pBefore);
// Get coordinate of $pCellReference
- list($newColumn, $newRow) = Coordinate::coordinateFromString($pCellReference);
+ [$newColumn, $newRow] = Coordinate::coordinateFromString($pCellReference);
// Verify which parts should be updated
$updateColumn = (($newColumn[0] != '$') && ($beforeColumn[0] != '$') && (Coordinate::columnIndexFromString($newColumn) >= Coordinate::columnIndexFromString($beforeColumn)));
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php
index 76a04d549f5..6e90fa355fa 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php
@@ -47,7 +47,7 @@ class RichText implements IComparable
*
* @param ITextElement $pText Rich text element
*
- * @return RichText
+ * @return $this
*/
public function addText(ITextElement $pText)
{
@@ -133,7 +133,7 @@ class RichText implements IComparable
*
* @param ITextElement[] $textElements Array of elements
*
- * @return RichText
+ * @return $this
*/
public function setRichTextElements(array $textElements)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php
index b4996235f33..aa4a8e462d3 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php
@@ -40,7 +40,7 @@ class Run extends TextElement implements ITextElement
*
* @param Font $pFont Font
*
- * @return ITextElement
+ * @return $this
*/
public function setFont(Font $pFont = null)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php
index d9ad0d7f5ed..f8be5d55b0e 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php
@@ -37,7 +37,7 @@ class TextElement implements ITextElement
*
* @param $text string Text
*
- * @return ITextElement
+ * @return $this
*/
public function setText($text)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
index c297dd682ea..03fa6ac2cbe 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
@@ -30,7 +30,6 @@ class Settings
* 7.2 < 7.2.1
* 7.1 < 7.1.13
* 7.0 < 7.0.27
- * 5.6 ANY
* then you may need to disable this check to prevent unwanted behaviour in other threads
* SECURITY WARNING: Changing this flag is not recommended.
*
@@ -122,7 +121,6 @@ class Settings
* 7.2 < 7.2.1
* 7.1 < 7.1.13
* 7.0 < 7.0.27
- * 5.6 ANY
* then you may need to disable this check to prevent unwanted behaviour in other threads
* SECURITY WARNING: Changing this flag to false is not recommended.
*
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php
index 8abcef2e192..bee13e2921e 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php
@@ -298,9 +298,7 @@ class Font
$upperLeftCornerX = $textBox[6];
// Consider the rotation when calculating the width
- $textWidth = max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX);
-
- return $textWidth;
+ return max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX);
}
/**
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php
index b3d82d466cc..a67b6c2de52 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php
@@ -174,7 +174,7 @@ class Matrix
switch ($match) {
//A($i0...; $j0...)
case 'integer,integer':
- list($i0, $j0) = $args;
+ [$i0, $j0] = $args;
if ($i0 >= 0) {
$m = $this->m - $i0;
} else {
@@ -197,7 +197,7 @@ class Matrix
break;
//A($i0...$iF; $j0...$jF)
case 'integer,integer,integer,integer':
- list($i0, $iF, $j0, $jF) = $args;
+ [$i0, $iF, $j0, $jF] = $args;
if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) {
$m = $iF - $i0;
} else {
@@ -220,7 +220,7 @@ class Matrix
break;
//$R = array of row indices; $C = array of column indices
case 'array,array':
- list($RL, $CL) = $args;
+ [$RL, $CL] = $args;
if (count($RL) > 0) {
$m = count($RL);
} else {
@@ -243,7 +243,7 @@ class Matrix
break;
//A($i0...$iF); $CL = array of column indices
case 'integer,integer,array':
- list($i0, $iF, $CL) = $args;
+ [$i0, $iF, $CL] = $args;
if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) {
$m = $iF - $i0;
} else {
@@ -266,7 +266,7 @@ class Matrix
break;
//$RL = array of row indices
case 'array,integer,integer':
- list($RL, $j0, $jF) = $args;
+ [$RL, $j0, $jF] = $args;
if (count($RL) > 0) {
$m = count($RL);
} else {
@@ -524,7 +524,7 @@ class Matrix
*
* @param mixed $B Matrix/Array
*
- * @return Matrix Sum
+ * @return $this
*/
public function plusEquals(...$args)
{
@@ -628,7 +628,7 @@ class Matrix
*
* @param mixed $B Matrix/Array
*
- * @return Matrix Sum
+ * @return $this
*/
public function minusEquals(...$args)
{
@@ -734,7 +734,7 @@ class Matrix
*
* @param mixed $B Matrix/Array
*
- * @return Matrix Matrix Aij
+ * @return $this
*/
public function arrayTimesEquals(...$args)
{
@@ -1091,7 +1091,7 @@ class Matrix
*
* @param mixed $B Matrix/Array
*
- * @return Matrix Sum
+ * @return $this
*/
public function power(...$args)
{
@@ -1150,7 +1150,7 @@ class Matrix
*
* @param mixed $B Matrix/Array
*
- * @return Matrix Sum
+ * @return $this
*/
public function concat(...$args)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php
index e666d74b6a3..3bb8a10ef91 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php
@@ -60,7 +60,7 @@ class QRDecomposition
{
if ($A instanceof Matrix) {
// Initialize.
- $this->QR = $A->getArrayCopy();
+ $this->QR = $A->getArray();
$this->m = $A->getRowDimension();
$this->n = $A->getColumnDimension();
// Main loop.
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php
index f09fbc92adc..efa7522fad6 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php
@@ -253,7 +253,7 @@ class OLE
*/
private static function _readInt1($fh)
{
- list(, $tmp) = unpack('c', fread($fh, 1));
+ [, $tmp] = unpack('c', fread($fh, 1));
return $tmp;
}
@@ -267,7 +267,7 @@ class OLE
*/
private static function _readInt2($fh)
{
- list(, $tmp) = unpack('v', fread($fh, 2));
+ [, $tmp] = unpack('v', fread($fh, 2));
return $tmp;
}
@@ -281,7 +281,7 @@ class OLE
*/
private static function _readInt4($fh)
{
- list(, $tmp) = unpack('V', fread($fh, 4));
+ [, $tmp] = unpack('V', fread($fh, 4));
return $tmp;
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
index 578e08fafe3..c52cea239fd 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
@@ -118,7 +118,7 @@ class Root extends PPS
$aList = [];
PPS::_savePpsSetPnt($aList, [$this]);
// calculate values for header
- list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo);
+ [$iSBDcnt, $iBBcnt, $iPPScnt] = $this->_calcSize($aList); //, $rhInfo);
// Save Header
$this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt);
@@ -149,7 +149,7 @@ class Root extends PPS
public function _calcSize(&$raList)
{
// Calculate Basic Setting
- list($iSBDcnt, $iBBcnt, $iPPScnt) = [0, 0, 0];
+ [$iSBDcnt, $iBBcnt, $iPPScnt] = [0, 0, 0];
$iSmallLen = 0;
$iSBcnt = 0;
$iCount = count($raList);
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php
index a5702775074..d949203babc 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php
@@ -430,9 +430,7 @@ class StringHelper
// characters
$chars = self::convertEncoding($value, 'UTF-16LE', 'UTF-8');
- $data = pack('vC', $ln, 0x0001) . $chars;
-
- return $data;
+ return pack('vC', $ln, 0x0001) . $chars;
}
/**
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php
index 177779f8f10..b8ce5e2daa8 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php
@@ -211,7 +211,7 @@ class Xls
*/
public static function oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height)
{
- list($column, $row) = Coordinate::coordinateFromString($coordinates);
+ [$column, $row] = Coordinate::coordinateFromString($coordinates);
$col_start = Coordinate::columnIndexFromString($column);
$row_start = $row - 1;
@@ -269,7 +269,7 @@ class Xls
$startCoordinates = Coordinate::stringFromColumnIndex($col_start) . ($row_start + 1);
$endCoordinates = Coordinate::stringFromColumnIndex($col_end) . ($row_end + 1);
- $twoAnchor = [
+ return [
'startCoordinates' => $startCoordinates,
'startOffsetX' => $x1,
'startOffsetY' => $y1,
@@ -277,7 +277,5 @@ class Xls
'endOffsetX' => $x2,
'endOffsetY' => $y2,
];
-
- return $twoAnchor;
}
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
index 8eadcbe8b1a..d33a98719fd 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
@@ -721,7 +721,7 @@ class Spreadsheet
{
$worksheetCount = count($this->workSheetCollection);
for ($i = 0; $i < $worksheetCount; ++$i) {
- if ($this->workSheetCollection[$i]->getTitle() === $pName) {
+ if ($this->workSheetCollection[$i]->getTitle() === trim($pName, "'")) {
return $this->workSheetCollection[$i];
}
}
@@ -955,7 +955,7 @@ class Spreadsheet
* @param string $namedRange
* @param null|Worksheet $pSheet scope: use null for global scope
*
- * @return Spreadsheet
+ * @return $this
*/
public function removeNamedRange($namedRange, Worksheet $pSheet = null)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php
index b4df792b167..5eb7c2b0f47 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php
@@ -140,7 +140,7 @@ class Alignment extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Alignment
+ * @return $this
*/
public function applyFromArray(array $pStyles)
{
@@ -193,7 +193,7 @@ class Alignment extends Supervisor
*
* @param string $pValue see self::HORIZONTAL_*
*
- * @return Alignment
+ * @return $this
*/
public function setHorizontal($pValue)
{
@@ -230,7 +230,7 @@ class Alignment extends Supervisor
*
* @param string $pValue see self::VERTICAL_*
*
- * @return Alignment
+ * @return $this
*/
public function setVertical($pValue)
{
@@ -269,7 +269,7 @@ class Alignment extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Alignment
+ * @return $this
*/
public function setTextRotation($pValue)
{
@@ -312,7 +312,7 @@ class Alignment extends Supervisor
*
* @param bool $pValue
*
- * @return Alignment
+ * @return $this
*/
public function setWrapText($pValue)
{
@@ -348,7 +348,7 @@ class Alignment extends Supervisor
*
* @param bool $pValue
*
- * @return Alignment
+ * @return $this
*/
public function setShrinkToFit($pValue)
{
@@ -384,7 +384,7 @@ class Alignment extends Supervisor
*
* @param int $pValue
*
- * @return Alignment
+ * @return $this
*/
public function setIndent($pValue)
{
@@ -424,7 +424,7 @@ class Alignment extends Supervisor
*
* @param int $pValue
*
- * @return Alignment
+ * @return $this
*/
public function setReadOrder($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php
index c957cf595a9..5fa0cae70d9 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php
@@ -127,7 +127,7 @@ class Border extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Border
+ * @return $this
*/
public function applyFromArray(array $pStyles)
{
@@ -166,7 +166,7 @@ class Border extends Supervisor
* When passing a boolean, FALSE equates Border::BORDER_NONE
* and TRUE to Border::BORDER_MEDIUM
*
- * @return Border
+ * @return $this
*/
public function setBorderStyle($pValue)
{
@@ -202,7 +202,7 @@ class Border extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Border
+ * @return $this
*/
public function setColor(Color $pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php
index a1d6759b936..8f005a9980a 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php
@@ -197,7 +197,7 @@ class Borders extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Borders
+ * @return $this
*/
public function applyFromArray(array $pStyles)
{
@@ -382,7 +382,7 @@ class Borders extends Supervisor
*
* @param int $pValue see self::DIAGONAL_*
*
- * @return Borders
+ * @return $this
*/
public function setDiagonalDirection($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php
index 8a1812d20b7..ab22cbe3248 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php
@@ -6,6 +6,17 @@ use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
class Color extends Supervisor
{
+ const NAMED_COLORS = [
+ 'Black',
+ 'White',
+ 'Red',
+ 'Green',
+ 'Blue',
+ 'Yellow',
+ 'Magenta',
+ 'Cyan',
+ ];
+
// Colors
const COLOR_BLACK = 'FF000000';
const COLOR_WHITE = 'FFFFFFFF';
@@ -95,7 +106,7 @@ class Color extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Color
+ * @return $this
*/
public function applyFromArray(array $pStyles)
{
@@ -132,7 +143,7 @@ class Color extends Supervisor
*
* @param string $pValue see self::COLOR_*
*
- * @return Color
+ * @return $this
*/
public function setARGB($pValue)
{
@@ -168,7 +179,7 @@ class Color extends Supervisor
*
* @param string $pValue RGB value
*
- * @return Color
+ * @return $this
*/
public function setRGB($pValue)
{
@@ -198,11 +209,8 @@ class Color extends Supervisor
private static function getColourComponent($RGB, $offset, $hex = true)
{
$colour = substr($RGB, $offset, 2);
- if (!$hex) {
- $colour = hexdec($colour);
- }
- return $colour;
+ return ($hex) ? $colour : hexdec($colour);
}
/**
@@ -257,7 +265,7 @@ class Color extends Supervisor
*/
public static function changeBrightness($hex, $adjustPercentage)
{
- $rgba = (strlen($hex) == 8);
+ $rgba = (strlen($hex) === 8);
$red = self::getRed($hex, false);
$green = self::getGreen($hex, false);
@@ -289,9 +297,9 @@ class Color extends Supervisor
}
$rgb = strtoupper(
- str_pad(dechex($red), 2, '0', 0) .
- str_pad(dechex($green), 2, '0', 0) .
- str_pad(dechex($blue), 2, '0', 0)
+ str_pad(dechex((int) $red), 2, '0', 0) .
+ str_pad(dechex((int) $green), 2, '0', 0) .
+ str_pad(dechex((int) $blue), 2, '0', 0)
);
return (($rgba) ? 'FF' : '') . $rgb;
@@ -304,7 +312,7 @@ class Color extends Supervisor
* @param bool $background Flag to indicate whether default background or foreground colour
* should be returned if the indexed colour doesn't exist
*
- * @return Color
+ * @return self
*/
public static function indexedColor($pIndex, $background = false)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php
index 91a000dba4a..ec8c858b03b 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php
@@ -12,6 +12,7 @@ class Conditional implements IComparable
const CONDITION_CONTAINSTEXT = 'containsText';
const CONDITION_EXPRESSION = 'expression';
const CONDITION_CONTAINSBLANKS = 'containsBlanks';
+ const CONDITION_NOTCONTAINSBLANKS = 'notContainsBlanks';
// Operator types
const OPERATOR_NONE = '';
@@ -93,7 +94,7 @@ class Conditional implements IComparable
*
* @param string $pValue Condition type, see self::CONDITION_*
*
- * @return Conditional
+ * @return $this
*/
public function setConditionType($pValue)
{
@@ -117,7 +118,7 @@ class Conditional implements IComparable
*
* @param string $pValue Conditional operator type, see self::OPERATOR_*
*
- * @return Conditional
+ * @return $this
*/
public function setOperatorType($pValue)
{
@@ -141,7 +142,7 @@ class Conditional implements IComparable
*
* @param string $value
*
- * @return Conditional
+ * @return $this
*/
public function setText($value)
{
@@ -165,7 +166,7 @@ class Conditional implements IComparable
*
* @param bool $value
*
- * @return Conditional
+ * @return $this
*/
public function setStopIfTrue($value)
{
@@ -189,7 +190,7 @@ class Conditional implements IComparable
*
* @param string[] $pValue Condition
*
- * @return Conditional
+ * @return $this
*/
public function setConditions($pValue)
{
@@ -206,7 +207,7 @@ class Conditional implements IComparable
*
* @param string $pValue Condition
*
- * @return Conditional
+ * @return $this
*/
public function addCondition($pValue)
{
@@ -230,7 +231,7 @@ class Conditional implements IComparable
*
* @param Style $pValue
*
- * @return Conditional
+ * @return $this
*/
public function setStyle(Style $pValue = null)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php
index c2ad895ee12..1d6bace147a 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php
@@ -141,7 +141,7 @@ class Fill extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Fill
+ * @return $this
*/
public function applyFromArray(array $pStyles)
{
@@ -188,7 +188,7 @@ class Fill extends Supervisor
*
* @param string $pValue Fill type, see self::FILL_*
*
- * @return Fill
+ * @return $this
*/
public function setFillType($pValue)
{
@@ -221,7 +221,7 @@ class Fill extends Supervisor
*
* @param float $pValue
*
- * @return Fill
+ * @return $this
*/
public function setRotation($pValue)
{
@@ -252,7 +252,7 @@ class Fill extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Fill
+ * @return $this
*/
public function setStartColor(Color $pValue)
{
@@ -286,7 +286,7 @@ class Fill extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Fill
+ * @return $this
*/
public function setEndColor(Color $pValue)
{
@@ -313,12 +313,13 @@ class Fill extends Supervisor
if ($this->isSupervisor) {
return $this->getSharedComponent()->getHashCode();
}
-
+ // Note that we don't care about colours for fill type NONE, but could have duplicate NONEs with
+ // different hashes if we don't explicitly prevent this
return md5(
$this->getFillType() .
$this->getRotation() .
- $this->getStartColor()->getHashCode() .
- $this->getEndColor()->getHashCode() .
+ ($this->getFillType() !== self::FILL_NONE ? $this->getStartColor()->getHashCode() : '') .
+ ($this->getFillType() !== self::FILL_NONE ? $this->getEndColor()->getHashCode() : '') .
__CLASS__
);
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php
index 6d8e23b1954..0341cad0dc4 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php
@@ -161,7 +161,7 @@ class Font extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Font
+ * @return $this
*/
public function applyFromArray(array $pStyles)
{
@@ -219,7 +219,7 @@ class Font extends Supervisor
*
* @param string $pValue
*
- * @return Font
+ * @return $this
*/
public function setName($pValue)
{
@@ -255,7 +255,7 @@ class Font extends Supervisor
*
* @param float $pValue
*
- * @return Font
+ * @return $this
*/
public function setSize($pValue)
{
@@ -291,7 +291,7 @@ class Font extends Supervisor
*
* @param bool $pValue
*
- * @return Font
+ * @return $this
*/
public function setBold($pValue)
{
@@ -327,7 +327,7 @@ class Font extends Supervisor
*
* @param bool $pValue
*
- * @return Font
+ * @return $this
*/
public function setItalic($pValue)
{
@@ -363,7 +363,7 @@ class Font extends Supervisor
*
* @param bool $pValue
*
- * @return Font
+ * @return $this
*/
public function setSuperscript($pValue)
{
@@ -400,7 +400,7 @@ class Font extends Supervisor
*
* @param bool $pValue
*
- * @return Font
+ * @return $this
*/
public function setSubscript($pValue)
{
@@ -439,7 +439,7 @@ class Font extends Supervisor
* If a boolean is passed, then TRUE equates to UNDERLINE_SINGLE,
* false equates to UNDERLINE_NONE
*
- * @return Font
+ * @return $this
*/
public function setUnderline($pValue)
{
@@ -477,7 +477,7 @@ class Font extends Supervisor
*
* @param bool $pValue
*
- * @return Font
+ * @return $this
*/
public function setStrikethrough($pValue)
{
@@ -512,7 +512,7 @@ class Font extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Font
+ * @return $this
*/
public function setColor(Color $pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php
index 37442249a50..df4ca76f4ff 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php
@@ -23,8 +23,8 @@ class NumberFormat extends Supervisor
const FORMAT_PERCENTAGE_00 = '0.00%';
const FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd';
- const FORMAT_DATE_YYYYMMDD = 'yy-mm-dd';
- const FORMAT_DATE_DDMMYYYY = 'dd/mm/yy';
+ const FORMAT_DATE_YYYYMMDD = 'yyyy-mm-dd';
+ const FORMAT_DATE_DDMMYYYY = 'dd/mm/yyyy';
const FORMAT_DATE_DMYSLASH = 'd/m/yy';
const FORMAT_DATE_DMYMINUS = 'd-m-yy';
const FORMAT_DATE_DMMINUS = 'd-m';
@@ -43,7 +43,7 @@ class NumberFormat extends Supervisor
const FORMAT_DATE_TIME6 = 'h:mm:ss';
const FORMAT_DATE_TIME7 = 'i:s.S';
const FORMAT_DATE_TIME8 = 'h:mm:ss;@';
- const FORMAT_DATE_YYYYMMDDSLASH = 'yy/mm/dd;@';
+ const FORMAT_DATE_YYYYMMDDSLASH = 'yyyy/mm/dd;@';
const FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-';
const FORMAT_CURRENCY_USD = '$#,##0_-';
@@ -139,7 +139,7 @@ class NumberFormat extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return NumberFormat
+ * @return $this
*/
public function applyFromArray(array $pStyles)
{
@@ -176,7 +176,7 @@ class NumberFormat extends Supervisor
*
* @param string $pValue see self::FORMAT_*
*
- * @return NumberFormat
+ * @return $this
*/
public function setFormatCode($pValue)
{
@@ -213,7 +213,7 @@ class NumberFormat extends Supervisor
*
* @param int $pValue
*
- * @return NumberFormat
+ * @return $this
*/
public function setBuiltInFormatCode($pValue)
{
@@ -367,7 +367,7 @@ class NumberFormat extends Supervisor
self::fillBuiltInFormatCodes();
// Lookup format code
- if (isset(self::$flippedBuiltInFormats[$formatCode])) {
+ if (array_key_exists($formatCode, self::$flippedBuiltInFormats)) {
return self::$flippedBuiltInFormats[$formatCode];
}
@@ -551,24 +551,32 @@ class NumberFormat extends Supervisor
}
}
- private static function complexNumberFormatMask($number, $mask)
+ private static function mergeComplexNumberFormatMasks($numbers, $masks)
{
- $sign = ($number < 0.0);
- $number = abs($number);
- if (strpos($mask, '.') !== false) {
- $numbers = explode('.', $number . '.0');
- $masks = explode('.', $mask . '.0');
- $result1 = self::complexNumberFormatMask($numbers[0], $masks[0]);
- $result2 = strrev(self::complexNumberFormatMask(strrev($numbers[1]), strrev($masks[1])));
+ $decimalCount = strlen($numbers[1]);
+ $postDecimalMasks = [];
- return (($sign) ? '-' : '') . $result1 . '.' . $result2;
- }
+ do {
+ $tempMask = array_pop($masks);
+ $postDecimalMasks[] = $tempMask;
+ $decimalCount -= strlen($tempMask);
+ } while ($decimalCount > 0);
- $r = preg_match_all('/0+/', $mask, $result, PREG_OFFSET_CAPTURE);
- if ($r > 1) {
- $result = array_reverse($result[0]);
+ return [
+ implode('.', $masks),
+ implode('.', array_reverse($postDecimalMasks)),
+ ];
+ }
- foreach ($result as $block) {
+ private static function processComplexNumberFormatMask($number, $mask)
+ {
+ $result = $number;
+ $maskingBlockCount = preg_match_all('/0+/', $mask, $maskingBlocks, PREG_OFFSET_CAPTURE);
+
+ if ($maskingBlockCount > 1) {
+ $maskingBlocks = array_reverse($maskingBlocks[0]);
+
+ foreach ($maskingBlocks as $block) {
$divisor = 1 . $block[0];
$size = strlen($block[0]);
$offset = $block[1];
@@ -584,13 +592,134 @@ class NumberFormat extends Supervisor
$mask = substr_replace($mask, $number, $offset, 0);
}
$result = $mask;
- } else {
- $result = $number;
}
+ return $result;
+ }
+
+ private static function complexNumberFormatMask($number, $mask, $splitOnPoint = true)
+ {
+ $sign = ($number < 0.0);
+ $number = abs($number);
+
+ if ($splitOnPoint && strpos($mask, '.') !== false && strpos($number, '.') !== false) {
+ $numbers = explode('.', $number);
+ $masks = explode('.', $mask);
+ if (count($masks) > 2) {
+ $masks = self::mergeComplexNumberFormatMasks($numbers, $masks);
+ }
+ $result1 = self::complexNumberFormatMask($numbers[0], $masks[0], false);
+ $result2 = strrev(self::complexNumberFormatMask(strrev($numbers[1]), strrev($masks[1]), false));
+
+ return (($sign) ? '-' : '') . $result1 . '.' . $result2;
+ }
+
+ $result = self::processComplexNumberFormatMask($number, $mask);
+
return (($sign) ? '-' : '') . $result;
}
+ private static function formatStraightNumericValue($value, $format, array $matches, $useThousands, $number_regex)
+ {
+ $left = $matches[1];
+ $dec = $matches[2];
+ $right = $matches[3];
+
+ // minimun width of formatted number (including dot)
+ $minWidth = strlen($left) + strlen($dec) + strlen($right);
+ if ($useThousands) {
+ $value = number_format(
+ $value,
+ strlen($right),
+ StringHelper::getDecimalSeparator(),
+ StringHelper::getThousandsSeparator()
+ );
+ $value = preg_replace($number_regex, $value, $format);
+ } else {
+ if (preg_match('/[0#]E[+-]0/i', $format)) {
+ // Scientific format
+ $value = sprintf('%5.2E', $value);
+ } elseif (preg_match('/0([^\d\.]+)0/', $format) || substr_count($format, '.') > 1) {
+ if ($value == (int) $value && substr_count($format, '.') === 1) {
+ $value *= 10 ** strlen(explode('.', $format)[1]);
+ }
+ $value = self::complexNumberFormatMask($value, $format);
+ } else {
+ $sprintf_pattern = "%0$minWidth." . strlen($right) . 'f';
+ $value = sprintf($sprintf_pattern, $value);
+ $value = preg_replace($number_regex, $value, $format);
+ }
+ }
+
+ return $value;
+ }
+
+ private static function formatAsNumber($value, $format)
+ {
+ if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) {
+ return 'EUR ' . sprintf('%1.2f', $value);
+ }
+
+ // Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
+ $format = str_replace(['"', '*'], '', $format);
+
+ // Find out if we need thousands separator
+ // This is indicated by a comma enclosed by a digit placeholder:
+ // #,# or 0,0
+ $useThousands = preg_match('/(#,#|0,0)/', $format);
+ if ($useThousands) {
+ $format = preg_replace('/0,0/', '00', $format);
+ $format = preg_replace('/#,#/', '##', $format);
+ }
+
+ // Scale thousands, millions,...
+ // This is indicated by a number of commas after a digit placeholder:
+ // #, or 0.0,,
+ $scale = 1; // same as no scale
+ $matches = [];
+ if (preg_match('/(#|0)(,+)/', $format, $matches)) {
+ $scale = pow(1000, strlen($matches[2]));
+
+ // strip the commas
+ $format = preg_replace('/0,+/', '0', $format);
+ $format = preg_replace('/#,+/', '#', $format);
+ }
+
+ if (preg_match('/#?.*\?\/\?/', $format, $m)) {
+ if ($value != (int) $value) {
+ self::formatAsFraction($value, $format);
+ }
+ } else {
+ // Handle the number itself
+
+ // scale number
+ $value = $value / $scale;
+ // Strip #
+ $format = preg_replace('/\\#/', '0', $format);
+ // Remove locale code [$-###]
+ $format = preg_replace('/\[\$\-.*\]/', '', $format);
+
+ $n = '/\\[[^\\]]+\\]/';
+ $m = preg_replace($n, '', $format);
+ $number_regex = '/(0+)(\\.?)(0*)/';
+ if (preg_match($number_regex, $m, $matches)) {
+ $value = self::formatStraightNumericValue($value, $format, $matches, $useThousands, $number_regex);
+ }
+ }
+
+ if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
+ // Currency or Accounting
+ $currencyCode = $m[1];
+ [$currencyCode] = explode('-', $currencyCode);
+ if ($currencyCode == '') {
+ $currencyCode = StringHelper::getCurrencyCode();
+ }
+ $value = preg_replace('/\[\$([^\]]*)\]/u', $currencyCode, $value);
+ }
+
+ return $value;
+ }
+
/**
* Convert a value in a pre-defined format to a PHP string.
*
@@ -660,7 +789,9 @@ class NumberFormat extends Supervisor
// Save format with color information for later use below
$formatColor = $format;
-
+ // Strip colour information
+ $color_regex = '/\[(' . implode('|', Color::NAMED_COLORS) . ')\]/';
+ $format = preg_replace($color_regex, '', $format);
// Let's begin inspecting the format and converting the value to a formatted string
// Check for date/time characters (not inside quotes)
@@ -668,105 +799,19 @@ class NumberFormat extends Supervisor
// datetime format
self::formatAsDate($value, $format);
} else {
- // Strip color information
- $color_regex = '/^\\[[a-zA-Z]+\\]/';
- $format = preg_replace($color_regex, '', $format);
- if (preg_match('/%$/', $format)) {
+ if (substr($format, 0, 1) === '"' && substr($format, -1, 1) === '"') {
+ $value = substr($format, 1, -1);
+ } elseif (preg_match('/%$/', $format)) {
// % number format
self::formatAsPercentage($value, $format);
} else {
- if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) {
- $value = 'EUR ' . sprintf('%1.2f', $value);
- } else {
- // Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
- $format = str_replace(['"', '*'], '', $format);
-
- // Find out if we need thousands separator
- // This is indicated by a comma enclosed by a digit placeholder:
- // #,# or 0,0
- $useThousands = preg_match('/(#,#|0,0)/', $format);
- if ($useThousands) {
- $format = preg_replace('/0,0/', '00', $format);
- $format = preg_replace('/#,#/', '##', $format);
- }
-
- // Scale thousands, millions,...
- // This is indicated by a number of commas after a digit placeholder:
- // #, or 0.0,,
- $scale = 1; // same as no scale
- $matches = [];
- if (preg_match('/(#|0)(,+)/', $format, $matches)) {
- $scale = pow(1000, strlen($matches[2]));
-
- // strip the commas
- $format = preg_replace('/0,+/', '0', $format);
- $format = preg_replace('/#,+/', '#', $format);
- }
-
- if (preg_match('/#?.*\?\/\?/', $format, $m)) {
- if ($value != (int) $value) {
- self::formatAsFraction($value, $format);
- }
- } else {
- // Handle the number itself
-
- // scale number
- $value = $value / $scale;
-
- // Strip #
- $format = preg_replace('/\\#/', '0', $format);
-
- // Remove locale code [$-###]
- $format = preg_replace('/\[\$\-.*\]/', '', $format);
-
- $n = '/\\[[^\\]]+\\]/';
- $m = preg_replace($n, '', $format);
- $number_regex = '/(0+)(\\.?)(0*)/';
- if (preg_match($number_regex, $m, $matches)) {
- $left = $matches[1];
- $dec = $matches[2];
- $right = $matches[3];
-
- // minimun width of formatted number (including dot)
- $minWidth = strlen($left) + strlen($dec) + strlen($right);
- if ($useThousands) {
- $value = number_format(
- $value,
- strlen($right),
- StringHelper::getDecimalSeparator(),
- StringHelper::getThousandsSeparator()
- );
- $value = preg_replace($number_regex, $value, $format);
- } else {
- if (preg_match('/[0#]E[+-]0/i', $format)) {
- // Scientific format
- $value = sprintf('%5.2E', $value);
- } elseif (preg_match('/0([^\d\.]+)0/', $format)) {
- $value = self::complexNumberFormatMask($value, $format);
- } else {
- $sprintf_pattern = "%0$minWidth." . strlen($right) . 'f';
- $value = sprintf($sprintf_pattern, $value);
- $value = preg_replace($number_regex, $value, $format);
- }
- }
- }
- }
- if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
- // Currency or Accounting
- $currencyCode = $m[1];
- list($currencyCode) = explode('-', $currencyCode);
- if ($currencyCode == '') {
- $currencyCode = StringHelper::getCurrencyCode();
- }
- $value = preg_replace('/\[\$([^\]]*)\]/u', $currencyCode, $value);
- }
- }
+ $value = self::formatAsNumber($value, $format);
}
}
// Additional formatting provided by callback function
if ($callBack !== null) {
- list($writerInstance, $function) = $callBack;
+ [$writerInstance, $function] = $callBack;
$value = $writerInstance->$function($value, $formatColor);
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php
index b5feb53486e..8112bc62049 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php
@@ -86,7 +86,7 @@ class Protection extends Supervisor
*
* @throws PhpSpreadsheetException
*
- * @return Protection
+ * @return $this
*/
public function applyFromArray(array $pStyles)
{
@@ -123,7 +123,7 @@ class Protection extends Supervisor
*
* @param string $pValue see self::PROTECTION_*
*
- * @return Protection
+ * @return $this
*/
public function setLocked($pValue)
{
@@ -156,7 +156,7 @@ class Protection extends Supervisor
*
* @param string $pValue see self::PROTECTION_*
*
- * @return Protection
+ * @return $this
*/
public function setHidden($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php
index fbe07c2cdd2..a37d99b5f58 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php
@@ -189,7 +189,7 @@ class Style extends Supervisor
* @param array $pStyles Array containing style information
* @param bool $pAdvanced advanced mode for setting borders
*
- * @return Style
+ * @return $this
*/
public function applyFromArray(array $pStyles, $pAdvanced = true)
{
@@ -204,7 +204,7 @@ class Style extends Supervisor
$rangeA = $pRange;
$rangeB = $pRange;
} else {
- list($rangeA, $rangeB) = explode(':', $pRange);
+ [$rangeA, $rangeB] = explode(':', $pRange);
}
// Calculate range outer borders
@@ -485,7 +485,7 @@ class Style extends Supervisor
*
* @param Font $font
*
- * @return Style
+ * @return $this
*/
public function setFont(Font $font)
{
@@ -539,7 +539,7 @@ class Style extends Supervisor
*
* @param Conditional[] $pValue Array of conditional styles
*
- * @return Style
+ * @return $this
*/
public function setConditionalStyles(array $pValue)
{
@@ -577,7 +577,7 @@ class Style extends Supervisor
*
* @param bool $pValue
*
- * @return Style
+ * @return $this
*/
public function setQuotePrefix($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php
index 2d1a27266b1..1a700974b72 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php
@@ -48,7 +48,7 @@ abstract class Supervisor implements IComparable
* @param Spreadsheet|Style $parent
* @param null|string $parentPropertyName
*
- * @return Supervisor
+ * @return $this
*/
public function bindParent($parent, $parentPropertyName = null)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php
index 494948a77f2..dcbc4da5b43 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php
@@ -59,7 +59,7 @@ class AutoFilter
*
* @param Worksheet $pSheet
*
- * @return AutoFilter
+ * @return $this
*/
public function setParent(Worksheet $pSheet = null)
{
@@ -85,12 +85,12 @@ class AutoFilter
*
* @throws PhpSpreadsheetException
*
- * @return AutoFilter
+ * @return $this
*/
public function setRange($pRange)
{
// extract coordinate
- list($worksheet, $pRange) = Worksheet::extractSheetTitle($pRange, true);
+ [$worksheet, $pRange] = Worksheet::extractSheetTitle($pRange, true);
if (strpos($pRange, ':') !== false) {
$this->range = $pRange;
@@ -105,7 +105,7 @@ class AutoFilter
$this->columns = [];
} else {
// Discard any column rules that are no longer valid within this range
- list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($this->range);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
foreach ($this->columns as $key => $value) {
$colIndex = Coordinate::columnIndexFromString($key);
if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) {
@@ -143,7 +143,7 @@ class AutoFilter
}
$columnIndex = Coordinate::columnIndexFromString($column);
- list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($this->range);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
if (($rangeStart[0] > $columnIndex) || ($rangeEnd[0] < $columnIndex)) {
throw new PhpSpreadsheetException('Column is outside of current autofilter range.');
}
@@ -196,7 +196,7 @@ class AutoFilter
*/
public function getColumnByOffset($pColumnOffset)
{
- list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($this->range);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
$pColumn = Coordinate::stringFromColumnIndex($rangeStart[0] + $pColumnOffset);
return $this->getColumn($pColumn);
@@ -210,7 +210,7 @@ class AutoFilter
*
* @throws PhpSpreadsheetException
*
- * @return AutoFilter
+ * @return $this
*/
public function setColumn($pColumn)
{
@@ -241,7 +241,7 @@ class AutoFilter
*
* @throws PhpSpreadsheetException
*
- * @return AutoFilter
+ * @return $this
*/
public function clearColumn($pColumn)
{
@@ -264,7 +264,7 @@ class AutoFilter
* @param string $fromColumn Column name (e.g. A)
* @param string $toColumn Column name (e.g. B)
*
- * @return AutoFilter
+ * @return $this
*/
public function shiftColumn($fromColumn, $toColumn)
{
@@ -357,7 +357,7 @@ class AutoFilter
{
$dataSet = $ruleSet['filterRules'];
$join = $ruleSet['join'];
- $customRuleForBlanks = isset($ruleSet['customRuleForBlanks']) ? $ruleSet['customRuleForBlanks'] : false;
+ $customRuleForBlanks = $ruleSet['customRuleForBlanks'] ?? false;
if (!$customRuleForBlanks) {
// Blank cells are always ignored, so return a FALSE
@@ -617,11 +617,11 @@ class AutoFilter
*
* @throws PhpSpreadsheetException
*
- * @return AutoFilter
+ * @return $this
*/
public function showHideRows()
{
- list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($this->range);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
// The heading row should always be visible
$this->workSheet->getRowDimension($rangeStart[1])->setVisible(true);
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
index b5ab61e9713..3ed7270a147 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
@@ -101,7 +101,7 @@ class Column
}
/**
- * Get AutoFilter Column Index.
+ * Get AutoFilter column index as string eg: 'A'.
*
* @return string
*/
@@ -111,13 +111,13 @@ class Column
}
/**
- * Set AutoFilter Column Index.
+ * Set AutoFilter column index as string eg: 'A'.
*
* @param string $pColumn Column (e.g. A)
*
* @throws PhpSpreadsheetException
*
- * @return Column
+ * @return $this
*/
public function setColumnIndex($pColumn)
{
@@ -147,7 +147,7 @@ class Column
*
* @param AutoFilter $pParent
*
- * @return Column
+ * @return $this
*/
public function setParent(AutoFilter $pParent = null)
{
@@ -173,7 +173,7 @@ class Column
*
* @throws PhpSpreadsheetException
*
- * @return Column
+ * @return $this
*/
public function setFilterType($pFilterType)
{
@@ -203,7 +203,7 @@ class Column
*
* @throws PhpSpreadsheetException
*
- * @return Column
+ * @return $this
*/
public function setJoin($pJoin)
{
@@ -223,7 +223,7 @@ class Column
*
* @param string[] $attributes
*
- * @return Column
+ * @return $this
*/
public function setAttributes(array $attributes)
{
@@ -238,7 +238,7 @@ class Column
* @param string $pName Attribute Name
* @param string $pValue Attribute Value
*
- * @return Column
+ * @return $this
*/
public function setAttribute($pName, $pValue)
{
@@ -316,7 +316,7 @@ class Column
*
* @param Column\Rule $pRule
*
- * @return Column
+ * @return $this
*/
public function addRule(Column\Rule $pRule)
{
@@ -332,7 +332,7 @@ class Column
*
* @param int $pIndex Rule index in the ruleset array
*
- * @return Column
+ * @return $this
*/
public function deleteRule($pIndex)
{
@@ -350,7 +350,7 @@ class Column
/**
* Delete all AutoFilter Column Rules.
*
- * @return Column
+ * @return $this
*/
public function clearRules()
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
index 450bccdb6ac..09a2bacdd9b 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
@@ -262,7 +262,7 @@ class Rule
*
* @throws PhpSpreadsheetException
*
- * @return Rule
+ * @return $this
*/
public function setRuleType($pRuleType)
{
@@ -292,7 +292,7 @@ class Rule
*
* @throws PhpSpreadsheetException
*
- * @return Rule
+ * @return $this
*/
public function setValue($pValue)
{
@@ -336,7 +336,7 @@ class Rule
*
* @throws PhpSpreadsheetException
*
- * @return Rule
+ * @return $this
*/
public function setOperator($pOperator)
{
@@ -369,7 +369,7 @@ class Rule
*
* @throws PhpSpreadsheetException
*
- * @return Rule
+ * @return $this
*/
public function setGrouping($pGrouping)
{
@@ -393,7 +393,7 @@ class Rule
*
* @throws PhpSpreadsheetException
*
- * @return Rule
+ * @return $this
*/
public function setRule($pOperator, $pValue, $pGrouping = null)
{
@@ -424,7 +424,7 @@ class Rule
*
* @param Column $pParent
*
- * @return Rule
+ * @return $this
*/
public function setParent(Column $pParent = null)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
index 98b689729e1..7d24e4496bd 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
@@ -154,7 +154,7 @@ class BaseDrawing implements IComparable
*
* @param string $pValue
*
- * @return BaseDrawing
+ * @return $this
*/
public function setName($pValue)
{
@@ -178,7 +178,7 @@ class BaseDrawing implements IComparable
*
* @param string $description
*
- * @return BaseDrawing
+ * @return $this
*/
public function setDescription($description)
{
@@ -205,7 +205,7 @@ class BaseDrawing implements IComparable
*
* @throws PhpSpreadsheetException
*
- * @return BaseDrawing
+ * @return $this
*/
public function setWorksheet(Worksheet $pValue = null, $pOverrideOld = false)
{
@@ -253,7 +253,7 @@ class BaseDrawing implements IComparable
*
* @param string $pValue eg: 'A1'
*
- * @return BaseDrawing
+ * @return $this
*/
public function setCoordinates($pValue)
{
@@ -277,7 +277,7 @@ class BaseDrawing implements IComparable
*
* @param int $pValue
*
- * @return BaseDrawing
+ * @return $this
*/
public function setOffsetX($pValue)
{
@@ -301,7 +301,7 @@ class BaseDrawing implements IComparable
*
* @param int $pValue
*
- * @return BaseDrawing
+ * @return $this
*/
public function setOffsetY($pValue)
{
@@ -325,7 +325,7 @@ class BaseDrawing implements IComparable
*
* @param int $pValue
*
- * @return BaseDrawing
+ * @return $this
*/
public function setWidth($pValue)
{
@@ -356,7 +356,7 @@ class BaseDrawing implements IComparable
*
* @param int $pValue
*
- * @return BaseDrawing
+ * @return $this
*/
public function setHeight($pValue)
{
@@ -386,7 +386,7 @@ class BaseDrawing implements IComparable
* @param int $width
* @param int $height
*
- * @return BaseDrawing
+ * @return $this
*/
public function setWidthAndHeight($width, $height)
{
@@ -423,7 +423,7 @@ class BaseDrawing implements IComparable
*
* @param bool $pValue
*
- * @return BaseDrawing
+ * @return $this
*/
public function setResizeProportional($pValue)
{
@@ -447,7 +447,7 @@ class BaseDrawing implements IComparable
*
* @param int $pValue
*
- * @return BaseDrawing
+ * @return $this
*/
public function setRotation($pValue)
{
@@ -471,7 +471,7 @@ class BaseDrawing implements IComparable
*
* @param Drawing\Shadow $pValue
*
- * @return BaseDrawing
+ * @return $this
*/
public function setShadow(Drawing\Shadow $pValue = null)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php
index 4baaae1f54a..098967f726d 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php
@@ -40,7 +40,7 @@ class Column
}
/**
- * Get column index.
+ * Get column index as string eg: 'A'.
*
* @return string
*/
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php
index 7e8f040d26a..d75da898814 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php
@@ -59,7 +59,7 @@ class ColumnCellIterator extends CellIterator
*
* @throws PhpSpreadsheetException
*
- * @return ColumnCellIterator
+ * @return $this
*/
public function resetStart($startRow = 1)
{
@@ -77,7 +77,7 @@ class ColumnCellIterator extends CellIterator
*
* @throws PhpSpreadsheetException
*
- * @return ColumnCellIterator
+ * @return $this
*/
public function resetEnd($endRow = null)
{
@@ -94,7 +94,7 @@ class ColumnCellIterator extends CellIterator
*
* @throws PhpSpreadsheetException
*
- * @return ColumnCellIterator
+ * @return $this
*/
public function seek($row = 1)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php
index e2ea8af15f7..4e87a344c80 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php
@@ -42,7 +42,7 @@ class ColumnDimension extends Dimension
}
/**
- * Get ColumnIndex.
+ * Get column index as string eg: 'A'.
*
* @return string
*/
@@ -52,11 +52,11 @@ class ColumnDimension extends Dimension
}
/**
- * Set ColumnIndex.
+ * Set column index as string eg: 'A'.
*
* @param string $pValue
*
- * @return ColumnDimension
+ * @return $this
*/
public function setColumnIndex($pValue)
{
@@ -80,7 +80,7 @@ class ColumnDimension extends Dimension
*
* @param float $pValue
*
- * @return ColumnDimension
+ * @return $this
*/
public function setWidth($pValue)
{
@@ -104,7 +104,7 @@ class ColumnDimension extends Dimension
*
* @param bool $pValue
*
- * @return ColumnDimension
+ * @return $this
*/
public function setAutoSize($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php
index d2b57aad2f5..c8913cc164a 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php
@@ -66,7 +66,7 @@ class ColumnIterator implements \Iterator
*
* @throws Exception
*
- * @return ColumnIterator
+ * @return $this
*/
public function resetStart($startColumn = 'A')
{
@@ -89,7 +89,7 @@ class ColumnIterator implements \Iterator
*
* @param string $endColumn The column address at which to stop iterating
*
- * @return ColumnIterator
+ * @return $this
*/
public function resetEnd($endColumn = null)
{
@@ -106,7 +106,7 @@ class ColumnIterator implements \Iterator
*
* @throws PhpSpreadsheetException
*
- * @return ColumnIterator
+ * @return $this
*/
public function seek($column = 'A')
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php
index 697fc97efc3..ce40cf57f72 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php
@@ -60,7 +60,7 @@ abstract class Dimension
*
* @param bool $pValue
*
- * @return Dimension
+ * @return $this
*/
public function setVisible($pValue)
{
@@ -87,7 +87,7 @@ abstract class Dimension
*
* @throws PhpSpreadsheetException
*
- * @return Dimension
+ * @return $this
*/
public function setOutlineLevel($pValue)
{
@@ -115,7 +115,7 @@ abstract class Dimension
*
* @param bool $pValue
*
- * @return Dimension
+ * @return $this
*/
public function setCollapsed($pValue)
{
@@ -139,7 +139,7 @@ abstract class Dimension
*
* @param int $pValue
*
- * @return Dimension
+ * @return $this
*/
public function setXfIndex($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php
index ed26006c048..da492b4c300 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php
@@ -78,7 +78,7 @@ class Drawing extends BaseDrawing
*
* @throws PhpSpreadsheetException
*
- * @return Drawing
+ * @return $this
*/
public function setPath($pValue, $pVerifyFile = true)
{
@@ -88,7 +88,7 @@ class Drawing extends BaseDrawing
if ($this->width == 0 && $this->height == 0) {
// Get width/height
- list($this->width, $this->height) = getimagesize($pValue);
+ [$this->width, $this->height] = getimagesize($pValue);
}
} else {
throw new PhpSpreadsheetException("File $pValue not found!");
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
index a1e05d60001..c7594dae926 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
@@ -100,7 +100,7 @@ class Shadow implements IComparable
*
* @param bool $pValue
*
- * @return Shadow
+ * @return $this
*/
public function setVisible($pValue)
{
@@ -124,7 +124,7 @@ class Shadow implements IComparable
*
* @param int $pValue
*
- * @return Shadow
+ * @return $this
*/
public function setBlurRadius($pValue)
{
@@ -148,7 +148,7 @@ class Shadow implements IComparable
*
* @param int $pValue
*
- * @return Shadow
+ * @return $this
*/
public function setDistance($pValue)
{
@@ -172,7 +172,7 @@ class Shadow implements IComparable
*
* @param int $pValue
*
- * @return Shadow
+ * @return $this
*/
public function setDirection($pValue)
{
@@ -196,7 +196,7 @@ class Shadow implements IComparable
*
* @param int $pValue
*
- * @return Shadow
+ * @return $this
*/
public function setAlignment($pValue)
{
@@ -220,7 +220,7 @@ class Shadow implements IComparable
*
* @param Color $pValue
*
- * @return Shadow
+ * @return $this
*/
public function setColor(Color $pValue = null)
{
@@ -244,7 +244,7 @@ class Shadow implements IComparable
*
* @param int $pValue
*
- * @return Shadow
+ * @return $this
*/
public function setAlpha($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php
index a78f4fccaeb..be19abbd27f 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php
@@ -172,7 +172,7 @@ class HeaderFooter
*
* @param string $pValue
*
- * @return HeaderFooter
+ * @return $this
*/
public function setOddHeader($pValue)
{
@@ -196,7 +196,7 @@ class HeaderFooter
*
* @param string $pValue
*
- * @return HeaderFooter
+ * @return $this
*/
public function setOddFooter($pValue)
{
@@ -220,7 +220,7 @@ class HeaderFooter
*
* @param string $pValue
*
- * @return HeaderFooter
+ * @return $this
*/
public function setEvenHeader($pValue)
{
@@ -244,7 +244,7 @@ class HeaderFooter
*
* @param string $pValue
*
- * @return HeaderFooter
+ * @return $this
*/
public function setEvenFooter($pValue)
{
@@ -268,7 +268,7 @@ class HeaderFooter
*
* @param string $pValue
*
- * @return HeaderFooter
+ * @return $this
*/
public function setFirstHeader($pValue)
{
@@ -292,7 +292,7 @@ class HeaderFooter
*
* @param string $pValue
*
- * @return HeaderFooter
+ * @return $this
*/
public function setFirstFooter($pValue)
{
@@ -316,7 +316,7 @@ class HeaderFooter
*
* @param bool $pValue
*
- * @return HeaderFooter
+ * @return $this
*/
public function setDifferentOddEven($pValue)
{
@@ -340,7 +340,7 @@ class HeaderFooter
*
* @param bool $pValue
*
- * @return HeaderFooter
+ * @return $this
*/
public function setDifferentFirst($pValue)
{
@@ -364,7 +364,7 @@ class HeaderFooter
*
* @param bool $pValue
*
- * @return HeaderFooter
+ * @return $this
*/
public function setScaleWithDocument($pValue)
{
@@ -388,7 +388,7 @@ class HeaderFooter
*
* @param bool $pValue
*
- * @return HeaderFooter
+ * @return $this
*/
public function setAlignWithMargins($pValue)
{
@@ -403,7 +403,7 @@ class HeaderFooter
* @param HeaderFooterDrawing $image
* @param string $location
*
- * @return HeaderFooter
+ * @return $this
*/
public function addImage(HeaderFooterDrawing $image, $location = self::IMAGE_HEADER_LEFT)
{
@@ -417,7 +417,7 @@ class HeaderFooter
*
* @param string $location
*
- * @return HeaderFooter
+ * @return $this
*/
public function removeImage($location = self::IMAGE_HEADER_LEFT)
{
@@ -433,7 +433,7 @@ class HeaderFooter
*
* @param HeaderFooterDrawing[] $images
*
- * @return HeaderFooter
+ * @return $this
*/
public function setImages(array $images)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
index 6012e93e6ea..f0935585dac 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
@@ -74,7 +74,7 @@ class MemoryDrawing extends BaseDrawing
*
* @param resource $value
*
- * @return MemoryDrawing
+ * @return $this
*/
public function setImageResource($value)
{
@@ -104,7 +104,7 @@ class MemoryDrawing extends BaseDrawing
*
* @param string $value see self::RENDERING_*
*
- * @return MemoryDrawing
+ * @return $this
*/
public function setRenderingFunction($value)
{
@@ -128,7 +128,7 @@ class MemoryDrawing extends BaseDrawing
*
* @param string $value see self::MIMETYPE_*
*
- * @return MemoryDrawing
+ * @return $this
*/
public function setMimeType($value)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php
index eb44a1094c5..9ebfb6488bc 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php
@@ -68,7 +68,7 @@ class PageMargins
*
* @param float $pValue
*
- * @return PageMargins
+ * @return $this
*/
public function setLeft($pValue)
{
@@ -92,7 +92,7 @@ class PageMargins
*
* @param float $pValue
*
- * @return PageMargins
+ * @return $this
*/
public function setRight($pValue)
{
@@ -116,7 +116,7 @@ class PageMargins
*
* @param float $pValue
*
- * @return PageMargins
+ * @return $this
*/
public function setTop($pValue)
{
@@ -140,7 +140,7 @@ class PageMargins
*
* @param float $pValue
*
- * @return PageMargins
+ * @return $this
*/
public function setBottom($pValue)
{
@@ -164,7 +164,7 @@ class PageMargins
*
* @param float $pValue
*
- * @return PageMargins
+ * @return $this
*/
public function setHeader($pValue)
{
@@ -188,7 +188,7 @@ class PageMargins
*
* @param float $pValue
*
- * @return PageMargins
+ * @return $this
*/
public function setFooter($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php
index ab007f61d54..38a09736395 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php
@@ -180,7 +180,7 @@ class PageSetup
* Print scaling. Valid values range from 10 to 400
* This setting is overridden when fitToWidth and/or fitToHeight are in use
*
- * @var int?
+ * @var null|int
*/
private $scale = 100;
@@ -196,7 +196,7 @@ class PageSetup
* Fit To Height
* Number of vertical pages to fit on.
*
- * @var int?
+ * @var null|int
*/
private $fitToHeight = 1;
@@ -204,7 +204,7 @@ class PageSetup
* Fit To Width
* Number of horizontal pages to fit on.
*
- * @var int?
+ * @var null|int
*/
private $fitToWidth = 1;
@@ -272,7 +272,7 @@ class PageSetup
*
* @param int $pValue see self::PAPERSIZE_*
*
- * @return PageSetup
+ * @return $this
*/
public function setPaperSize($pValue)
{
@@ -296,7 +296,7 @@ class PageSetup
*
* @param string $pValue see self::ORIENTATION_*
*
- * @return PageSetup
+ * @return $this
*/
public function setOrientation($pValue)
{
@@ -308,7 +308,7 @@ class PageSetup
/**
* Get Scale.
*
- * @return int?
+ * @return null|int
*/
public function getScale()
{
@@ -325,7 +325,7 @@ class PageSetup
*
* @throws PhpSpreadsheetException
*
- * @return PageSetup
+ * @return $this
*/
public function setScale($pValue, $pUpdate = true)
{
@@ -358,7 +358,7 @@ class PageSetup
*
* @param bool $pValue
*
- * @return PageSetup
+ * @return $this
*/
public function setFitToPage($pValue)
{
@@ -370,7 +370,7 @@ class PageSetup
/**
* Get Fit To Height.
*
- * @return int?
+ * @return null|int
*/
public function getFitToHeight()
{
@@ -383,7 +383,7 @@ class PageSetup
* @param null|int $pValue
* @param bool $pUpdate Update fitToPage so it applies rather than scaling
*
- * @return PageSetup
+ * @return $this
*/
public function setFitToHeight($pValue, $pUpdate = true)
{
@@ -398,7 +398,7 @@ class PageSetup
/**
* Get Fit To Width.
*
- * @return int?
+ * @return null|int
*/
public function getFitToWidth()
{
@@ -411,7 +411,7 @@ class PageSetup
* @param null|int $pValue
* @param bool $pUpdate Update fitToPage so it applies rather than scaling
*
- * @return PageSetup
+ * @return $this
*/
public function setFitToWidth($pValue, $pUpdate = true)
{
@@ -454,7 +454,7 @@ class PageSetup
*
* @param array $pValue Containing start column and end column, empty array if option unset
*
- * @return PageSetup
+ * @return $this
*/
public function setColumnsToRepeatAtLeft(array $pValue)
{
@@ -469,7 +469,7 @@ class PageSetup
* @param string $pStart eg: 'A'
* @param string $pEnd eg: 'B'
*
- * @return PageSetup
+ * @return $this
*/
public function setColumnsToRepeatAtLeftByStartAndEnd($pStart, $pEnd)
{
@@ -509,7 +509,7 @@ class PageSetup
*
* @param array $pValue Containing start column and end column, empty array if option unset
*
- * @return PageSetup
+ * @return $this
*/
public function setRowsToRepeatAtTop(array $pValue)
{
@@ -524,7 +524,7 @@ class PageSetup
* @param int $pStart eg: 1
* @param int $pEnd eg: 1
*
- * @return PageSetup
+ * @return $this
*/
public function setRowsToRepeatAtTopByStartAndEnd($pStart, $pEnd)
{
@@ -548,7 +548,7 @@ class PageSetup
*
* @param bool $value
*
- * @return PageSetup
+ * @return $this
*/
public function setHorizontalCentered($value)
{
@@ -572,7 +572,7 @@ class PageSetup
*
* @param bool $value
*
- * @return PageSetup
+ * @return $this
*/
public function setVerticalCentered($value)
{
@@ -634,7 +634,7 @@ class PageSetup
* Otherwise, the range identified by the value of $index will be removed from the series
* Print areas are numbered from 1
*
- * @return PageSetup
+ * @return $this
*/
public function clearPrintArea($index = 0)
{
@@ -671,7 +671,7 @@ class PageSetup
*
* @throws PhpSpreadsheetException
*
- * @return PageSetup
+ * @return $this
*/
public function setPrintArea($value, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
{
@@ -732,7 +732,7 @@ class PageSetup
*
* @throws PhpSpreadsheetException
*
- * @return PageSetup
+ * @return $this
*/
public function addPrintArea($value, $index = -1)
{
@@ -762,7 +762,7 @@ class PageSetup
*
* @throws PhpSpreadsheetException
*
- * @return PageSetup
+ * @return $this
*/
public function setPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
{
@@ -789,7 +789,7 @@ class PageSetup
*
* @throws PhpSpreadsheetException
*
- * @return PageSetup
+ * @return $this
*/
public function addPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = -1)
{
@@ -815,7 +815,7 @@ class PageSetup
*
* @param int $value
*
- * @return PageSetup
+ * @return $this
*/
public function setFirstPageNumber($value)
{
@@ -827,7 +827,7 @@ class PageSetup
/**
* Reset first page number.
*
- * @return PageSetup
+ * @return $this
*/
public function resetFirstPageNumber()
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php
index 1815f45bf07..2fd3e9195e5 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php
@@ -172,7 +172,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setSheet($pValue)
{
@@ -196,7 +196,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setObjects($pValue)
{
@@ -220,7 +220,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setScenarios($pValue)
{
@@ -244,7 +244,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setFormatCells($pValue)
{
@@ -268,7 +268,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setFormatColumns($pValue)
{
@@ -292,7 +292,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setFormatRows($pValue)
{
@@ -316,7 +316,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setInsertColumns($pValue)
{
@@ -340,7 +340,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setInsertRows($pValue)
{
@@ -364,7 +364,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setInsertHyperlinks($pValue)
{
@@ -388,7 +388,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setDeleteColumns($pValue)
{
@@ -412,7 +412,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setDeleteRows($pValue)
{
@@ -436,7 +436,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setSelectLockedCells($pValue)
{
@@ -460,7 +460,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setSort($pValue)
{
@@ -484,7 +484,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setAutoFilter($pValue)
{
@@ -508,7 +508,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setPivotTables($pValue)
{
@@ -532,7 +532,7 @@ class Protection
*
* @param bool $pValue
*
- * @return Protection
+ * @return $this
*/
public function setSelectUnlockedCells($pValue)
{
@@ -557,7 +557,7 @@ class Protection
* @param string $pValue
* @param bool $pAlreadyHashed If the password has already been hashed, set this to true
*
- * @return Protection
+ * @return $this
*/
public function setPassword($pValue, $pAlreadyHashed = false)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php
index 8510d402a35..9746d6403d1 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php
@@ -59,7 +59,7 @@ class RowCellIterator extends CellIterator
*
* @throws PhpSpreadsheetException
*
- * @return RowCellIterator
+ * @return $this
*/
public function resetStart($startColumn = 'A')
{
@@ -77,7 +77,7 @@ class RowCellIterator extends CellIterator
*
* @throws PhpSpreadsheetException
*
- * @return RowCellIterator
+ * @return $this
*/
public function resetEnd($endColumn = null)
{
@@ -95,7 +95,7 @@ class RowCellIterator extends CellIterator
*
* @throws PhpSpreadsheetException
*
- * @return RowCellIterator
+ * @return $this
*/
public function seek($column = 'A')
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php
index e4346404d23..c4a87bdb558 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php
@@ -56,7 +56,7 @@ class RowDimension extends Dimension
*
* @param int $pValue
*
- * @return RowDimension
+ * @return $this
*/
public function setRowIndex($pValue)
{
@@ -80,7 +80,7 @@ class RowDimension extends Dimension
*
* @param float $pValue
*
- * @return RowDimension
+ * @return $this
*/
public function setRowHeight($pValue)
{
@@ -104,7 +104,7 @@ class RowDimension extends Dimension
*
* @param bool $pValue
*
- * @return RowDimension
+ * @return $this
*/
public function setZeroHeight($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php
index 433cea6a12a..3b9d0e26537 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php
@@ -64,7 +64,7 @@ class RowIterator implements \Iterator
*
* @throws PhpSpreadsheetException
*
- * @return RowIterator
+ * @return $this
*/
public function resetStart($startRow = 1)
{
@@ -86,7 +86,7 @@ class RowIterator implements \Iterator
*
* @param int $endRow The row number at which to stop iterating
*
- * @return RowIterator
+ * @return $this
*/
public function resetEnd($endRow = null)
{
@@ -102,7 +102,7 @@ class RowIterator implements \Iterator
*
* @throws PhpSpreadsheetException
*
- * @return RowIterator
+ * @return $this
*/
public function seek($row = 1)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php
index 172823245e0..fa85bd27d68 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php
@@ -35,6 +35,16 @@ class SheetView
*/
private $zoomScaleNormal = 100;
+ /**
+ * ShowZeros.
+ *
+ * If true, "null" values from a calculation will be shown as "0". This is the default Excel behaviour and can be changed
+ * with the advanced worksheet option "Show a zero in cells that have zero value"
+ *
+ * @var bool
+ */
+ private $showZeros = true;
+
/**
* View.
*
@@ -69,7 +79,7 @@ class SheetView
*
* @throws PhpSpreadsheetException
*
- * @return SheetView
+ * @return $this
*/
public function setZoomScale($pValue)
{
@@ -102,7 +112,7 @@ class SheetView
*
* @throws PhpSpreadsheetException
*
- * @return SheetView
+ * @return $this
*/
public function setZoomScaleNormal($pValue)
{
@@ -115,6 +125,24 @@ class SheetView
return $this;
}
+ /**
+ * Set ShowZeroes setting.
+ *
+ * @param bool $pValue
+ */
+ public function setShowZeros($pValue)
+ {
+ $this->showZeros = $pValue;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getShowZeros()
+ {
+ return $this->showZeros;
+ }
+
/**
* Get View.
*
@@ -137,7 +165,7 @@ class SheetView
*
* @throws PhpSpreadsheetException
*
- * @return SheetView
+ * @return $this
*/
public function setView($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php
index 1e525a3fecb..9a3f96471d8 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php
@@ -647,7 +647,7 @@ class Worksheet implements IComparable
/**
* Refresh column dimensions.
*
- * @return Worksheet
+ * @return $this
*/
public function refreshColumnDimensions()
{
@@ -666,7 +666,7 @@ class Worksheet implements IComparable
/**
* Refresh row dimensions.
*
- * @return Worksheet
+ * @return $this
*/
public function refreshRowDimensions()
{
@@ -690,7 +690,7 @@ class Worksheet implements IComparable
public function calculateWorksheetDimension()
{
// Return
- return 'A1' . ':' . $this->getHighestColumn() . $this->getHighestRow();
+ return 'A1:' . $this->getHighestColumn() . $this->getHighestRow();
}
/**
@@ -701,13 +701,13 @@ class Worksheet implements IComparable
public function calculateWorksheetDataDimension()
{
// Return
- return 'A1' . ':' . $this->getHighestDataColumn() . $this->getHighestDataRow();
+ return 'A1:' . $this->getHighestDataColumn() . $this->getHighestDataRow();
}
/**
* Calculate widths for auto-size columns.
*
- * @return Worksheet;
+ * @return $this
*/
public function calculateColumnWidths()
{
@@ -797,7 +797,7 @@ class Worksheet implements IComparable
*
* @param Spreadsheet $parent
*
- * @return Worksheet
+ * @return $this
*/
public function rebindParent(Spreadsheet $parent)
{
@@ -838,7 +838,7 @@ class Worksheet implements IComparable
* @param bool $validate False to skip validation of new title. WARNING: This should only be set
* at parse time (by Readers), where titles can be assumed to be valid.
*
- * @return Worksheet
+ * @return $this
*/
public function setTitle($pValue, $updateFormulaCellReferences = true, $validate = true)
{
@@ -913,7 +913,7 @@ class Worksheet implements IComparable
*
* @param string $value Sheet state (visible, hidden, veryHidden)
*
- * @return Worksheet
+ * @return $this
*/
public function setSheetState($value)
{
@@ -937,7 +937,7 @@ class Worksheet implements IComparable
*
* @param PageSetup $pValue
*
- * @return Worksheet
+ * @return $this
*/
public function setPageSetup(PageSetup $pValue)
{
@@ -961,7 +961,7 @@ class Worksheet implements IComparable
*
* @param PageMargins $pValue
*
- * @return Worksheet
+ * @return $this
*/
public function setPageMargins(PageMargins $pValue)
{
@@ -985,7 +985,7 @@ class Worksheet implements IComparable
*
* @param HeaderFooter $pValue
*
- * @return Worksheet
+ * @return $this
*/
public function setHeaderFooter(HeaderFooter $pValue)
{
@@ -1009,7 +1009,7 @@ class Worksheet implements IComparable
*
* @param SheetView $pValue
*
- * @return Worksheet
+ * @return $this
*/
public function setSheetView(SheetView $pValue)
{
@@ -1033,7 +1033,7 @@ class Worksheet implements IComparable
*
* @param Protection $pValue
*
- * @return Worksheet
+ * @return $this
*/
public function setProtection(Protection $pValue)
{
@@ -1119,7 +1119,7 @@ class Worksheet implements IComparable
* @param string $pCoordinate Coordinate of the cell, eg: 'A1'
* @param mixed $pValue Value of the cell
*
- * @return Worksheet
+ * @return $this
*/
public function setCellValue($pCoordinate, $pValue)
{
@@ -1135,7 +1135,7 @@ class Worksheet implements IComparable
* @param int $row Numeric row coordinate of the cell
* @param mixed $value Value of the cell
*
- * @return Worksheet
+ * @return $this
*/
public function setCellValueByColumnAndRow($columnIndex, $row, $value)
{
@@ -1151,7 +1151,7 @@ class Worksheet implements IComparable
* @param mixed $pValue Value of the cell
* @param string $pDataType Explicit data type, see DataType::TYPE_*
*
- * @return Worksheet
+ * @return $this
*/
public function setCellValueExplicit($pCoordinate, $pValue, $pDataType)
{
@@ -1169,7 +1169,7 @@ class Worksheet implements IComparable
* @param mixed $value Value of the cell
* @param string $dataType Explicit data type, see DataType::TYPE_*
*
- * @return Worksheet
+ * @return $this
*/
public function setCellValueExplicitByColumnAndRow($columnIndex, $row, $value, $dataType)
{
@@ -1441,7 +1441,7 @@ class Worksheet implements IComparable
$this->parent->setActiveSheetIndex($this->parent->getIndex($this));
// set cell coordinate as active
- $this->setSelectedCells(strtoupper($pCellCoordinate));
+ $this->setSelectedCells($pCellCoordinate);
return $this->parent->getCellXfSupervisor();
}
@@ -1480,7 +1480,7 @@ class Worksheet implements IComparable
*
* @param string $pCoordinate eg: 'A1'
*
- * @return Worksheet
+ * @return $this
*/
public function removeConditionalStyles($pCoordinate)
{
@@ -1505,7 +1505,7 @@ class Worksheet implements IComparable
* @param string $pCoordinate eg: 'A1'
* @param $pValue Conditional[]
*
- * @return Worksheet
+ * @return $this
*/
public function setConditionalStyles($pCoordinate, $pValue)
{
@@ -1545,7 +1545,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function duplicateStyle(Style $pCellStyle, $pRange)
{
@@ -1561,7 +1561,7 @@ class Worksheet implements IComparable
}
// Calculate range outer borders
- list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($pRange . ':' . $pRange);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange . ':' . $pRange);
// Make sure we can loop upwards on rows and columns
if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
@@ -1590,7 +1590,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function duplicateConditionalStyle(array $pCellStyle, $pRange = '')
{
@@ -1601,7 +1601,7 @@ class Worksheet implements IComparable
}
// Calculate range outer borders
- list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($pRange . ':' . $pRange);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange . ':' . $pRange);
// Make sure we can loop upwards on rows and columns
if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
@@ -1628,7 +1628,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function setBreak($pCoordinate, $pBreak)
{
@@ -1657,7 +1657,7 @@ class Worksheet implements IComparable
* @param int $row Numeric row coordinate of the cell
* @param int $break Break type (type of Worksheet::BREAK_*)
*
- * @return Worksheet
+ * @return $this
*/
public function setBreakByColumnAndRow($columnIndex, $row, $break)
{
@@ -1681,7 +1681,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function mergeCells($pRange)
{
@@ -1726,7 +1726,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function mergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
{
@@ -1742,7 +1742,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function unmergeCells($pRange)
{
@@ -1772,7 +1772,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function unmergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
{
@@ -1797,7 +1797,7 @@ class Worksheet implements IComparable
*
* @param array $pValue
*
- * @return Worksheet
+ * @return $this
*/
public function setMergeCells(array $pValue)
{
@@ -1813,7 +1813,7 @@ class Worksheet implements IComparable
* @param string $pPassword Password to unlock the protection
* @param bool $pAlreadyHashed If the password has already been hashed, set this to true
*
- * @return Worksheet
+ * @return $this
*/
public function protectCells($pRange, $pPassword, $pAlreadyHashed = false)
{
@@ -1838,7 +1838,7 @@ class Worksheet implements IComparable
* @param string $password Password to unlock the protection
* @param bool $alreadyHashed If the password has already been hashed, set this to true
*
- * @return Worksheet
+ * @return $this
*/
public function protectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2, $password, $alreadyHashed = false)
{
@@ -1854,7 +1854,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function unprotectCells($pRange)
{
@@ -1880,7 +1880,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function unprotectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
{
@@ -1917,7 +1917,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function setAutoFilter($pValue)
{
@@ -1940,7 +1940,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function setAutoFilterByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
{
@@ -1954,7 +1954,7 @@ class Worksheet implements IComparable
/**
* Remove autofilter.
*
- * @return Worksheet
+ * @return $this
*/
public function removeAutoFilter()
{
@@ -1987,7 +1987,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function freezePane($cell, $topLeftCell = null)
{
@@ -2012,7 +2012,7 @@ class Worksheet implements IComparable
* @param int $columnIndex Numeric column coordinate of the cell
* @param int $row Numeric row coordinate of the cell
*
- * @return Worksheet
+ * @return $this
*/
public function freezePaneByColumnAndRow($columnIndex, $row)
{
@@ -2022,7 +2022,7 @@ class Worksheet implements IComparable
/**
* Unfreeze Pane.
*
- * @return Worksheet
+ * @return $this
*/
public function unfreezePane()
{
@@ -2047,7 +2047,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function insertNewRowBefore($pBefore, $pNumRows = 1)
{
@@ -2069,7 +2069,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function insertNewColumnBefore($pBefore, $pNumCols = 1)
{
@@ -2091,7 +2091,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function insertNewColumnBeforeByIndex($beforeColumnIndex, $pNumCols = 1)
{
@@ -2110,22 +2110,31 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function removeRow($pRow, $pNumRows = 1)
{
- if ($pRow >= 1) {
- $highestRow = $this->getHighestDataRow();
- $objReferenceHelper = ReferenceHelper::getInstance();
- $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
- for ($r = 0; $r < $pNumRows; ++$r) {
- $this->getCellCollection()->removeRow($highestRow);
- --$highestRow;
- }
- } else {
+ if ($pRow < 1) {
throw new Exception('Rows to be deleted should at least start from row 1.');
}
+ $highestRow = $this->getHighestDataRow();
+ $removedRowsCounter = 0;
+
+ for ($r = 0; $r < $pNumRows; ++$r) {
+ if ($pRow + $r <= $highestRow) {
+ $this->getCellCollection()->removeRow($pRow + $r);
+ ++$removedRowsCounter;
+ }
+ }
+
+ $objReferenceHelper = ReferenceHelper::getInstance();
+ $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
+ for ($r = 0; $r < $removedRowsCounter; ++$r) {
+ $this->getCellCollection()->removeRow($highestRow);
+ --$highestRow;
+ }
+
return $this;
}
@@ -2137,23 +2146,35 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function removeColumn($pColumn, $pNumCols = 1)
{
- if (!is_numeric($pColumn)) {
- $highestColumn = $this->getHighestDataColumn();
- $pColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($pColumn) + $pNumCols);
- $objReferenceHelper = ReferenceHelper::getInstance();
- $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
- for ($c = 0; $c < $pNumCols; ++$c) {
- $this->getCellCollection()->removeColumn($highestColumn);
- $highestColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($highestColumn) - 1);
- }
- } else {
+ if (is_numeric($pColumn)) {
throw new Exception('Column references should not be numeric.');
}
+ $highestColumn = $this->getHighestDataColumn();
+ $highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
+ $pColumnIndex = Coordinate::columnIndexFromString($pColumn);
+
+ if ($pColumnIndex > $highestColumnIndex) {
+ return $this;
+ }
+
+ $pColumn = Coordinate::stringFromColumnIndex($pColumnIndex + $pNumCols);
+ $objReferenceHelper = ReferenceHelper::getInstance();
+ $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
+
+ $maxPossibleColumnsToBeRemoved = $highestColumnIndex - $pColumnIndex + 1;
+
+ for ($c = 0, $n = min($maxPossibleColumnsToBeRemoved, $pNumCols); $c < $n; ++$c) {
+ $this->getCellCollection()->removeColumn($highestColumn);
+ $highestColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($highestColumn) - 1);
+ }
+
+ $this->garbageCollect();
+
return $this;
}
@@ -2165,7 +2186,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function removeColumnByIndex($columnIndex, $numColumns = 1)
{
@@ -2191,7 +2212,7 @@ class Worksheet implements IComparable
*
* @param bool $pValue Show gridlines (true/false)
*
- * @return Worksheet
+ * @return $this
*/
public function setShowGridlines($pValue)
{
@@ -2215,7 +2236,7 @@ class Worksheet implements IComparable
*
* @param bool $pValue Print gridlines (true/false)
*
- * @return Worksheet
+ * @return $this
*/
public function setPrintGridlines($pValue)
{
@@ -2239,7 +2260,7 @@ class Worksheet implements IComparable
*
* @param bool $pValue Show row and column headers (true/false)
*
- * @return Worksheet
+ * @return $this
*/
public function setShowRowColHeaders($pValue)
{
@@ -2263,7 +2284,7 @@ class Worksheet implements IComparable
*
* @param bool $pValue Show summary below (true/false)
*
- * @return Worksheet
+ * @return $this
*/
public function setShowSummaryBelow($pValue)
{
@@ -2287,7 +2308,7 @@ class Worksheet implements IComparable
*
* @param bool $pValue Show summary right (true/false)
*
- * @return Worksheet
+ * @return $this
*/
public function setShowSummaryRight($pValue)
{
@@ -2311,7 +2332,7 @@ class Worksheet implements IComparable
*
* @param Comment[] $pValue
*
- * @return Worksheet
+ * @return $this
*/
public function setComments(array $pValue)
{
@@ -2392,7 +2413,7 @@ class Worksheet implements IComparable
*
* @param string $pCoordinate Cell (i.e. A1)
*
- * @return Worksheet
+ * @return $this
*/
public function setSelectedCell($pCoordinate)
{
@@ -2404,7 +2425,7 @@ class Worksheet implements IComparable
*
* @param string $pCoordinate Cell range, examples: 'A1', 'B2:G5', 'A:C', '3:6'
*
- * @return Worksheet
+ * @return $this
*/
public function setSelectedCells($pCoordinate)
{
@@ -2424,7 +2445,7 @@ class Worksheet implements IComparable
$pCoordinate = preg_replace('/^(\d+):(\d+)$/', 'A${1}:XFD${2}', $pCoordinate);
if (Coordinate::coordinateIsRange($pCoordinate)) {
- list($first) = Coordinate::splitRange($pCoordinate);
+ [$first] = Coordinate::splitRange($pCoordinate);
$this->activeCell = $first[0];
} else {
$this->activeCell = $pCoordinate;
@@ -2442,7 +2463,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function setSelectedCellByColumnAndRow($columnIndex, $row)
{
@@ -2464,7 +2485,7 @@ class Worksheet implements IComparable
*
* @param bool $value Right-to-left true/false
*
- * @return Worksheet
+ * @return $this
*/
public function setRightToLeft($value)
{
@@ -2483,7 +2504,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function fromArray(array $source, $nullValue = null, $startCell = 'A1', $strictNullComparison = false)
{
@@ -2493,7 +2514,7 @@ class Worksheet implements IComparable
}
// start coordinate
- list($startColumn, $startRow) = Coordinate::coordinateFromString($startCell);
+ [$startColumn, $startRow] = Coordinate::coordinateFromString($startCell);
// Loop through $source
foreach ($source as $rowData) {
@@ -2535,7 +2556,7 @@ class Worksheet implements IComparable
// Returnvalue
$returnValue = [];
// Identify the range that we need to extract from the worksheet
- list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($pRange);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($pRange);
$minCol = Coordinate::stringFromColumnIndex($rangeStart[0]);
$minRow = $rangeStart[1];
$maxCol = Coordinate::stringFromColumnIndex($rangeEnd[0]);
@@ -2545,11 +2566,11 @@ class Worksheet implements IComparable
// Loop through rows
$r = -1;
for ($row = $minRow; $row <= $maxRow; ++$row) {
- $rRef = ($returnCellRef) ? $row : ++$r;
+ $rRef = $returnCellRef ? $row : ++$r;
$c = -1;
// Loop through columns in the current row
for ($col = $minCol; $col != $maxCol; ++$col) {
- $cRef = ($returnCellRef) ? $col : ++$c;
+ $cRef = $returnCellRef ? $col : ++$c;
// Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
// so we test and retrieve directly against cellCollection
if ($this->cellCollection->has($col . $row)) {
@@ -2668,7 +2689,7 @@ class Worksheet implements IComparable
/**
* Run PhpSpreadsheet garbage collector.
*
- * @return Worksheet
+ * @return $this
*/
public function garbageCollect()
{
@@ -2768,7 +2789,7 @@ class Worksheet implements IComparable
* @param string $pCellCoordinate Cell coordinate to insert hyperlink, eg: 'A1'
* @param null|Hyperlink $pHyperlink
*
- * @return Worksheet
+ * @return $this
*/
public function setHyperlink($pCellCoordinate, Hyperlink $pHyperlink = null)
{
@@ -2829,7 +2850,7 @@ class Worksheet implements IComparable
* @param string $pCellCoordinate Cell coordinate to insert data validation, eg: 'A1'
* @param null|DataValidation $pDataValidation
*
- * @return Worksheet
+ * @return $this
*/
public function setDataValidation($pCellCoordinate, DataValidation $pDataValidation = null)
{
@@ -2896,9 +2917,8 @@ class Worksheet implements IComparable
$rangeSet = $rangeBoundaries[0][0] . $rangeBoundaries[0][1] . ':' . $rangeBoundaries[1][0] . $rangeBoundaries[1][1];
}
unset($rangeSet);
- $stRange = implode(' ', $rangeBlocks);
- return $stRange;
+ return implode(' ', $rangeBlocks);
}
/**
@@ -2918,7 +2938,7 @@ class Worksheet implements IComparable
/**
* Reset tab color.
*
- * @return Worksheet
+ * @return $this
*/
public function resetTabColor()
{
@@ -2941,13 +2961,11 @@ class Worksheet implements IComparable
/**
* Copy worksheet (!= clone!).
*
- * @return Worksheet
+ * @return static
*/
public function copy()
{
- $copied = clone $this;
-
- return $copied;
+ return clone $this;
}
/**
@@ -2994,7 +3012,7 @@ class Worksheet implements IComparable
*
* @throws Exception
*
- * @return Worksheet
+ * @return $this
*/
public function setCodeName($pValue, $validate = true)
{
@@ -3034,7 +3052,7 @@ class Worksheet implements IComparable
}
}
- $pValue = $pValue . '_' . $i; // ok, we have a valid name
+ $pValue .= '_' . $i; // ok, we have a valid name
}
}
}
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php
index ae38ab73218..1166bd25b18 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php
@@ -147,7 +147,7 @@ class Csv extends BaseWriter
*
* @param string $pValue Delimiter, defaults to ','
*
- * @return CSV
+ * @return $this
*/
public function setDelimiter($pValue)
{
@@ -171,7 +171,7 @@ class Csv extends BaseWriter
*
* @param string $pValue Enclosure, defaults to "
*
- * @return CSV
+ * @return $this
*/
public function setEnclosure($pValue)
{
@@ -198,7 +198,7 @@ class Csv extends BaseWriter
*
* @param string $pValue Line ending, defaults to OS line ending (PHP_EOL)
*
- * @return CSV
+ * @return $this
*/
public function setLineEnding($pValue)
{
@@ -222,7 +222,7 @@ class Csv extends BaseWriter
*
* @param bool $pValue Use UTF-8 byte-order mark? Defaults to false
*
- * @return CSV
+ * @return $this
*/
public function setUseBOM($pValue)
{
@@ -246,7 +246,7 @@ class Csv extends BaseWriter
*
* @param bool $pValue Use separator line? Defaults to false
*
- * @return CSV
+ * @return $this
*/
public function setIncludeSeparatorLine($pValue)
{
@@ -271,7 +271,7 @@ class Csv extends BaseWriter
* @param bool $pValue Set the file to be written as a fully Excel compatible csv file
* Note that this overrides other settings such as useBOM, enclosure and delimiter
*
- * @return CSV
+ * @return $this
*/
public function setExcelCompatibility($pValue)
{
@@ -295,7 +295,7 @@ class Csv extends BaseWriter
*
* @param int $pValue Sheet index
*
- * @return CSV
+ * @return $this
*/
public function setSheetIndex($pValue)
{
diff --git a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php
index 8cd026d3190..37d68e912e2 100644
--- a/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php
+++ b/htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php
@@ -62,6 +62,13 @@ class Html extends BaseWriter
*/
private $useInlineCss = false;
+ /**
+ * Use embedded CSS?
+ *
+ * @var bool
+ */
+ private $useEmbeddedCSS = true;
+
/**
* Array of CSS styles.
*
@@ -290,7 +297,7 @@ class Html extends BaseWriter
*
* @param int $pValue Sheet index
*
- * @return HTML
+ * @return $this
*/
public function setSheetIndex($pValue)
{
@@ -314,7 +321,7 @@ class Html extends BaseWriter
*
* @param bool $pValue Flag indicating whether the sheet navigation block should be generated or not
*
- * @return HTML
+ * @return $this
*/
public function setGenerateSheetNavigationBlock($pValue)
{
@@ -325,6 +332,8 @@ class Html extends BaseWriter
/**
* Write all sheets (resets sheetIndex to NULL).
+ *
+ * @return $this
*/
public function writeAllSheets()
{
@@ -643,7 +652,10 @@ class Html extends BaseWriter
} else {
$imageDetails = getimagesize($filename);
if ($fp = fopen($filename, 'rb', 0)) {
- $picture = fread($fp, filesize($filename));
+ $picture = '';
+ while (!feof($fp)) {
+ $picture .= fread($fp, 1024);
+ }
fclose($fp);
// base64 encode the binary data, then break it
// into chunks according to RFC 2045 semantics
@@ -888,8 +900,8 @@ class Html extends BaseWriter
$css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = $width . 'pt';
if ($columnDimension->getVisible() === false) {
- $css['table.sheet' . $sheetIndex . ' col.col' . $column]['visibility'] = 'collapse';
- $css['table.sheet' . $sheetIndex . ' col.col' . $column]['*display'] = 'none'; // target IE6+7
+ $css['table.sheet' . $sheetIndex . ' .column' . $column]['visibility'] = 'collapse';
+ $css['table.sheet' . $sheetIndex . ' .column' . $column]['display'] = 'none'; // target IE6+7
}
}
}
@@ -950,15 +962,12 @@ class Html extends BaseWriter
private function createCSSStyle(Style $pStyle)
{
// Create CSS
- $css = array_merge(
+ return array_merge(
$this->createCSSStyleAlignment($pStyle->getAlignment()),
$this->createCSSStyleBorders($pStyle->getBorders()),
$this->createCSSStyleFont($pStyle->getFont()),
$this->createCSSStyleFill($pStyle->getFill())
);
-
- // Return
- return $css;
}
/**
@@ -1051,9 +1060,8 @@ class Html extends BaseWriter
{
// Create CSS - add !important to non-none border styles for merged cells
$borderStyle = $this->mapBorderStyle($pStyle->getBorderStyle());
- $css = $borderStyle . ' #' . $pStyle->getColor()->getRGB() . (($borderStyle == 'none') ? '' : ' !important');
- return $css;
+ return $borderStyle . ' #' . $pStyle->getColor()->getRGB() . (($borderStyle == 'none') ? '' : ' !important');
}
/**
@@ -1102,7 +1110,9 @@ class Html extends BaseWriter
// Construct HTML
$html = '';
- $html .= $this->setMargins($pSheet);
+ if ($this->useEmbeddedCSS) {
+ $html .= $this->setMargins($pSheet);
+ }
if (!$this->useInlineCss) {
$gridlines = $pSheet->getShowGridlines() ? ' gridlines' : '';
@@ -1141,9 +1151,7 @@ class Html extends BaseWriter
*/
private function generateTableFooter()
{
- $html = '
' . PHP_EOL;
-
- return $html;
+ return '